(function(d){	const l = d['en'] = d['en'] || {};	l.dictionary=Object.assign(		l.dictionary||{},		{"%0 of %1":"%0 of %1","Align cell text to the bottom":"Align cell text to the bottom","Align cell text to the center":"Align cell text to the center","Align cell text to the left":"Align cell text to the left","Align cell text to the middle":"Align cell text to the middle","Align cell text to the right":"Align cell text to the right","Align cell text to the top":"Align cell text to the top","Align center":"Align center","Align left":"Align left","Align right":"Align right","Align table to the left":"Align table to the left","Align table to the right":"Align table to the right",Alignment:"Alignment","Almost equal to":"Almost equal to",Angle:"Angle","Approximately equal to":"Approximately equal to",Aquamarine:"Aquamarine","Asterisk operator":"Asterisk operator","Austral sign":"Austral sign","back with leftwards arrow above":"back with leftwards arrow above",Background:"Background",Big:"Big","Bitcoin sign":"Bitcoin sign",Black:"Black","Block quote":"Block quote",Blue:"Blue","Blue marker":"Blue marker",Bold:"Bold",Border:"Border","Break text":"Break text","Bulleted List":"Bulleted List","Bulleted list styles toolbar":"Bulleted list styles toolbar",Cancel:"Cancel","Caption for image: %0":"Caption for image: %0","Caption for the image":"Caption for the image","Cedi sign":"Cedi sign","Cell properties":"Cell properties","Cent sign":"Cent sign","Center table":"Center table","Centered image":"Centered image","Change image text alternative":"Change image text alternative","Character categories":"Character categories","Characters: %0":"Characters: %0","Choose heading":"Choose heading","Choose language":"Choose language",Circle:"Circle",Code:"Code","Colon sign":"Colon sign",Color:"Color","Color picker":"Color picker",Column:"Column","Contains as member":"Contains as member","Copyright sign":"Copyright sign","Cruzeiro sign":"Cruzeiro sign","Currency sign":"Currency sign",Dashed:"Dashed",Decimal:"Decimal","Decimal with leading zero":"Decimal with leading zero","Decrease indent":"Decrease indent",Default:"Default","Degree sign":"Degree sign","Delete column":"Delete column","Delete row":"Delete row","Dim grey":"Dim grey",Dimensions:"Dimensions","Disable editing":"Disable editing",Disc:"Disc","Division sign":"Division sign","Document colors":"Document colors","Dollar sign":"Dollar sign","Dong sign":"Dong sign",Dotted:"Dotted",Double:"Double","Double dagger":"Double dagger","Double exclamation mark":"Double exclamation mark","Double low-9 quotation mark":"Double low-9 quotation mark","Double question mark":"Double question mark",Downloadable:"Downloadable","downwards arrow to bar":"downwards arrow to bar","downwards dashed arrow":"downwards dashed arrow","downwards double arrow":"downwards double arrow","downwards simple arrow":"downwards simple arrow","Drachma sign":"Drachma sign","Dropdown toolbar":"Dropdown toolbar","Edit block":"Edit block","Edit link":"Edit link","Edit source":"Edit source","Editor block content toolbar":"Editor block content toolbar","Editor contextual toolbar":"Editor contextual toolbar","Editor editing area: %0":"Editor editing area: %0","Editor toolbar":"Editor toolbar","Element of":"Element of","Em dash":"Em dash","Empty set":"Empty set","Empty snippet content":"Empty snippet content","En dash":"En dash","Enable editing":"Enable editing","end with leftwards arrow above":"end with leftwards arrow above","Enter image caption":"Enter image caption","Enter table caption":"Enter table caption","Euro sign":"Euro sign","Euro-currency sign":"Euro-currency sign","Exclamation question mark":"Exclamation question mark",Find:"Find","Find and replace":"Find and replace","Find in text…":"Find in text…","Font Background Color":"Font Background Color","Font Color":"Font Color","Font Family":"Font Family","Font Size":"Font Size","For all":"For all","Fraction slash":"Fraction slash","French franc sign":"French franc sign","Full size image":"Full size image","German penny sign":"German penny sign","Greater-than or equal to":"Greater-than or equal to","Greater-than sign":"Greater-than sign",Green:"Green","Green marker":"Green marker","Green pen":"Green pen",Grey:"Grey",Groove:"Groove","Guarani sign":"Guarani sign","Header column":"Header column","Header row":"Header row",Heading:"Heading","Heading 1":"Heading 1","Heading 2":"Heading 2","Heading 3":"Heading 3","Heading 4":"Heading 4","Heading 5":"Heading 5","Heading 6":"Heading 6",Height:"Height",Highlight:"Highlight","Horizontal ellipsis":"Horizontal ellipsis","Horizontal line":"Horizontal line","Horizontal text alignment toolbar":"Horizontal text alignment toolbar","Hryvnia sign":"Hryvnia sign","HTML object":"HTML object","HTML snippet":"HTML snippet",Huge:"Huge","Identical to":"Identical to","Image resize list":"Image resize list","Image toolbar":"Image toolbar","image widget":"image widget","In line":"In line","Increase indent":"Increase indent","Indian rupee sign":"Indian rupee sign",Infinity:"Infinity",Insert:"Insert","Insert column left":"Insert column left","Insert column right":"Insert column right","Insert HTML":"Insert HTML","Insert image":"Insert image","Insert image via URL":"Insert image via URL","Insert media":"Insert media","Insert paragraph after block":"Insert paragraph after block","Insert paragraph before block":"Insert paragraph before block","Insert row above":"Insert row above","Insert row below":"Insert row below","Insert table":"Insert table",Inset:"Inset",Integral:"Integral",Intersection:"Intersection","Inverted exclamation mark":"Inverted exclamation mark","Inverted question mark":"Inverted question mark",Italic:"Italic",Justify:"Justify","Justify cell text":"Justify cell text","Kip sign":"Kip sign",Language:"Language","Latin capital letter a with breve":"Latin capital letter a with breve","Latin capital letter a with macron":"Latin capital letter a with macron","Latin capital letter a with ogonek":"Latin capital letter a with ogonek","Latin capital letter c with acute":"Latin capital letter c with acute","Latin capital letter c with caron":"Latin capital letter c with caron","Latin capital letter c with circumflex":"Latin capital letter c with circumflex","Latin capital letter c with dot above":"Latin capital letter c with dot above","Latin capital letter d with caron":"Latin capital letter d with caron","Latin capital letter d with stroke":"Latin capital letter d with stroke","Latin capital letter e with breve":"Latin capital letter e with breve","Latin capital letter e with caron":"Latin capital letter e with caron","Latin capital letter e with dot above":"Latin capital letter e with dot above","Latin capital letter e with macron":"Latin capital letter e with macron","Latin capital letter e with ogonek":"Latin capital letter e with ogonek","Latin capital letter eng":"Latin capital letter eng","Latin capital letter g with breve":"Latin capital letter g with breve","Latin capital letter g with cedilla":"Latin capital letter g with cedilla","Latin capital letter g with circumflex":"Latin capital letter g with circumflex","Latin capital letter g with dot above":"Latin capital letter g with dot above","Latin capital letter h with circumflex":"Latin capital letter h with circumflex","Latin capital letter h with stroke":"Latin capital letter h with stroke","Latin capital letter i with breve":"Latin capital letter i with breve","Latin capital letter i with dot above":"Latin capital letter i with dot above","Latin capital letter i with macron":"Latin capital letter i with macron","Latin capital letter i with ogonek":"Latin capital letter i with ogonek","Latin capital letter i with tilde":"Latin capital letter i with tilde","Latin capital letter j with circumflex":"Latin capital letter j with circumflex","Latin capital letter k with cedilla":"Latin capital letter k with cedilla","Latin capital letter l with acute":"Latin capital letter l with acute","Latin capital letter l with caron":"Latin capital letter l with caron","Latin capital letter l with cedilla":"Latin capital letter l with cedilla","Latin capital letter l with middle dot":"Latin capital letter l with middle dot","Latin capital letter l with stroke":"Latin capital letter l with stroke","Latin capital letter n with acute":"Latin capital letter n with acute","Latin capital letter n with caron":"Latin capital letter n with caron","Latin capital letter n with cedilla":"Latin capital letter n with cedilla","Latin capital letter o with breve":"Latin capital letter o with breve","Latin capital letter o with double acute":"Latin capital letter o with double acute","Latin capital letter o with macron":"Latin capital letter o with macron","Latin capital letter r with acute":"Latin capital letter r with acute","Latin capital letter r with caron":"Latin capital letter r with caron","Latin capital letter r with cedilla":"Latin capital letter r with cedilla","Latin capital letter s with acute":"Latin capital letter s with acute","Latin capital letter s with caron":"Latin capital letter s with caron","Latin capital letter s with cedilla":"Latin capital letter s with cedilla","Latin capital letter s with circumflex":"Latin capital letter s with circumflex","Latin capital letter t with caron":"Latin capital letter t with caron","Latin capital letter t with cedilla":"Latin capital letter t with cedilla","Latin capital letter t with stroke":"Latin capital letter t with stroke","Latin capital letter u with breve":"Latin capital letter u with breve","Latin capital letter u with double acute":"Latin capital letter u with double acute","Latin capital letter u with macron":"Latin capital letter u with macron","Latin capital letter u with ogonek":"Latin capital letter u with ogonek","Latin capital letter u with ring above":"Latin capital letter u with ring above","Latin capital letter u with tilde":"Latin capital letter u with tilde","Latin capital letter w with circumflex":"Latin capital letter w with circumflex","Latin capital letter y with circumflex":"Latin capital letter y with circumflex","Latin capital letter y with diaeresis":"Latin capital letter y with diaeresis","Latin capital letter z with acute":"Latin capital letter z with acute","Latin capital letter z with caron":"Latin capital letter z with caron","Latin capital letter z with dot above":"Latin capital letter z with dot above","Latin capital ligature ij":"Latin capital ligature ij","Latin capital ligature oe":"Latin capital ligature oe","Latin small letter a with breve":"Latin small letter a with breve","Latin small letter a with macron":"Latin small letter a with macron","Latin small letter a with ogonek":"Latin small letter a with ogonek","Latin small letter c with acute":"Latin small letter c with acute","Latin small letter c with caron":"Latin small letter c with caron","Latin small letter c with circumflex":"Latin small letter c with circumflex","Latin small letter c with dot above":"Latin small letter c with dot above","Latin small letter d with caron":"Latin small letter d with caron","Latin small letter d with stroke":"Latin small letter d with stroke","Latin small letter dotless i":"Latin small letter dotless i","Latin small letter e with breve":"Latin small letter e with breve","Latin small letter e with caron":"Latin small letter e with caron","Latin small letter e with dot above":"Latin small letter e with dot above","Latin small letter e with macron":"Latin small letter e with macron","Latin small letter e with ogonek":"Latin small letter e with ogonek","Latin small letter eng":"Latin small letter eng","Latin small letter f with hook":"Latin small letter f with hook","Latin small letter g with breve":"Latin small letter g with breve","Latin small letter g with cedilla":"Latin small letter g with cedilla","Latin small letter g with circumflex":"Latin small letter g with circumflex","Latin small letter g with dot above":"Latin small letter g with dot above","Latin small letter h with circumflex":"Latin small letter h with circumflex","Latin small letter h with stroke":"Latin small letter h with stroke","Latin small letter i with breve":"Latin small letter i with breve","Latin small letter i with macron":"Latin small letter i with macron","Latin small letter i with ogonek":"Latin small letter i with ogonek","Latin small letter i with tilde":"Latin small letter i with tilde","Latin small letter j with circumflex":"Latin small letter j with circumflex","Latin small letter k with cedilla":"Latin small letter k with cedilla","Latin small letter kra":"Latin small letter kra","Latin small letter l with acute":"Latin small letter l with acute","Latin small letter l with caron":"Latin small letter l with caron","Latin small letter l with cedilla":"Latin small letter l with cedilla","Latin small letter l with middle dot":"Latin small letter l with middle dot","Latin small letter l with stroke":"Latin small letter l with stroke","Latin small letter long s":"Latin small letter long s","Latin small letter n preceded by apostrophe":"Latin small letter n preceded by apostrophe","Latin small letter n with acute":"Latin small letter n with acute","Latin small letter n with caron":"Latin small letter n with caron","Latin small letter n with cedilla":"Latin small letter n with cedilla","Latin small letter o with breve":"Latin small letter o with breve","Latin small letter o with double acute":"Latin small letter o with double acute","Latin small letter o with macron":"Latin small letter o with macron","Latin small letter r with acute":"Latin small letter r with acute","Latin small letter r with caron":"Latin small letter r with caron","Latin small letter r with cedilla":"Latin small letter r with cedilla","Latin small letter s with acute":"Latin small letter s with acute","Latin small letter s with caron":"Latin small letter s with caron","Latin small letter s with cedilla":"Latin small letter s with cedilla","Latin small letter s with circumflex":"Latin small letter s with circumflex","Latin small letter t with caron":"Latin small letter t with caron","Latin small letter t with cedilla":"Latin small letter t with cedilla","Latin small letter t with stroke":"Latin small letter t with stroke","Latin small letter u with breve":"Latin small letter u with breve","Latin small letter u with double acute":"Latin small letter u with double acute","Latin small letter u with macron":"Latin small letter u with macron","Latin small letter u with ogonek":"Latin small letter u with ogonek","Latin small letter u with ring above":"Latin small letter u with ring above","Latin small letter u with tilde":"Latin small letter u with tilde","Latin small letter w with circumflex":"Latin small letter w with circumflex","Latin small letter y with circumflex":"Latin small letter y with circumflex","Latin small letter z with acute":"Latin small letter z with acute","Latin small letter z with caron":"Latin small letter z with caron","Latin small letter z with dot above":"Latin small letter z with dot above","Latin small ligature ij":"Latin small ligature ij","Latin small ligature oe":"Latin small ligature oe","Left aligned image":"Left aligned image","Left double quotation mark":"Left double quotation mark","Left single quotation mark":"Left single quotation mark","Left-pointing double angle quotation mark":"Left-pointing double angle quotation mark","leftwards arrow to bar":"leftwards arrow to bar","leftwards dashed arrow":"leftwards dashed arrow","leftwards double arrow":"leftwards double arrow","leftwards simple arrow":"leftwards simple arrow","Less-than or equal to":"Less-than or equal to","Less-than sign":"Less-than sign","Light blue":"Light blue","Light green":"Light green","Light grey":"Light grey",Link:"Link","Link image":"Link image","Link URL":"Link URL","Lira sign":"Lira sign","List properties":"List properties","Livre tournois sign":"Livre tournois sign","Logical and":"Logical and","Logical or":"Logical or","Lower-latin":"Lower-latin","Lower–roman":"Lower–roman",Macron:"Macron","Manat sign":"Manat sign","Match case":"Match case","Media toolbar":"Media toolbar","Media URL":"Media URL","media widget":"media widget","Merge cell down":"Merge cell down","Merge cell left":"Merge cell left","Merge cell right":"Merge cell right","Merge cell up":"Merge cell up","Merge cells":"Merge cells","Mill sign":"Mill sign","Minus sign":"Minus sign","Multiplication sign":"Multiplication sign","N-ary product":"N-ary product","N-ary summation":"N-ary summation",Nabla:"Nabla","Naira sign":"Naira sign","New sheqel sign":"New sheqel sign",Next:"Next","Next result":"Next result","No preview available":"No preview available",None:"None","Nordic mark sign":"Nordic mark sign","Not an element of":"Not an element of","Not equal to":"Not equal to","Not sign":"Not sign","Numbered List":"Numbered List","Numbered list styles toolbar":"Numbered list styles toolbar","on with exclamation mark with left right arrow above":"on with exclamation mark with left right arrow above","Open in a new tab":"Open in a new tab","Open link in new tab":"Open link in new tab","Open media in new tab":"Open media in new tab",Orange:"Orange",Original:"Original",Outset:"Outset",Overline:"Overline",Padding:"Padding","Page break":"Page break",Paragraph:"Paragraph","Paragraph sign":"Paragraph sign","Partial differential":"Partial differential","Paste raw HTML here...":"Paste raw HTML here...","Paste the media URL in the input.":"Paste the media URL in the input.","Per mille sign":"Per mille sign","Per ten thousand sign":"Per ten thousand sign","Peseta sign":"Peseta sign","Peso sign":"Peso sign","Pink marker":"Pink marker","Plus-minus sign":"Plus-minus sign","Pound sign":"Pound sign",Previous:"Previous","Previous result":"Previous result","Proportional to":"Proportional to",Purple:"Purple","Question exclamation mark":"Question exclamation mark",Red:"Red","Red pen":"Red pen",Redo:"Redo","Registered sign":"Registered sign","Remove color":"Remove color","Remove Format":"Remove Format","Remove highlight":"Remove highlight","Remove language":"Remove language",Replace:"Replace","Replace all":"Replace all","Replace with…":"Replace with…","Resize image":"Resize image","Resize image to %0":"Resize image to %0","Resize image to the original size":"Resize image to the original size","Restore default":"Restore default","Reversed order":"Reversed order","Reversed paragraph sign":"Reversed paragraph sign","Rich Text Editor":"Rich Text Editor","Rich Text Editor. Editing area: %0":"Rich Text Editor. Editing area: %0",Ridge:"Ridge","Right aligned image":"Right aligned image","Right double quotation mark":"Right double quotation mark","Right single quotation mark":"Right single quotation mark","Right-pointing double angle quotation mark":"Right-pointing double angle quotation mark","rightwards arrow to bar":"rightwards arrow to bar","rightwards dashed arrow":"rightwards dashed arrow","rightwards double arrow":"rightwards double arrow","rightwards simple arrow":"rightwards simple arrow",Row:"Row","Ruble sign":"Ruble sign","Rupee sign":"Rupee sign",Save:"Save","Save changes":"Save changes","Saving changes":"Saving changes","Section sign":"Section sign","Select all":"Select all","Select column":"Select column","Select row":"Select row","Show more items":"Show more items","Show options":"Show options","Side image":"Side image","Single left-pointing angle quotation mark":"Single left-pointing angle quotation mark","Single low-9 quotation mark":"Single low-9 quotation mark","Single right-pointing angle quotation mark":"Single right-pointing angle quotation mark",Small:"Small",Solid:"Solid","soon with rightwards arrow above":"soon with rightwards arrow above",Source:"Source","Special characters":"Special characters","Spesmilo sign":"Spesmilo sign","Split cell horizontally":"Split cell horizontally","Split cell vertically":"Split cell vertically",Square:"Square","Square root":"Square root","Start at":"Start at","Start index must be greater than 0.":"Start index must be greater than 0.",Strikethrough:"Strikethrough",Style:"Style",Subscript:"Subscript",Superscript:"Superscript","Table alignment toolbar":"Table alignment toolbar","Table cell text alignment":"Table cell text alignment","Table properties":"Table properties","Table toolbar":"Table toolbar","Tenge sign":"Tenge sign","Text alignment":"Text alignment","Text alignment toolbar":"Text alignment toolbar","Text alternative":"Text alternative","Text highlight toolbar":"Text highlight toolbar","Text to find must not be empty.":"Text to find must not be empty.","The color is invalid. Try \"#FF0000\" or \"rgb(255,0,0)\" or \"red\".":"The color is invalid. Try \"#FF0000\" or \"rgb(255,0,0)\" or \"red\".","The URL must not be empty.":"The URL must not be empty.","The value is invalid. Try \"10px\" or \"2em\" or simply \"2\".":"The value is invalid. Try \"10px\" or \"2em\" or simply \"2\".","There exists":"There exists","This link has no URL":"This link has no URL","This media URL is not supported.":"This media URL is not supported.","Tilde operator":"Tilde operator",Tiny:"Tiny","Tip: Find some text first in order to replace it.":"Tip: Find some text first in order to replace it.","Tip: Paste the URL into the content to embed faster.":"Tip: Paste the URL into the content to embed faster.","Toggle caption off":"Toggle caption off","Toggle caption on":"Toggle caption on","Toggle the circle list style":"Toggle the circle list style","Toggle the decimal list style":"Toggle the decimal list style","Toggle the decimal with leading zero list style":"Toggle the decimal with leading zero list style","Toggle the disc list style":"Toggle the disc list style","Toggle the lower–latin list style":"Toggle the lower–latin list style","Toggle the lower–roman list style":"Toggle the lower–roman list style","Toggle the square list style":"Toggle the square list style","Toggle the upper–latin list style":"Toggle the upper–latin list style","Toggle the upper–roman list style":"Toggle the upper–roman list style","top with upwards arrow above":"top with upwards arrow above","Trade mark sign":"Trade mark sign","Tugrik sign":"Tugrik sign","Turkish lira sign":"Turkish lira sign",Turquoise:"Turquoise","Two dot leader":"Two dot leader",Underline:"Underline",Undo:"Undo",Union:"Union",Unlink:"Unlink","up down arrow with base":"up down arrow with base",Update:"Update","Update image URL":"Update image URL","Upload failed":"Upload failed","Upload in progress":"Upload in progress","Upper-latin":"Upper-latin","Upper-roman":"Upper-roman","upwards arrow to bar":"upwards arrow to bar","upwards dashed arrow":"upwards dashed arrow","upwards double arrow":"upwards double arrow","upwards simple arrow":"upwards simple arrow","Vertical text alignment toolbar":"Vertical text alignment toolbar","Vulgar fraction one half":"Vulgar fraction one half","Vulgar fraction one quarter":"Vulgar fraction one quarter","Vulgar fraction three quarters":"Vulgar fraction three quarters",White:"White","Whole words only":"Whole words only","Widget toolbar":"Widget toolbar",Width:"Width","Won sign":"Won sign","Words: %0":"Words: %0","Wrap text":"Wrap text",Yellow:"Yellow","Yellow marker":"Yellow marker","Yen sign":"Yen sign"}	);})(window.CKEDITOR_TRANSLATIONS||(window.CKEDITOR_TRANSLATIONS={}));
/*!
 * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md.
 */
(function webpackUniversalModuleDefinition(root, factory) {
	if(typeof exports === 'object' && typeof module === 'object')
		module.exports = factory();
	else if(typeof define === 'function' && define.amd)
		define([], factory);
	else if(typeof exports === 'object')
		exports["ckEditor5"] = factory();
	else
		root["ckEditor5"] = factory();
})(self, () => {
return /******/ (() => { // webpackBootstrap
/******/ 	"use strict";
/******/ 	var __webpack_modules__ = ({

/***/ 277:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _node_modules_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_node_modules_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, "#fullscreenoverlay{overflow:hidden}#fullscreeneditor{bottom:0;left:0;position:fixed!important;right:0;top:0;z-index:1000}#fullscreeneditor,#fullscreeneditor .ck.ck-editor__editable.ck-editor__editable_inline,#fullscreeneditor .ck.ck-editor__main{height:calc(100% - 12px)!important;max-height:calc(100% - 12px)!important;max-width:100%!important;width:100%!important}#fullscreeneditor .ck-source-editing-area,#fullscreeneditor .ck-source-editing-area>textarea{height:100%!important;max-height:100%!important;max-width:100%!important;overflow:auto;width:100%!important}", "",{"version":3,"sources":["webpack://./ckeditor5-fullscreen/css/style.css"],"names":[],"mappings":"AAAA,mBACC,eACD,CACA,kBAKC,QAAS,CAFT,MAAO,CAFP,wBAA0B,CAG1B,OAAO,CAFP,KAAK,CAIL,YACD,CACA,6HAEC,kCAAoC,CADpC,sCAAwC,CAExC,wBAA0B,CAC1B,oBACD,CAUA,6FANC,qBAAsB,CADtB,yBAA2B,CAE3B,wBAA0B,CAE1B,aAAc,CADd,oBAUD","sourcesContent":["#fullscreenoverlay{\n\toverflow: hidden;\n}\n#fullscreeneditor{\n\tposition: fixed !important;\n\ttop:0;\n\tleft: 0;\n\tright:0;\n\tbottom: 0;\n\tz-index: 1000;\n}\n#fullscreeneditor, #fullscreeneditor .ck.ck-editor__editable.ck-editor__editable_inline, #fullscreeneditor .ck.ck-editor__main {\n\tmax-height: calc(100% - 12px) !important;\n\theight: calc(100% - 12px) !important;\n\tmax-width: 100% !important;\n\twidth: 100% !important;\n}\n\n #fullscreeneditor .ck-source-editing-area {\n\tmax-height: 100% !important;\n\theight:100% !important;\n\tmax-width: 100% !important;\n\twidth: 100% !important;\n\toverflow: auto;\n}\n\n#fullscreeneditor .ck-source-editing-area > textarea {\n\tmax-height: 100% !important;\n\theight:100% !important;\n\tmax-width: 100% !important;\n\twidth: 100% !important;\n\toverflow: auto;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8180:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content code{background-color:hsla(0,0%,78%,.3);border-radius:2px;padding:.15em}.ck.ck-editor__editable .ck-code_selected{background-color:hsla(0,0%,78%,.5)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-basic-styles/theme/code.css"],"names":[],"mappings":"AAKA,iBACC,kCAAuC,CAEvC,iBAAkB,CADlB,aAED,CAEA,0CACC,kCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content code {\n\tbackground-color: hsla(0, 0%, 78%, 0.3);\n\tpadding: .15em;\n\tborder-radius: 2px;\n}\n\n.ck.ck-editor__editable .ck-code_selected  {\n\tbackground-color: hsla(0, 0%, 78%, 0.5);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 636:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content blockquote{border-left:5px solid #ccc;font-style:italic;margin-left:0;margin-right:0;overflow:hidden;padding-left:1.5em;padding-right:1.5em}.ck-content[dir=rtl] blockquote{border-left:0;border-right:5px solid #ccc}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-block-quote/theme/blockquote.css"],"names":[],"mappings":"AAKA,uBAWC,0BAAsC,CADtC,iBAAkB,CAFlB,aAAc,CACd,cAAe,CAPf,eAAgB,CAIhB,kBAAmB,CADnB,mBAOD,CAEA,gCACC,aAAc,CACd,2BACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content blockquote {\n\t/* See #12 */\n\toverflow: hidden;\n\n\t/* https://github.com/ckeditor/ckeditor5-block-quote/issues/15 */\n\tpadding-right: 1.5em;\n\tpadding-left: 1.5em;\n\n\tmargin-left: 0;\n\tmargin-right: 0;\n\tfont-style: italic;\n\tborder-left: solid 5px hsl(0, 0%, 80%);\n}\n\n.ck-content[dir=\"rtl\"] blockquote {\n\tborder-left: 0;\n\tborder-right: solid 5px hsl(0, 0%, 80%);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 390:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-editor__editable .ck.ck-clipboard-drop-target-position{display:inline;pointer-events:none;position:relative}.ck.ck-editor__editable .ck.ck-clipboard-drop-target-position span{position:absolute;width:0}.ck.ck-editor__editable .ck-widget:-webkit-drag>.ck-widget__selection-handle,.ck.ck-editor__editable .ck-widget:-webkit-drag>.ck-widget__type-around{display:none}:root{--ck-clipboard-drop-target-dot-width:12px;--ck-clipboard-drop-target-dot-height:8px;--ck-clipboard-drop-target-color:var(--ck-color-focus-border)}.ck.ck-editor__editable .ck.ck-clipboard-drop-target-position span{background:var(--ck-clipboard-drop-target-color);border:1px solid var(--ck-clipboard-drop-target-color);bottom:calc(var(--ck-clipboard-drop-target-dot-height)*-.5);margin-left:-1px;top:calc(var(--ck-clipboard-drop-target-dot-height)*-.5)}.ck.ck-editor__editable .ck.ck-clipboard-drop-target-position span:after{border-color:var(--ck-clipboard-drop-target-color) transparent transparent transparent;border-style:solid;border-width:calc(var(--ck-clipboard-drop-target-dot-height)) calc(var(--ck-clipboard-drop-target-dot-width)*.5) 0 calc(var(--ck-clipboard-drop-target-dot-width)*.5);content:\"\";display:block;height:0;left:50%;position:absolute;top:calc(var(--ck-clipboard-drop-target-dot-height)*-.5);transform:translateX(-50%);width:0}.ck.ck-editor__editable .ck-widget.ck-clipboard-drop-target-range{outline:var(--ck-widget-outline-thickness) solid var(--ck-clipboard-drop-target-color)!important}.ck.ck-editor__editable .ck-widget:-webkit-drag{zoom:.6;outline:none!important}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-clipboard/theme/clipboard.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-clipboard/clipboard.css"],"names":[],"mappings":"AASC,8DACC,cAAe,CAEf,mBAAoB,CADpB,iBAOD,CAJC,mEACC,iBAAkB,CAClB,OACD,CAWA,qJACC,YACD,CCzBF,MACC,yCAA0C,CAC1C,yCAA0C,CAC1C,6DACD,CAOE,mEAIC,gDAAiD,CADjD,sDAAuD,CAFvD,2DAA8D,CAI9D,gBAAiB,CAHjB,wDAqBD,CAfC,yEAWC,sFAAuF,CAEvF,kBAAmB,CADnB,qKAA0K,CAX1K,UAAW,CAIX,aAAc,CAFd,QAAS,CAIT,QAAS,CADT,iBAAkB,CAElB,wDAA2D,CAE3D,0BAA2B,CAR3B,OAYD,CA2DF,kEACC,gGACD,CAKA,gDACC,OAAS,CACT,sBACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-editor__editable {\n\t/*\n\t * Vertical drop target (in text).\n\t */\n\t& .ck.ck-clipboard-drop-target-position {\n\t\tdisplay: inline;\n\t\tposition: relative;\n\t\tpointer-events: none;\n\n\t\t& span {\n\t\t\tposition: absolute;\n\t\t\twidth: 0;\n\t\t}\n\t}\n\n\t/*\n\t * Styles of the widget being dragged (its preview).\n\t */\n\t& .ck-widget:-webkit-drag {\n\t\t& > .ck-widget__selection-handle {\n\t\t\tdisplay: none;\n\t\t}\n\n\t\t& > .ck-widget__type-around {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-clipboard-drop-target-dot-width: 12px;\n\t--ck-clipboard-drop-target-dot-height: 8px;\n\t--ck-clipboard-drop-target-color: var(--ck-color-focus-border)\n}\n\n.ck.ck-editor__editable {\n\t/*\n\t * Vertical drop target (in text).\n\t */\n\t& .ck.ck-clipboard-drop-target-position {\n\t\t& span {\n\t\t\tbottom: calc(-.5 * var(--ck-clipboard-drop-target-dot-height));\n\t\t\ttop: calc(-.5 * var(--ck-clipboard-drop-target-dot-height));\n\t\t\tborder: 1px solid var(--ck-clipboard-drop-target-color);\n\t\t\tbackground: var(--ck-clipboard-drop-target-color);\n\t\t\tmargin-left: -1px;\n\n\t\t\t/* The triangle above the marker */\n\t\t\t&::after {\n\t\t\t\tcontent: \"\";\n\t\t\t\twidth: 0;\n\t\t\t\theight: 0;\n\n\t\t\t\tdisplay: block;\n\t\t\t\tposition: absolute;\n\t\t\t\tleft: 50%;\n\t\t\t\ttop: calc(var(--ck-clipboard-drop-target-dot-height) * -.5);\n\n\t\t\t\ttransform: translateX(-50%);\n\t\t\t\tborder-color: var(--ck-clipboard-drop-target-color) transparent transparent transparent;\n\t\t\t\tborder-width: calc(var(--ck-clipboard-drop-target-dot-height)) calc(.5 * var(--ck-clipboard-drop-target-dot-width)) 0 calc(.5 * var(--ck-clipboard-drop-target-dot-width));\n\t\t\t\tborder-style: solid;\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t// Horizontal drop target (between blocks).\n\t& .ck.ck-clipboard-drop-target-position {\n\t\tdisplay: block;\n\t\tposition: relative;\n\t\twidth: 100%;\n\t\theight: 0;\n\t\tmargin: 0;\n\t\ttext-align: initial;\n\n\t\t& .ck-clipboard-drop-target__line {\n\t\t\tposition: absolute;\n\t\t\twidth: 100%;\n\t\t\theight: 0;\n\t\t\tborder: 1px solid var(--ck-clipboard-drop-target-color);\n\t\t\tmargin-top: -1px;\n\n\t\t\t&::before {\n\t\t\t\tcontent: \"\";\n\t\t\t\twidth: 0;\n\t\t\t\theight: 0;\n\n\t\t\t\tdisplay: block;\n\t\t\t\tposition: absolute;\n\t\t\t\tleft: calc(-1 * var(--ck-clipboard-drop-target-dot-size));\n\t\t\t\ttop: 0;\n\n\t\t\t\ttransform: translateY(-50%);\n\t\t\t\tborder-color: transparent transparent transparent var(--ck-clipboard-drop-target-color);\n\t\t\t\tborder-width: var(--ck-clipboard-drop-target-dot-size) 0 var(--ck-clipboard-drop-target-dot-size) calc(2 * var(--ck-clipboard-drop-target-dot-size));\n\t\t\t\tborder-style: solid;\n\t\t\t}\n\n\t\t\t&::after {\n\t\t\t\tcontent: \"\";\n\t\t\t\twidth: 0;\n\t\t\t\theight: 0;\n\n\t\t\t\tdisplay: block;\n\t\t\t\tposition: absolute;\n\t\t\t\tright: calc(-1 * var(--ck-clipboard-drop-target-dot-size));\n\t\t\t\ttop: 0;\n\n\t\t\t\ttransform: translateY(-50%);\n\t\t\t\tborder-color: transparent var(--ck-clipboard-drop-target-color) transparent transparent;\n\t\t\t\tborder-width: var(--ck-clipboard-drop-target-dot-size) calc(2 * var(--ck-clipboard-drop-target-dot-size)) var(--ck-clipboard-drop-target-dot-size) 0;\n\t\t\t\tborder-style: solid;\n\t\t\t}\n\t\t}\n\t}\n\t*/\n\n\t/*\n\t * Styles of the widget that it a drop target.\n\t */\n\t& .ck-widget.ck-clipboard-drop-target-range {\n\t\toutline: var(--ck-widget-outline-thickness) solid var(--ck-clipboard-drop-target-color) !important;\n\t}\n\n\t/*\n\t * Styles of the widget being dragged (its preview).\n\t */\n\t& .ck-widget:-webkit-drag {\n\t\tzoom: 0.6;\n\t\toutline: none !important;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3638:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-editor{position:relative}.ck.ck-editor .ck-editor__top .ck-sticky-panel .ck-toolbar{z-index:var(--ck-z-modal)}.ck.ck-editor__top .ck-sticky-panel .ck-toolbar{border-radius:0}.ck-rounded-corners .ck.ck-editor__top .ck-sticky-panel .ck-toolbar,.ck.ck-editor__top .ck-sticky-panel .ck-toolbar.ck-rounded-corners{border-radius:var(--ck-border-radius);border-bottom-left-radius:0;border-bottom-right-radius:0}.ck.ck-editor__top .ck-sticky-panel .ck-toolbar{border-bottom-width:0}.ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content_sticky .ck-toolbar{border-bottom-width:1px;border-radius:0}.ck-rounded-corners .ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content_sticky .ck-toolbar,.ck.ck-editor__top .ck-sticky-panel .ck-sticky-panel__content_sticky .ck-toolbar.ck-rounded-corners{border-radius:var(--ck-border-radius);border-radius:0}.ck.ck-editor__main>.ck-editor__editable{background:var(--ck-color-base-background);border-radius:0}.ck-rounded-corners .ck.ck-editor__main>.ck-editor__editable,.ck.ck-editor__main>.ck-editor__editable.ck-rounded-corners{border-radius:var(--ck-border-radius);border-top-left-radius:0;border-top-right-radius:0}.ck.ck-editor__main>.ck-editor__editable:not(.ck-focused){border-color:var(--ck-color-base-border)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-editor-classic/theme/classiceditor.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-editor-classic/classiceditor.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAKA,cAIC,iBAMD,CAJC,2DAEC,yBACD,CCLC,gDCED,eDKC,CAPA,uICMA,qCAAsC,CDJpC,2BAA4B,CAC5B,4BAIF,CAPA,gDAMC,qBACD,CAEA,iFACC,uBAAwB,CCR1B,eDaC,CANA,yMCHA,qCAAsC,CDOpC,eAEF,CAKF,yCAEC,0CAA2C,CCpB3C,eD8BD,CAZA,yHCdE,qCAAsC,CDmBtC,wBAAyB,CACzB,yBAMF,CAHC,0DACC,wCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-editor {\n\t/* All the elements within `.ck-editor` are positioned relatively to it.\n\t If any element needs to be positioned with respect to the <body>, etc.,\n\t it must land outside of the `.ck-editor` in DOM. */\n\tposition: relative;\n\n\t& .ck-editor__top .ck-sticky-panel .ck-toolbar {\n\t\t/* https://github.com/ckeditor/ckeditor5-editor-classic/issues/62 */\n\t\tz-index: var(--ck-z-modal);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../mixins/_rounded.css\";\n\n.ck.ck-editor__top {\n\t& .ck-sticky-panel {\n\t\t& .ck-toolbar {\n\t\t\t@mixin ck-rounded-corners {\n\t\t\t\tborder-bottom-left-radius: 0;\n\t\t\t\tborder-bottom-right-radius: 0;\n\t\t\t}\n\n\t\t\tborder-bottom-width: 0;\n\t\t}\n\n\t\t& .ck-sticky-panel__content_sticky .ck-toolbar {\n\t\t\tborder-bottom-width: 1px;\n\n\t\t\t@mixin ck-rounded-corners {\n\t\t\t\tborder-radius: 0;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/* Note: Use ck-editor__main to make sure these styles don't apply to other editor types */\n.ck.ck-editor__main > .ck-editor__editable {\n\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/113 */\n\tbackground: var(--ck-color-base-background);\n\n\t@mixin ck-rounded-corners {\n\t\tborder-top-left-radius: 0;\n\t\tborder-top-right-radius: 0;\n\t}\n\n\t&:not(.ck-focused) {\n\t\tborder-color: var(--ck-color-base-border);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8894:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-placeholder,.ck.ck-placeholder{position:relative}.ck .ck-placeholder:before,.ck.ck-placeholder:before{content:attr(data-placeholder);left:0;pointer-events:none;position:absolute;right:0}.ck.ck-read-only .ck-placeholder:before{display:none}.ck.ck-reset_all .ck-placeholder{position:relative}.ck .ck-placeholder:before,.ck.ck-placeholder:before{color:var(--ck-color-engine-placeholder-text);cursor:text}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-engine/theme/placeholder.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-engine/placeholder.css"],"names":[],"mappings":"AAMA,uCAEC,iBAWD,CATC,qDAIC,8BAA+B,CAF/B,MAAO,CAKP,mBAAoB,CANpB,iBAAkB,CAElB,OAKD,CAKA,wCACC,YACD,CAQD,iCACC,iBACD,CC5BC,qDAEC,6CAA8C,CAD9C,WAED","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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/* See ckeditor/ckeditor5#936. */\n.ck.ck-placeholder,\n.ck .ck-placeholder {\n\tposition: relative;\n\n\t&::before {\n\t\tposition: absolute;\n\t\tleft: 0;\n\t\tright: 0;\n\t\tcontent: attr(data-placeholder);\n\n\t\t/* See ckeditor/ckeditor5#469. */\n\t\tpointer-events: none;\n\t}\n}\n\n/* See ckeditor/ckeditor5#1987. */\n.ck.ck-read-only .ck-placeholder {\n\t&::before {\n\t\tdisplay: none;\n\t}\n}\n\n/*\n * Rules for the `ck-placeholder` are loaded before the rules for `ck-reset_all` in the base CKEditor 5 DLL build.\n * This fix overwrites the incorrectly set `position: static` from `ck-reset_all`.\n * See https://github.com/ckeditor/ckeditor5/issues/11418.\n */\n.ck.ck-reset_all .ck-placeholder {\n\tposition: relative;\n}\n","/*\n * Copyright (c) 2003-2022, 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/* See ckeditor/ckeditor5#936. */\n.ck.ck-placeholder, .ck .ck-placeholder {\n\t&::before {\n\t\tcursor: text;\n\t\tcolor: var(--ck-color-engine-placeholder-text);\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4401:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-editor__editable span[data-ck-unsafe-element]{display:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-engine/theme/renderer.css"],"names":[],"mappings":"AAMA,qDACC,YACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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/* Elements marked by the Renderer as hidden should be invisible in the editor. */\n.ck.ck-editor__editable span[data-ck-unsafe-element] {\n\tdisplay: none;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5436:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-find-result{background:var(--ck-color-highlight-background);color:var(--ck-color-text)}.ck-find-result_selected{background:#ff9633}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-find-and-replace/theme/findandreplace.css"],"names":[],"mappings":"AAKA,gBACC,+CAAgD,CAChD,0BACD,CAEA,yBACC,kBACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-find-result {\n\tbackground: var(--ck-color-highlight-background);\n\tcolor: var(--ck-color-text);\n}\n\n.ck-find-result_selected {\n\tbackground: hsl(29, 100%, 60%);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9289:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-find-and-replace-form{max-width:100%}.ck.ck-find-and-replace-form fieldset{display:flex}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find .ck-results-counter{position:absolute}.ck.ck-find-and-replace-form{width:400px}.ck.ck-find-and-replace-form:focus{outline:none}.ck.ck-find-and-replace-form fieldset{align-content:stretch;align-items:center;border:0;flex-direction:row;flex-wrap:nowrap;margin:0;padding:var(--ck-spacing-large)}.ck.ck-find-and-replace-form fieldset>.ck-button{flex:0 0 auto}[dir=ltr] .ck.ck-find-and-replace-form fieldset>*+*{margin-left:var(--ck-spacing-standard)}[dir=rtl] .ck.ck-find-and-replace-form fieldset>*+*{margin-right:var(--ck-spacing-standard)}.ck.ck-find-and-replace-form fieldset .ck-labeled-field-view{flex:1 1 auto}.ck.ck-find-and-replace-form fieldset .ck-labeled-field-view .ck-input{min-width:50px;width:100%}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find{align-items:flex-start}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button-find{font-weight:700}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button-find .ck-button__label{padding-left:var(--ck-spacing-large);padding-right:var(--ck-spacing-large)}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button-prev>.ck-icon{transform:rotate(90deg)}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button-next>.ck-icon{transform:rotate(-90deg)}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find .ck-results-counter{top:50%;transform:translateY(-50%)}[dir=ltr] .ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find .ck-results-counter{right:var(--ck-spacing-standard)}[dir=rtl] .ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find .ck-results-counter{left:var(--ck-spacing-standard)}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find .ck-results-counter{color:var(--ck-color-base-border)}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace{flex-wrap:wrap;justify-content:flex-end;margin-top:calc(var(--ck-spacing-large)*-1)}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace>.ck-labeled-field-view{margin-bottom:var(--ck-spacing-large)}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace>.ck-options-dropdown{margin-left:0;margin-right:auto}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace>.ck-labeled-field-view,.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace>.ck-labeled-field-view .ck-input{width:100%}@media screen and (max-width:600px){.ck.ck-find-and-replace-form{width:300px}.ck.ck-find-and-replace-form fieldset{flex-wrap:wrap}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find .ck-labeled-field-view{flex:1 0 auto;margin-bottom:var(--ck-spacing-standard);width:100%}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button{text-align:center}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button:first-of-type{flex:1 1 auto}[dir=ltr] .ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button:first-of-type{margin-left:0}[dir=rtl] .ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button:first-of-type{margin-right:0}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__find>.ck-button:first-of-type .ck-button__label{text-align:center;width:100%}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace>:not(.ck-labeled-field-view){flex:1 1 auto}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace>.ck-dropdown:not(.ck-labeled-field-view){flex-grow:0}.ck.ck-find-and-replace-form fieldset.ck-find-and-replace-form__replace>.ck-button:not(.ck-labeled-field-view)>.ck-button__label{text-align:center;width:100%}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-find-and-replace/theme/findandreplaceform.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-find-and-replace/findandreplaceform.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css"],"names":[],"mappings":"AAKA,6BACC,cAUD,CARC,sCACC,YAMD,CAHC,yFACC,iBACD,CCNF,6BACC,WAyGD,CAnGC,mCACC,YACD,CAEA,sCAIC,qBAAsB,CADtB,kBAAmB,CAInB,QAAS,CANT,kBAAmB,CACnB,gBAAiB,CAMjB,QAAS,CAFT,+BAwFD,CApFC,iDACC,aACD,CAGC,oDACC,sCACD,CAIA,oDACC,uCACD,CAGD,6DACC,aAMD,CAJC,uEAEC,cAAe,CADf,UAED,CAID,qEAEC,sBAkCD,CAhCC,qFACC,eAOD,CAJC,uGACC,oCAAqC,CACrC,qCACD,CAGD,8FACC,uBACD,CAEA,8FACC,wBACD,CAEA,yFACC,OAAQ,CACR,0BAWD,CAbA,mGAKE,gCAQF,CAbA,mGASE,+BAIF,CAbA,yFAYC,iCACD,CAID,wEACC,cAAe,CACf,wBAAyB,CACzB,2CAeD,CAbC,+FACC,qCACD,CAEA,6FAEC,aAAc,CADd,iBAED,CAEA,wMAEC,UACD,CCzGF,oCD+GA,6BACC,WAiDD,CA/CC,sCACC,cA6CD,CAzCE,4FACC,aAAc,CAEd,wCAAyC,CADzC,UAED,CAEA,gFACC,iBAkBD,CAhBC,8FACC,aAcD,CAfA,wGAIE,aAWF,CAfA,wGAQE,cAOF,CAJC,gHAEC,iBAAkB,CADlB,UAED,CAMH,qGACC,aAUD,CARC,iHACC,WACD,CAEA,iIAEC,iBAAkB,CADlB,UAED,CC5JH","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-find-and-replace-form {\n\tmax-width: 100%;\n\n\t& fieldset {\n\t\tdisplay: flex;\n\n\t\t/* The find fieldset */\n\t\t&.ck-find-and-replace-form__find .ck-results-counter {\n\t\t\tposition: absolute;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n.ck.ck-find-and-replace-form {\n\twidth: 400px;\n\n\t/*\n\t * The <form> needs tabindex=\"-1\" for proper Esc handling after being clicked\n\t * but the side effect is that this creates a nasty focus outline in some browsers.\n\t */\n\t&:focus {\n\t\toutline: none;\n\t}\n\n\t& fieldset {\n\t\tflex-direction: row;\n\t\tflex-wrap: nowrap;\n\t\talign-items: center;\n\t\talign-content: stretch;\n\n\t\tpadding: var(--ck-spacing-large);\n\t\tborder: 0;\n\t\tmargin: 0;\n\n\t\t& > .ck-button {\n\t\t\tflex: 0 0 auto;\n\t\t}\n\n\t\t@mixin ck-dir ltr {\n\t\t\t& > * + * {\n\t\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t\t}\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\t& > * + * {\n\t\t\t\tmargin-right: var(--ck-spacing-standard);\n\t\t\t}\n\t\t}\n\n\t\t& .ck-labeled-field-view {\n\t\t\tflex: 1 1 auto;\n\n\t\t\t& .ck-input {\n\t\t\t\twidth: 100%;\n\t\t\t\tmin-width: 50px;\n\t\t\t}\n\t\t}\n\n\t\t/* The find fieldset */\n\t\t&.ck-find-and-replace-form__find {\n\t\t\t/* To display all controls in line when there's an error under the input */\n\t\t\talign-items: flex-start;\n\n\t\t\t& > .ck-button-find {\n\t\t\t\tfont-weight: bold;\n\n\t\t\t\t/* Beef the find button up a little. It's the main action button in the form */\n\t\t\t\t& .ck-button__label {\n\t\t\t\t\tpadding-left: var(--ck-spacing-large);\n\t\t\t\t\tpadding-right: var(--ck-spacing-large);\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t& > .ck-button-prev > .ck-icon {\n\t\t\t\ttransform: rotate(90deg);\n\t\t\t}\n\n\t\t\t& > .ck-button-next > .ck-icon {\n\t\t\t\ttransform: rotate(-90deg);\n\t\t\t}\n\n\t\t\t& .ck-results-counter {\n\t\t\t\ttop: 50%;\n\t\t\t\ttransform: translateY(-50%);\n\n\t\t\t\t@mixin ck-dir ltr {\n\t\t\t\t\tright: var(--ck-spacing-standard);\n\t\t\t\t}\n\n\t\t\t\t@mixin ck-dir rtl {\n\t\t\t\t\tleft: var(--ck-spacing-standard);\n\t\t\t\t}\n\n\t\t\t\tcolor: var(--ck-color-base-border);\n\t\t\t}\n\t\t}\n\n\t\t/* The replace fieldset */\n\t\t&.ck-find-and-replace-form__replace {\n\t\t\tflex-wrap: wrap;\n\t\t\tjustify-content: flex-end;\n\t\t\tmargin-top: calc( -1 * var(--ck-spacing-large) );\n\n\t\t\t& > .ck-labeled-field-view {\n\t\t\t\tmargin-bottom: var(--ck-spacing-large);\n\t\t\t}\n\n\t\t\t& > .ck-options-dropdown {\n\t\t\t\tmargin-right: auto;\n\t\t\t\tmargin-left: 0;\n\t\t\t}\n\n\t\t\t& > .ck-labeled-field-view,\n\t\t\t& > .ck-labeled-field-view .ck-input {\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\t}\n\t}\n}\n\n@mixin ck-media-phone {\n\t.ck.ck-find-and-replace-form {\n\t\twidth: 300px;\n\n\t\t& fieldset {\n\t\t\tflex-wrap: wrap;\n\n\t\t\t/* The find fieldset */\n\t\t\t&.ck-find-and-replace-form__find {\n\t\t\t\t& .ck-labeled-field-view {\n\t\t\t\t\tflex: 1 0 auto;\n\t\t\t\t\twidth: 100%;\n\t\t\t\t\tmargin-bottom: var(--ck-spacing-standard);\n\t\t\t\t}\n\n\t\t\t\t& > .ck-button {\n\t\t\t\t\ttext-align: center;\n\n\t\t\t\t\t&:first-of-type {\n\t\t\t\t\t\tflex: 1 1 auto;\n\n\t\t\t\t\t\t@mixin ck-dir ltr {\n\t\t\t\t\t\t\tmargin-left: 0;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t@mixin ck-dir rtl {\n\t\t\t\t\t\t\tmargin-right: 0;\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t& .ck-button__label {\n\t\t\t\t\t\t\twidth: 100%;\n\t\t\t\t\t\t\ttext-align: center;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t/* The replace fieldset */\n\t\t\t&.ck-find-and-replace-form__replace > :not(.ck-labeled-field-view) {\n\t\t\t\tflex: 1 1 auto;\n\n\t\t\t\t&.ck-dropdown {\n\t\t\t\t\tflex-grow: 0;\n\t\t\t\t}\n\n\t\t\t\t&.ck-button > .ck-button__label {\n\t\t\t\t\twidth: 100%;\n\t\t\t\t\ttext-align: center;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 2585:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-button.ck-color-table__remove-color{align-items:center;display:flex;width:100%}label.ck.ck-color-grid__label{font-weight:unset}.ck .ck-button.ck-color-table__remove-color{border-bottom-left-radius:0;border-bottom-right-radius:0;padding:calc(var(--ck-spacing-standard)/2) var(--ck-spacing-standard)}.ck .ck-button.ck-color-table__remove-color:not(:focus){border-bottom:1px solid var(--ck-color-base-border)}[dir=ltr] .ck .ck-button.ck-color-table__remove-color .ck.ck-icon{margin-right:var(--ck-spacing-standard)}[dir=rtl] .ck .ck-button.ck-color-table__remove-color .ck.ck-icon{margin-left:var(--ck-spacing-standard)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-font/theme/fontcolor.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-font/fontcolor.css"],"names":[],"mappings":"AAKA,4CAEC,kBAAmB,CADnB,YAAa,CAEb,UACD,CAEA,8BACC,iBACD,CCNA,4CAEC,2BAA4B,CAC5B,4BAA6B,CAF7B,qEAiBD,CAbC,wDACC,mDACD,CAEA,kEAEE,uCAMF,CARA,kEAME,sCAEF","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck .ck-button.ck-color-table__remove-color {\n\tdisplay: flex;\n\talign-items: center;\n\twidth: 100%;\n}\n\nlabel.ck.ck-color-grid__label {\n\tfont-weight: unset;\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n.ck .ck-button.ck-color-table__remove-color {\n\tpadding: calc(var(--ck-spacing-standard) / 2 ) var(--ck-spacing-standard);\n\tborder-bottom-left-radius: 0;\n\tborder-bottom-right-radius: 0;\n\n\t&:not(:focus) {\n\t\tborder-bottom: 1px solid var(--ck-color-base-border);\n\t}\n\n\t& .ck.ck-icon {\n\t\t@mixin ck-dir ltr {\n\t\t\tmargin-right: var(--ck-spacing-standard);\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t}\n\t}\n}\n\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6203:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content .text-tiny{font-size:.7em}.ck-content .text-small{font-size:.85em}.ck-content .text-big{font-size:1.4em}.ck-content .text-huge{font-size:1.8em}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-font/theme/fontsize.css"],"names":[],"mappings":"AAUC,uBACC,cACD,CAEA,wBACC,eACD,CAEA,sBACC,eACD,CAEA,uBACC,eACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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/* The values should be synchronized with the \"FONT_SIZE_PRESET_UNITS\" object in the \"/src/fontsize/utils.js\" file. */\n\n/* Styles should be prefixed with the `.ck-content` class.\nSee https://github.com/ckeditor/ckeditor5/issues/6636 */\n.ck-content {\n\t& .text-tiny {\n\t\tfont-size: .7em;\n\t}\n\n\t& .text-small {\n\t\tfont-size: .85em;\n\t}\n\n\t& .text-big {\n\t\tfont-size: 1.4em;\n\t}\n\n\t& .text-huge {\n\t\tfont-size: 1.8em;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3230:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-heading_heading1{font-size:20px}.ck.ck-heading_heading2{font-size:17px}.ck.ck-heading_heading3{font-size:14px}.ck[class*=ck-heading_heading]{font-weight:700}.ck.ck-dropdown.ck-heading-dropdown .ck-dropdown__button .ck-button__label{width:8em}.ck.ck-dropdown.ck-heading-dropdown .ck-dropdown__panel .ck-list__item{min-width:18em}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-heading/theme/heading.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-heading/heading.css"],"names":[],"mappings":"AAKA,wBACC,cACD,CAEA,wBACC,cACD,CAEA,wBACC,cACD,CAEA,+BACC,eACD,CCZC,2EACC,SACD,CAEA,uEACC,cACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-heading_heading1 {\n\tfont-size: 20px;\n}\n\n.ck.ck-heading_heading2 {\n\tfont-size: 17px;\n}\n\n.ck.ck-heading_heading3 {\n\tfont-size: 14px;\n}\n\n.ck[class*=\"ck-heading_heading\"] {\n\tfont-weight: bold;\n}\n","/*\n * Copyright (c) 2003-2022, 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/* Resize dropdown's button label. */\n.ck.ck-dropdown.ck-heading-dropdown {\n\t& .ck-dropdown__button .ck-button__label {\n\t\twidth: 8em;\n\t}\n\n\t& .ck-dropdown__panel .ck-list__item {\n\t\tmin-width: 18em;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 713:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-highlight-marker-yellow:#fdfd77;--ck-highlight-marker-green:#62f962;--ck-highlight-marker-pink:#fc7899;--ck-highlight-marker-blue:#72ccfd;--ck-highlight-pen-red:#e71313;--ck-highlight-pen-green:#128a00}.ck-content .marker-yellow{background-color:var(--ck-highlight-marker-yellow)}.ck-content .marker-green{background-color:var(--ck-highlight-marker-green)}.ck-content .marker-pink{background-color:var(--ck-highlight-marker-pink)}.ck-content .marker-blue{background-color:var(--ck-highlight-marker-blue)}.ck-content .pen-red{background-color:transparent;color:var(--ck-highlight-pen-red)}.ck-content .pen-green{background-color:transparent;color:var(--ck-highlight-pen-green)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-highlight/theme/highlight.css"],"names":[],"mappings":"AAKA,MACC,oCAA+C,CAC/C,mCAA+C,CAC/C,kCAA8C,CAC9C,kCAA8C,CAC9C,8BAAwC,CACxC,gCACD,CAGC,2BACC,kDACD,CAFA,0BACC,iDACD,CAFA,yBACC,gDACD,CAFA,yBACC,gDACD,CAIA,qBAIC,4BAA6B,CAH7B,iCAID,CALA,uBAIC,4BAA6B,CAH7B,mCAID","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-highlight-marker-yellow: hsl(60, 97%, 73%);\n\t--ck-highlight-marker-green: hsl(120, 93%, 68%);\n\t--ck-highlight-marker-pink: hsl(345, 96%, 73%);\n\t--ck-highlight-marker-blue: hsl(201, 97%, 72%);\n\t--ck-highlight-pen-red: hsl(0, 85%, 49%);\n\t--ck-highlight-pen-green: hsl(112, 100%, 27%);\n}\n\n@define-mixin highlight-marker-color $color {\n\t.ck-content .marker-$color {\n\t\tbackground-color: var(--ck-highlight-marker-$color);\n\t}\n}\n\n@define-mixin highlight-pen-color $color {\n\t.ck-content .pen-$color {\n\t\tcolor: var(--ck-highlight-pen-$color);\n\n\t\t/* Override default yellow background of `<mark>` from user agent stylesheet */\n\t\tbackground-color: transparent;\n\t}\n}\n\n@mixin highlight-marker-color yellow;\n@mixin highlight-marker-color green;\n@mixin highlight-marker-color pink;\n@mixin highlight-marker-color blue;\n\n@mixin highlight-pen-color red;\n@mixin highlight-pen-color green;\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 2536:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-editor__editable .ck-horizontal-line{display:flow-root}.ck-content hr{background:#dedede;border:0;height:4px;margin:15px 0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css"],"names":[],"mappings":"AAMA,yCAEC,iBACD,CAEA,eAGC,kBAA2B,CAC3B,QAAS,CAFT,UAAW,CADX,aAID","sourcesContent":["/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n\n.ck-editor__editable .ck-horizontal-line {\n\t/* Necessary to render properly next to floated objects, e.g. side image case. */\n\tdisplay: flow-root;\n}\n\n.ck-content hr {\n\tmargin: 15px 0;\n\theight: 4px;\n\tbackground: hsl(0, 0%, 87%);\n\tborder: 0;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3403:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-widget.raw-html-embed{display:flow-root;font-style:normal;margin:.9em auto;min-width:15em;position:relative}.ck-widget.raw-html-embed:before{position:absolute;z-index:1}.ck-widget.raw-html-embed .raw-html-embed__buttons-wrapper{display:flex;flex-direction:column;position:absolute}.ck-widget.raw-html-embed .raw-html-embed__preview{display:flex;overflow:hidden;position:relative}.ck-widget.raw-html-embed .raw-html-embed__preview-content{border-collapse:separate;border-spacing:7px;display:table;margin:auto;position:relative;width:100%}.ck-widget.raw-html-embed .raw-html-embed__preview-placeholder{align-items:center;bottom:0;display:flex;justify-content:center;left:0;position:absolute;right:0;top:0}:root{--ck-html-embed-content-width:calc(100% - var(--ck-icon-size)*1.5);--ck-html-embed-source-height:10em;--ck-html-embed-unfocused-outline-width:1px;--ck-html-embed-content-min-height:calc(var(--ck-icon-size) + var(--ck-spacing-standard));--ck-html-embed-source-disabled-background:var(--ck-color-base-foreground);--ck-html-embed-source-disabled-color:#737373}.ck-widget.raw-html-embed{background-color:var(--ck-color-base-foreground);font-size:var(--ck-font-size-base)}.ck-widget.raw-html-embed:not(.ck-widget_selected):not(:hover){outline:var(--ck-html-embed-unfocused-outline-width) dashed var(--ck-color-widget-blurred-border)}.ck-widget.raw-html-embed[dir=ltr]{text-align:left}.ck-widget.raw-html-embed[dir=rtl]{text-align:right}.ck-widget.raw-html-embed:before{background:#999;border-radius:0 0 var(--ck-border-radius) var(--ck-border-radius);color:var(--ck-color-base-background);content:attr(data-html-embed-label);font-family:var(--ck-font-face);font-size:var(--ck-font-size-tiny);left:var(--ck-spacing-standard);padding:calc(var(--ck-spacing-tiny) + var(--ck-html-embed-unfocused-outline-width)) var(--ck-spacing-small) var(--ck-spacing-tiny);top:calc(var(--ck-html-embed-unfocused-outline-width)*-1);transition:background var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve)}.ck-widget.raw-html-embed[dir=rtl]:before{left:auto;right:var(--ck-spacing-standard)}.ck-widget.raw-html-embed[dir=ltr] .ck-widget__type-around .ck-widget__type-around__button.ck-widget__type-around__button_before{margin-left:50px}.ck.ck-editor__editable.ck-blurred .ck-widget.raw-html-embed.ck-widget_selected:before{padding:var(--ck-spacing-tiny) var(--ck-spacing-small);top:0}.ck.ck-editor__editable:not(.ck-blurred) .ck-widget.raw-html-embed.ck-widget_selected:before{background:var(--ck-color-focus-border);padding:var(--ck-spacing-tiny) var(--ck-spacing-small);top:0}.ck.ck-editor__editable .ck-widget.raw-html-embed:not(.ck-widget_selected):hover:before{padding:var(--ck-spacing-tiny) var(--ck-spacing-small);top:0}.ck-widget.raw-html-embed .raw-html-embed__content-wrapper{padding:var(--ck-spacing-standard)}.ck-widget.raw-html-embed .raw-html-embed__buttons-wrapper{right:var(--ck-spacing-standard);top:var(--ck-spacing-standard)}.ck-widget.raw-html-embed .raw-html-embed__buttons-wrapper .ck-button.raw-html-embed__save-button{color:var(--ck-color-button-save)}.ck-widget.raw-html-embed .raw-html-embed__buttons-wrapper .ck-button.raw-html-embed__cancel-button{color:var(--ck-color-button-cancel)}.ck-widget.raw-html-embed .raw-html-embed__buttons-wrapper .ck-button:not(:first-child){margin-top:var(--ck-spacing-small)}.ck-widget.raw-html-embed[dir=rtl] .raw-html-embed__buttons-wrapper{left:var(--ck-spacing-standard);right:auto}.ck-widget.raw-html-embed .raw-html-embed__source{box-sizing:border-box;direction:ltr;font-family:monospace;font-size:var(--ck-font-size-base);height:var(--ck-html-embed-source-height);min-width:0;padding:var(--ck-spacing-standard);resize:none;tab-size:4;text-align:left;white-space:pre-wrap;width:var(--ck-html-embed-content-width)}.ck-widget.raw-html-embed .raw-html-embed__source[disabled]{-webkit-text-fill-color:var(--ck-html-embed-source-disabled-color);background:var(--ck-html-embed-source-disabled-background);color:var(--ck-html-embed-source-disabled-color);opacity:1}.ck-widget.raw-html-embed .raw-html-embed__preview{min-height:var(--ck-html-embed-content-min-height);width:var(--ck-html-embed-content-width)}.ck-editor__editable:not(.ck-read-only) .ck-widget.raw-html-embed .raw-html-embed__preview{pointer-events:none}.ck-widget.raw-html-embed .raw-html-embed__preview-content{background-color:var(--ck-color-base-foreground);box-sizing:border-box}.ck-widget.raw-html-embed .raw-html-embed__preview-content>*{margin-left:auto;margin-right:auto}.ck-widget.raw-html-embed .raw-html-embed__preview-placeholder{color:var(--ck-html-embed-source-disabled-color)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-html-embed/theme/htmlembed.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-html-embed/htmlembed.css"],"names":[],"mappings":"AAMA,0BAMC,iBAAkB,CAOlB,iBAAkB,CATlB,gBAAkB,CAMlB,cAAe,CALf,iBAwDD,CA5CC,iCACC,iBAAkB,CAGlB,SACD,CAKA,2DAEC,YAAa,CACb,qBAAsB,CAFtB,iBAGD,CAEA,mDAGC,YAAa,CADb,eAAgB,CADhB,iBAGD,CAEA,2DAOC,wBAAyB,CACzB,kBAAmB,CAFnB,aAAc,CAHd,WAAY,CADZ,iBAAkB,CADlB,UAQD,CAEA,+DAQC,kBAAmB,CAHnB,QAAS,CAET,YAAa,CAEb,sBAAuB,CAPvB,MAAO,CADP,iBAAkB,CAGlB,OAAQ,CADR,KAOD,CC7DD,MACC,kEAAqE,CACrE,kCAAmC,CACnC,2CAA4C,CAC5C,yFAA0F,CAE1F,0EAA2E,CAC3E,6CACD,CAGA,0BAEC,gDAAiD,CADjD,kCA0ID,CAvIC,+DACC,iGACD,CAGA,mCACC,eACD,CAEA,mCACC,gBACD,CAIA,iCAIC,eAA4B,CAG5B,iEAAkE,CAClE,qCAAsC,CAPtC,mCAAoC,CASpC,+BAAgC,CADhC,kCAAmC,CANnC,+BAAgC,CAGhC,kIAAmI,CAJnI,yDAA4D,CAG5D,0GAMD,CAEA,0CACC,SAAU,CACV,gCACD,CAGA,iIACC,gBACD,CAxCD,uFA4CE,sDAAuD,CADvD,KAgGF,CA3IA,6FAkDE,uCAAwC,CADxC,sDAAuD,CADvD,KA2FF,CA3IA,wFAuDE,sDAAuD,CADvD,KAqFF,CA/EC,2DACC,kCACD,CAGA,2DAEC,gCAAiC,CADjC,8BAcD,CAXC,kGACC,iCACD,CAEA,oGACC,mCACD,CAEA,wFACC,kCACD,CAGD,oEACC,+BAAgC,CAChC,UACD,CAGA,kDACC,qBAAsB,CActB,aAAc,CAPd,qBAAsB,CAGtB,kCAAmC,CATnC,yCAA0C,CAG1C,WAAY,CACZ,kCAAmC,CAFnC,WAAY,CAKZ,UAAW,CAKX,eAAgB,CAJhB,oBAAqB,CAPrB,wCAsBD,CARC,4DAKC,kEAAmE,CAJnE,0DAA2D,CAC3D,gDAAiD,CAIjD,SACD,CAID,mDACC,kDAAmD,CACnD,wCAMD,CARA,2FAME,mBAEF,CAEA,2DAEC,gDAAiD,CADjD,qBAOD,CAJC,6DACC,gBAAiB,CACjB,iBACD,CAGD,+DACC,gDACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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/* The feature container. */\n.ck-widget.raw-html-embed {\n\t/* Give the embed some air. */\n\t/* The first value should be equal to --ck-spacing-large variable if used in the editor context\n\tto avoid the content jumping (See https://github.com/ckeditor/ckeditor5/issues/9825). */\n\tmargin: 0.9em auto;\n\tposition: relative;\n\tdisplay: flow-root;\n\n\t/* Give the html embed some minimal width in the content to prevent them\n\tfrom being \"squashed\" in tight spaces, e.g. in table cells (https://github.com/ckeditor/ckeditor5/issues/8331) */\n\tmin-width: 15em;\n\n\t/* Don't inherit the style, e.g. when in a block quote. */\n\tfont-style: normal;\n\n\t/* ----- Emebed label in the upper left corner ----------------------------------------------- */\n\n\t&::before {\n\t\tposition: absolute;\n\n\t\t/* Make sure the content does not cover the label. */\n\t\tz-index: 1;\n\t}\n\n\t/* ----- Emebed internals --------------------------------------------------------------------- */\n\n\t/* The switch mode button wrapper. */\n\t& .raw-html-embed__buttons-wrapper {\n\t\tposition: absolute;\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t}\n\n\t& .raw-html-embed__preview {\n\t\tposition: relative;\n\t\toverflow: hidden;\n\t\tdisplay: flex;\n\t}\n\n\t& .raw-html-embed__preview-content {\n\t\twidth: 100%;\n\t\tposition: relative;\n\t\tmargin: auto;\n\n\t\t/* Gives spacing to the small renderable elements, so they always cover the placeholder. */\n\t\tdisplay: table;\n\t\tborder-collapse: separate;\n\t\tborder-spacing: 7px;\n\t}\n\n\t& .raw-html-embed__preview-placeholder {\n\t\tposition: absolute;\n\t\tleft: 0;\n\t\ttop: 0;\n\t\tright: 0;\n\t\tbottom: 0;\n\n\t\tdisplay: flex;\n\t\talign-items: center;\n\t\tjustify-content: center;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-html-embed-content-width: calc(100% - 1.5 * var(--ck-icon-size));\n\t--ck-html-embed-source-height: 10em;\n\t--ck-html-embed-unfocused-outline-width: 1px;\n\t--ck-html-embed-content-min-height: calc(var(--ck-icon-size) + var(--ck-spacing-standard));\n\n\t--ck-html-embed-source-disabled-background: var(--ck-color-base-foreground);\n\t--ck-html-embed-source-disabled-color: hsl(0deg 0% 45%);\n}\n\n/* The feature container. */\n.ck-widget.raw-html-embed {\n\tfont-size: var(--ck-font-size-base);\n\tbackground-color: var(--ck-color-base-foreground);\n\n\t&:not(.ck-widget_selected):not(:hover) {\n\t\toutline: var(--ck-html-embed-unfocused-outline-width) dashed var(--ck-color-widget-blurred-border);\n\t}\n\n\t/* HTML embed widget itself should respect UI language direction */\n\t&[dir=\"ltr\"] {\n\t\ttext-align: left;\n\t}\n\n\t&[dir=\"rtl\"] {\n\t\ttext-align: right;\n\t}\n\n\t/* ----- Embed label in the upper left corner ----------------------------------------------- */\n\n\t&::before {\n\t\tcontent: attr(data-html-embed-label);\n\t\ttop: calc(-1 * var(--ck-html-embed-unfocused-outline-width));\n\t\tleft: var(--ck-spacing-standard);\n\t\tbackground: hsl(0deg 0% 60%);\n\t\ttransition: background var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve);\n\t\tpadding: calc(var(--ck-spacing-tiny) + var(--ck-html-embed-unfocused-outline-width)) var(--ck-spacing-small) var(--ck-spacing-tiny);\n\t\tborder-radius: 0 0 var(--ck-border-radius) var(--ck-border-radius);\n\t\tcolor: var(--ck-color-base-background);\n\t\tfont-size: var(--ck-font-size-tiny);\n\t\tfont-family: var(--ck-font-face);\n\t}\n\n\t&[dir=\"rtl\"]::before {\n\t\tleft: auto;\n\t\tright: var(--ck-spacing-standard);\n\t}\n\n\t/* Make space for label but it only collides in LTR languages */\n\t&[dir=\"ltr\"] .ck-widget__type-around .ck-widget__type-around__button.ck-widget__type-around__button_before {\n\t\tmargin-left: 50px;\n\t}\n\n\t@nest .ck.ck-editor__editable.ck-blurred &.ck-widget_selected::before {\n\t\ttop: 0px;\n\t\tpadding: var(--ck-spacing-tiny) var(--ck-spacing-small);\n\t}\n\n\t@nest .ck.ck-editor__editable:not(.ck-blurred) &.ck-widget_selected::before {\n\t\ttop: 0;\n\t\tpadding: var(--ck-spacing-tiny) var(--ck-spacing-small);\n\t\tbackground: var(--ck-color-focus-border);\n\t}\n\n\t@nest .ck.ck-editor__editable &:not(.ck-widget_selected):hover::before {\n\t\ttop: 0px;\n\t\tpadding: var(--ck-spacing-tiny) var(--ck-spacing-small);\n\t}\n\n\t/* ----- Emebed internals --------------------------------------------------------------------- */\n\n\t& .raw-html-embed__content-wrapper {\n\t\tpadding: var(--ck-spacing-standard);\n\t}\n\n\t/* The switch mode button wrapper. */\n\t& .raw-html-embed__buttons-wrapper {\n\t\ttop: var(--ck-spacing-standard);\n\t\tright: var(--ck-spacing-standard);\n\n\t\t& .ck-button.raw-html-embed__save-button {\n\t\t\tcolor: var(--ck-color-button-save);\n\t\t}\n\n\t\t& .ck-button.raw-html-embed__cancel-button {\n\t\t\tcolor: var(--ck-color-button-cancel);\n\t\t}\n\n\t\t& .ck-button:not(:first-child) {\n\t\t\tmargin-top: var(--ck-spacing-small);\n\t\t}\n\t}\n\n\t&[dir=\"rtl\"] .raw-html-embed__buttons-wrapper {\n\t\tleft: var(--ck-spacing-standard);\n\t\tright: auto;\n\t}\n\n\t/* The edit source element. */\n\t& .raw-html-embed__source {\n\t\tbox-sizing: border-box;\n\t\theight: var(--ck-html-embed-source-height);\n\t\twidth: var(--ck-html-embed-content-width);\n\t\tresize: none;\n\t\tmin-width: 0;\n\t\tpadding: var(--ck-spacing-standard);\n\n\t\tfont-family: monospace;\n\t\ttab-size: 4;\n\t\twhite-space: pre-wrap;\n\t\tfont-size: var(--ck-font-size-base); /* Safari needs this. */\n\n\t\t/* HTML code is direction–agnostic. */\n\t\ttext-align: left;\n\t\tdirection: ltr;\n\n\t\t&[disabled] {\n\t\t\tbackground: var(--ck-html-embed-source-disabled-background);\n\t\t\tcolor: var(--ck-html-embed-source-disabled-color);\n\n\t\t\t/* Safari needs this for the proper text color in disabled input (https://github.com/ckeditor/ckeditor5/issues/8320). */\n\t\t\t-webkit-text-fill-color: var(--ck-html-embed-source-disabled-color);\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t/* The preview data container. */\n\t& .raw-html-embed__preview {\n\t\tmin-height: var(--ck-html-embed-content-min-height);\n\t\twidth: var(--ck-html-embed-content-width);\n\n\t\t/* Disable all mouse interaction as long as the editor is not read–only. */\n\t\t@nest .ck-editor__editable:not(.ck-read-only) & {\n\t\t\tpointer-events: none;\n\t\t}\n\t}\n\n\t& .raw-html-embed__preview-content {\n\t\tbox-sizing: border-box;\n\t\tbackground-color: var(--ck-color-base-foreground);\n\n\t\t& > * {\n\t\t\tmargin-left: auto;\n\t\t\tmargin-right: auto;\n\t\t}\n\t}\n\n\t& .raw-html-embed__preview-placeholder {\n\t\tcolor: var(--ck-html-embed-source-disabled-color)\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8468:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-html-object-embed-unfocused-outline-width:1px}.ck-widget.html-object-embed{background-color:var(--ck-color-base-foreground);font-size:var(--ck-font-size-base);min-width:calc(76px + var(--ck-spacing-standard));padding:var(--ck-spacing-small);padding-top:calc(var(--ck-font-size-tiny) + var(--ck-spacing-large))}.ck-widget.html-object-embed:not(.ck-widget_selected):not(:hover){outline:var(--ck-html-object-embed-unfocused-outline-width) dashed var(--ck-color-widget-blurred-border)}.ck-widget.html-object-embed:before{background:#999;border-radius:0 0 var(--ck-border-radius) var(--ck-border-radius);color:var(--ck-color-base-background);content:attr(data-html-object-embed-label);font-family:var(--ck-font-face);font-size:var(--ck-font-size-tiny);font-style:normal;font-weight:400;left:var(--ck-spacing-standard);padding:calc(var(--ck-spacing-tiny) + var(--ck-html-object-embed-unfocused-outline-width)) var(--ck-spacing-small) var(--ck-spacing-tiny);position:absolute;top:0;transition:background var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve)}.ck-widget.html-object-embed .ck-widget__type-around .ck-widget__type-around__button.ck-widget__type-around__button_before{margin-left:50px}.ck-widget.html-object-embed .html-object-embed__content{pointer-events:none}div.ck-widget.html-object-embed{margin:1em auto}span.ck-widget.html-object-embed{display:inline-block}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-html-support/theme/datafilter.css"],"names":[],"mappings":"AAKA,MACC,kDACD,CAEA,6BAEC,gDAAiD,CADjD,kCAAmC,CAKnC,iDAAkD,CAHlD,+BAAgC,CAEhC,oEAgCD,CA7BC,kEACC,wGACD,CAEA,oCAOC,eAA4B,CAG5B,iEAAkE,CAClE,qCAAsC,CAPtC,0CAA2C,CAS3C,+BAAgC,CADhC,kCAAmC,CAVnC,iBAAkB,CADlB,eAAmB,CAKnB,+BAAgC,CAGhC,yIAA0I,CAN1I,iBAAkB,CAElB,KAAM,CAGN,0GAMD,CAGA,2HACC,gBACD,CAEA,yDAEC,mBACD,CAGD,gCACC,eACD,CAEA,iCACC,oBACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-html-object-embed-unfocused-outline-width: 1px;\n}\n\n.ck-widget.html-object-embed {\n\tfont-size: var(--ck-font-size-base);\n\tbackground-color: var(--ck-color-base-foreground);\n\tpadding: var(--ck-spacing-small);\n\t/* Leave space for label */\n\tpadding-top: calc(var(--ck-font-size-tiny) + var(--ck-spacing-large));\n\tmin-width: calc(76px + var(--ck-spacing-standard));\n\n\t&:not(.ck-widget_selected):not(:hover) {\n\t\toutline: var(--ck-html-object-embed-unfocused-outline-width) dashed var(--ck-color-widget-blurred-border);\n\t}\n\n\t&::before {\n\t\tfont-weight: normal;\n\t\tfont-style: normal;\n\t\tposition: absolute;\n\t\tcontent: attr(data-html-object-embed-label);\n\t\ttop: 0;\n\t\tleft: var(--ck-spacing-standard);\n\t\tbackground: hsl(0deg 0% 60%);\n\t\ttransition: background var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve);\n\t\tpadding: calc(var(--ck-spacing-tiny) + var(--ck-html-object-embed-unfocused-outline-width)) var(--ck-spacing-small) var(--ck-spacing-tiny);\n\t\tborder-radius: 0 0 var(--ck-border-radius) var(--ck-border-radius);\n\t\tcolor: var(--ck-color-base-background);\n\t\tfont-size: var(--ck-font-size-tiny);\n\t\tfont-family: var(--ck-font-face);\n\t}\n\n\t/* Make space for label. */\n\t& .ck-widget__type-around .ck-widget__type-around__button.ck-widget__type-around__button_before {\n\t\tmargin-left: 50px;\n\t}\n\n\t& .html-object-embed__content {\n\t\t/* Disable user interaction with embed content */\n\t\tpointer-events: none;\n\t}\n}\n\ndiv.ck-widget.html-object-embed {\n\tmargin: 1em auto;\n}\n\nspan.ck-widget.html-object-embed {\n\tdisplay: inline-block;\n}\n\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9048:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content .image{clear:both;display:table;margin:.9em auto;min-width:50px;text-align:center}.ck-content .image img{display:block;margin:0 auto;max-width:100%;min-width:100%}.ck-content .image-inline{align-items:flex-start;display:inline-flex;max-width:100%}.ck-content .image-inline picture{display:flex}.ck-content .image-inline img,.ck-content .image-inline picture{flex-grow:1;flex-shrink:1;max-width:100%}.ck.ck-editor__editable .image>figcaption.ck-placeholder:before{overflow:hidden;padding-left:inherit;padding-right:inherit;text-overflow:ellipsis;white-space:nowrap}.ck.ck-editor__editable .image-inline.ck-widget_selected,.ck.ck-editor__editable .image.ck-widget_selected{z-index:1}.ck.ck-editor__editable .image-inline.ck-widget_selected ::selection{display:none}.ck.ck-editor__editable td .image-inline img,.ck.ck-editor__editable th .image-inline img{max-width:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/image.css"],"names":[],"mappings":"AAMC,mBAEC,UAAW,CADX,aAAc,CAOd,gBAAkB,CAGlB,cAAe,CARf,iBAuBD,CAbC,uBAEC,aAAc,CAGd,aAAc,CAGd,cAAe,CAGf,cACD,CAGD,0BAYC,sBAAuB,CANvB,mBAAoB,CAGpB,cAoBD,CAdC,kCACC,YACD,CAGA,gEAGC,WAAY,CACZ,aAAc,CAGd,cACD,CAUD,gEASC,eAAgB,CARhB,oBAAqB,CACrB,qBAAsB,CAQtB,sBAAuB,CAFvB,kBAGD,CAWA,2GACC,SAUD,CAHC,qEACC,YACD,CAOA,0FACC,cACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content {\n\t& .image {\n\t\tdisplay: table;\n\t\tclear: both;\n\t\ttext-align: center;\n\n\t\t/* Make sure there is some space between the content and the image. Center image by default. */\n\t\t/* The first value should be equal to --ck-spacing-large variable if used in the editor context\n\t \tto avoid the content jumping (See https://github.com/ckeditor/ckeditor5/issues/9825). */\n\t\tmargin: 0.9em auto;\n\n\t\t/* Make sure the caption will be displayed properly (See: https://github.com/ckeditor/ckeditor5/issues/1870). */\n\t\tmin-width: 50px;\n\n\t\t& img {\n\t\t\t/* Prevent unnecessary margins caused by line-height (see #44). */\n\t\t\tdisplay: block;\n\n\t\t\t/* Center the image if its width is smaller than the content's width. */\n\t\t\tmargin: 0 auto;\n\n\t\t\t/* Make sure the image never exceeds the size of the parent container (ckeditor/ckeditor5-ui#67). */\n\t\t\tmax-width: 100%;\n\n\t\t\t/* Make sure the image is never smaller than the parent container (See: https://github.com/ckeditor/ckeditor5/issues/9300). */\n\t\t\tmin-width: 100%\n\t\t}\n\t}\n\n\t& .image-inline {\n\t\t/*\n\t\t * Normally, the .image-inline would have \"display: inline-block\" and \"img { width: 100% }\" (to follow the wrapper while resizing).\n\t\t * Unfortunately, together with \"srcset\", it gets automatically stretched up to the width of the editing root.\n\t\t * This strange behavior does not happen with inline-flex.\n\t\t */\n\t\tdisplay: inline-flex;\n\n\t\t/* While being resized, don't allow the image to exceed the width of the editing root. */\n\t\tmax-width: 100%;\n\n\t\t/* This is required by Safari to resize images in a sensible way. Without this, the browser breaks the ratio. */\n\t\talign-items: flex-start;\n\n\t\t/* When the picture is present it must act as a flex container to let the img resize properly */\n\t\t& picture {\n\t\t\tdisplay: flex;\n\t\t}\n\n\t\t/* When the picture is present, it must act like a resizable img. */\n\t\t& picture,\n\t\t& img {\n\t\t\t/* This is necessary for the img to span the entire .image-inline wrapper and to resize properly. */\n\t\t\tflex-grow: 1;\n\t\t\tflex-shrink: 1;\n\n\t\t\t/* Prevents overflowing the editing root boundaries when an inline image is very wide. */\n\t\t\tmax-width: 100%;\n\t\t}\n\t}\n}\n\n.ck.ck-editor__editable {\n\t/*\n\t * Inhertit the content styles padding of the <figcaption> in case the integration overrides `text-align: center`\n\t * of `.image` (e.g. to the left/right). This ensures the placeholder stays at the padding just like the native\n\t * caret does, and not at the edge of <figcaption>.\n\t */\n\t& .image > figcaption.ck-placeholder::before {\n\t\tpadding-left: inherit;\n\t\tpadding-right: inherit;\n\n\t\t/*\n\t\t * Make sure the image caption placeholder doesn't overflow the placeholder area.\n\t\t * See https://github.com/ckeditor/ckeditor5/issues/9162.\n\t\t */\n\t\twhite-space: nowrap;\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t}\n\n\n\t/*\n\t * Make sure the selected inline image always stays on top of its siblings.\n\t * See https://github.com/ckeditor/ckeditor5/issues/9108.\n\t */\n\t& .image.ck-widget_selected {\n\t\tz-index: 1;\n\t}\n\n\t& .image-inline.ck-widget_selected {\n\t\tz-index: 1;\n\n\t\t/*\n\t\t * Make sure the native browser selection style is not displayed.\n\t\t * Inline image widgets have their own styles for the selected state and\n\t\t * leaving this up to the browser is asking for a visual collision.\n\t\t */\n\t\t& ::selection {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t/* The inline image nested in the table should have its original size if not resized.\n\tSee https://github.com/ckeditor/ckeditor5/issues/9117. */\n\t& td,\n\t& th {\n\t\t& .image-inline img {\n\t\t\tmax-width: none;\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8662:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-image-caption-background:#f7f7f7;--ck-color-image-caption-text:#333;--ck-color-image-caption-highligted-background:#fd0}.ck-content .image>figcaption{background-color:var(--ck-color-image-caption-background);caption-side:bottom;color:var(--ck-color-image-caption-text);display:table-caption;font-size:.75em;outline-offset:-1px;padding:.6em;word-break:break-word}.ck.ck-editor__editable .image>figcaption.image__caption_highlighted{animation:ck-image-caption-highlight .6s ease-out}@keyframes ck-image-caption-highlight{0%{background-color:var(--ck-color-image-caption-highligted-background)}to{background-color:var(--ck-color-image-caption-background)}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imagecaption.css"],"names":[],"mappings":"AAKA,MACC,2CAAoD,CACpD,kCAA8C,CAC9C,mDACD,CAGA,8BAKC,yDAA0D,CAH1D,mBAAoB,CAEpB,wCAAyC,CAHzC,qBAAsB,CAMtB,eAAgB,CAChB,mBAAoB,CAFpB,YAAa,CAHb,qBAMD,CAGA,qEACC,iDACD,CAEA,sCACC,GACC,oEACD,CAEA,GACC,yDACD,CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-image-caption-background: hsl(0, 0%, 97%);\n\t--ck-color-image-caption-text: hsl(0, 0%, 20%);\n\t--ck-color-image-caption-highligted-background: hsl(52deg 100% 50%);\n}\n\n/* Content styles */\n.ck-content .image > figcaption {\n\tdisplay: table-caption;\n\tcaption-side: bottom;\n\tword-break: break-word;\n\tcolor: var(--ck-color-image-caption-text);\n\tbackground-color: var(--ck-color-image-caption-background);\n\tpadding: .6em;\n\tfont-size: .75em;\n\toutline-offset: -1px;\n}\n\n/* Editing styles */\n.ck.ck-editor__editable .image > figcaption.image__caption_highlighted {\n\tanimation: ck-image-caption-highlight .6s ease-out;\n}\n\n@keyframes ck-image-caption-highlight {\n\t0% {\n\t\tbackground-color: var(--ck-color-image-caption-highligted-background);\n\t}\n\n\t100% {\n\t\tbackground-color: var(--ck-color-image-caption-background);\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9292:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-image-insert__panel{padding:var(--ck-spacing-large)}.ck.ck-image-insert__ck-finder-button{border:1px solid #ccc;border-radius:var(--ck-border-radius);display:block;margin:var(--ck-spacing-standard) auto;width:100%}.ck.ck-splitbutton>.ck-file-dialog-button.ck-button{border:none;margin:0;padding:0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imageinsert.css"],"names":[],"mappings":"AAKA,2BACC,+BACD,CAEA,sCAIC,qBAAiC,CACjC,qCAAsC,CAJtC,aAAc,CAEd,sCAAuC,CADvC,UAID,CAGA,oDAGC,WAAY,CADZ,QAAS,CADT,SAGD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-image-insert__panel {\n\tpadding: var(--ck-spacing-large);\n}\n\n.ck.ck-image-insert__ck-finder-button {\n\tdisplay: block;\n\twidth: 100%;\n\tmargin: var(--ck-spacing-standard) auto;\n\tborder: 1px solid hsl(0, 0%, 80%);\n\tborder-radius: var(--ck-border-radius);\n}\n\n/* https://github.com/ckeditor/ckeditor5/issues/7986 */\n.ck.ck-splitbutton > .ck-file-dialog-button.ck-button {\n\tpadding: 0;\n\tmargin: 0;\n\tborder: none;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5150:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-image-insert-form:focus{outline:none}.ck.ck-form__row{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:space-between}.ck.ck-form__row>:not(.ck-label){flex-grow:1}.ck.ck-form__row.ck-image-insert-form__action-row{margin-top:var(--ck-spacing-standard)}.ck.ck-form__row.ck-image-insert-form__action-row .ck-button-cancel,.ck.ck-form__row.ck-image-insert-form__action-row .ck-button-save{justify-content:center}.ck.ck-form__row.ck-image-insert-form__action-row .ck-button .ck-button__label{color:var(--ck-color-text)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imageinsertformrowview.css"],"names":[],"mappings":"AAMC,+BAEC,YACD,CAGD,iBACC,YAAa,CACb,kBAAmB,CACnB,gBAAiB,CACjB,6BAmBD,CAhBC,iCACC,WACD,CAEA,kDACC,qCAUD,CARC,sIAEC,sBACD,CAEA,+EACC,0BACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-image-insert-form {\n\t&:focus {\n\t\t/* See: https://github.com/ckeditor/ckeditor5/issues/4773 */\n\t\toutline: none;\n\t}\n}\n\n.ck.ck-form__row {\n\tdisplay: flex;\n\tflex-direction: row;\n\tflex-wrap: nowrap;\n\tjustify-content: space-between;\n\n\t/* Ignore labels that work as fieldset legends */\n\t& > *:not(.ck-label) {\n\t\tflex-grow: 1;\n\t}\n\n\t&.ck-image-insert-form__action-row {\n\t\tmargin-top: var(--ck-spacing-standard);\n\n\t\t& .ck-button-save,\n\t\t& .ck-button-cancel {\n\t\t\tjustify-content: center;\n\t\t}\n\n\t\t& .ck-button .ck-button__label {\n\t\t\tcolor: var(--ck-color-text);\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 1043:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content .image.image_resized{box-sizing:border-box;display:block;max-width:100%}.ck-content .image.image_resized img{width:100%}.ck-content .image.image_resized>figcaption{display:block}.ck.ck-editor__editable td .image-inline.image_resized img,.ck.ck-editor__editable th .image-inline.image_resized img{max-width:100%}[dir=ltr] .ck.ck-button.ck-button_with-text.ck-resize-image-button .ck-button__icon{margin-right:var(--ck-spacing-standard)}[dir=rtl] .ck.ck-button.ck-button_with-text.ck-resize-image-button .ck-button__icon{margin-left:var(--ck-spacing-standard)}.ck.ck-dropdown .ck-button.ck-resize-image-button .ck-button__label{width:4em}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imageresize.css"],"names":[],"mappings":"AAKA,iCAQC,qBAAsB,CADtB,aAAc,CANd,cAkBD,CATC,qCAEC,UACD,CAEA,4CAEC,aACD,CAQC,sHACC,cACD,CAIF,oFACC,uCACD,CAEA,oFACC,sCACD,CAEA,oEACC,SACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content .image.image_resized {\n\tmax-width: 100%;\n\t/*\n\tThe `<figure>` element for resized images must not use `display:table` as browsers do not support `max-width` for it well.\n\tSee https://stackoverflow.com/questions/4019604/chrome-safari-ignoring-max-width-in-table/14420691#14420691 for more.\n\tFortunately, since we control the width, there is no risk that the image will look bad.\n\t*/\n\tdisplay: block;\n\tbox-sizing: border-box;\n\n\t& img {\n\t\t/* For resized images it is the `<figure>` element that determines the image width. */\n\t\twidth: 100%;\n\t}\n\n\t& > figcaption {\n\t\t/* The `<figure>` element uses `display:block`, so `<figcaption>` also has to. */\n\t\tdisplay: block;\n\t}\n}\n\n.ck.ck-editor__editable {\n\t/* The resized inline image nested in the table should respect its parent size.\n\tSee https://github.com/ckeditor/ckeditor5/issues/9117. */\n\t& td,\n\t& th {\n\t\t& .image-inline.image_resized img {\n\t\t\tmax-width: 100%;\n\t\t}\n\t}\n}\n\n[dir=\"ltr\"] .ck.ck-button.ck-button_with-text.ck-resize-image-button .ck-button__icon {\n\tmargin-right: var(--ck-spacing-standard);\n}\n\n[dir=\"rtl\"] .ck.ck-button.ck-button_with-text.ck-resize-image-button .ck-button__icon {\n\tmargin-left: var(--ck-spacing-standard);\n}\n\n.ck.ck-dropdown .ck-button.ck-resize-image-button .ck-button__label {\n\twidth: 4em;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4622:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-image-style-spacing:1.5em;--ck-inline-image-style-spacing:calc(var(--ck-image-style-spacing)/2)}.ck-content .image-style-block-align-left,.ck-content .image-style-block-align-right{max-width:calc(100% - var(--ck-image-style-spacing))}.ck-content .image-style-align-left,.ck-content .image-style-align-right{clear:none}.ck-content .image-style-side{float:right;margin-left:var(--ck-image-style-spacing);max-width:50%}.ck-content .image-style-align-left{float:left;margin-right:var(--ck-image-style-spacing)}.ck-content .image-style-align-center{margin-left:auto;margin-right:auto}.ck-content .image-style-align-right{float:right;margin-left:var(--ck-image-style-spacing)}.ck-content .image-style-block-align-right{margin-left:auto;margin-right:0}.ck-content .image-style-block-align-left{margin-left:0;margin-right:auto}.ck-content p+.image-style-align-left,.ck-content p+.image-style-align-right,.ck-content p+.image-style-side{margin-top:0}.ck-content .image-inline.image-style-align-left,.ck-content .image-inline.image-style-align-right{margin-bottom:var(--ck-inline-image-style-spacing);margin-top:var(--ck-inline-image-style-spacing)}.ck-content .image-inline.image-style-align-left{margin-right:var(--ck-inline-image-style-spacing)}.ck-content .image-inline.image-style-align-right{margin-left:var(--ck-inline-image-style-spacing)}.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open>.ck-splitbutton__action:not(.ck-disabled),.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open>.ck-splitbutton__arrow:not(.ck-disabled),.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open>.ck-splitbutton__arrow:not(.ck-disabled):not(:hover),.ck.ck-splitbutton.ck-splitbutton_flatten:hover>.ck-splitbutton__action:not(.ck-disabled),.ck.ck-splitbutton.ck-splitbutton_flatten:hover>.ck-splitbutton__arrow:not(.ck-disabled),.ck.ck-splitbutton.ck-splitbutton_flatten:hover>.ck-splitbutton__arrow:not(.ck-disabled):not(:hover){background-color:var(--ck-color-button-on-background)}.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open>.ck-splitbutton__action:not(.ck-disabled):after,.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open>.ck-splitbutton__arrow:not(.ck-disabled):after,.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open>.ck-splitbutton__arrow:not(.ck-disabled):not(:hover):after,.ck.ck-splitbutton.ck-splitbutton_flatten:hover>.ck-splitbutton__action:not(.ck-disabled):after,.ck.ck-splitbutton.ck-splitbutton_flatten:hover>.ck-splitbutton__arrow:not(.ck-disabled):after,.ck.ck-splitbutton.ck-splitbutton_flatten:hover>.ck-splitbutton__arrow:not(.ck-disabled):not(:hover):after{display:none}.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open:hover>.ck-splitbutton__action:not(.ck-disabled),.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open:hover>.ck-splitbutton__arrow:not(.ck-disabled),.ck.ck-splitbutton.ck-splitbutton_flatten.ck-splitbutton_open:hover>.ck-splitbutton__arrow:not(.ck-disabled):not(:hover){background-color:var(--ck-color-button-on-hover-background)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imagestyle.css"],"names":[],"mappings":"AAKA,MACC,8BAA+B,CAC/B,qEACD,CAMC,qFAEC,oDACD,CAIA,yEAEC,UACD,CAEA,8BACC,WAAY,CACZ,yCAA0C,CAC1C,aACD,CAEA,oCACC,UAAW,CACX,0CACD,CAEA,sCACC,gBAAiB,CACjB,iBACD,CAEA,qCACC,WAAY,CACZ,yCACD,CAEA,2CAEC,gBAAiB,CADjB,cAED,CAEA,0CACC,aAAc,CACd,iBACD,CAGA,6GAGC,YACD,CAGC,mGAGC,kDAAmD,CADnD,+CAED,CAEA,iDACC,iDACD,CAEA,kDACC,gDACD,CAUC,0lBAGC,qDAKD,CAHC,8nBACC,YACD,CAKD,oVAGC,2DACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-image-style-spacing: 1.5em;\n\t--ck-inline-image-style-spacing: calc(var(--ck-image-style-spacing) / 2);\n}\n\n.ck-content {\n\t/* Provides a minimal side margin for the left and right aligned images, so that the user has a visual feedback\n\tconfirming successful application of the style if image width exceeds the editor's size.\n\tSee https://github.com/ckeditor/ckeditor5/issues/9342 */\n\t& .image-style-block-align-left,\n\t& .image-style-block-align-right {\n\t\tmax-width: calc(100% - var(--ck-image-style-spacing));\n\t}\n\n\t/* Allows displaying multiple floating images in the same line.\n\tSee https://github.com/ckeditor/ckeditor5/issues/9183#issuecomment-804988132 */\n\t& .image-style-align-left,\n\t& .image-style-align-right {\n\t\tclear: none;\n\t}\n\n\t& .image-style-side {\n\t\tfloat: right;\n\t\tmargin-left: var(--ck-image-style-spacing);\n\t\tmax-width: 50%;\n\t}\n\n\t& .image-style-align-left {\n\t\tfloat: left;\n\t\tmargin-right: var(--ck-image-style-spacing);\n\t}\n\n\t& .image-style-align-center {\n\t\tmargin-left: auto;\n\t\tmargin-right: auto;\n\t}\n\n\t& .image-style-align-right {\n\t\tfloat: right;\n\t\tmargin-left: var(--ck-image-style-spacing);\n\t}\n\n\t& .image-style-block-align-right {\n\t\tmargin-right: 0;\n\t\tmargin-left: auto;\n\t}\n\n\t& .image-style-block-align-left {\n\t\tmargin-left: 0;\n\t\tmargin-right: auto;\n\t}\n\n\t/* Simulates margin collapsing with the preceding paragraph, which does not work for the floating elements. */\n\t& p + .image-style-align-left,\n\t& p + .image-style-align-right,\n\t& p + .image-style-side {\n\t\tmargin-top: 0;\n\t}\n\n\t& .image-inline {\n\t\t&.image-style-align-left,\n\t\t&.image-style-align-right {\n\t\t\tmargin-top: var(--ck-inline-image-style-spacing);\n\t\t\tmargin-bottom: var(--ck-inline-image-style-spacing);\n\t\t}\n\n\t\t&.image-style-align-left {\n\t\t\tmargin-right: var(--ck-inline-image-style-spacing);\n\t\t}\n\n\t\t&.image-style-align-right {\n\t\t\tmargin-left: var(--ck-inline-image-style-spacing);\n\t\t}\n\t}\n}\n\n.ck.ck-splitbutton {\n\t/* The button should display as a regular drop-down if the action button\n\tis forced to fire the same action as the arrow button. */\n\t&.ck-splitbutton_flatten {\n\t\t&:hover,\n\t\t&.ck-splitbutton_open {\n\t\t\t& > .ck-splitbutton__action:not(.ck-disabled),\n\t\t\t& > .ck-splitbutton__arrow:not(.ck-disabled),\n\t\t\t& > .ck-splitbutton__arrow:not(.ck-disabled):not(:hover) {\n\t\t\t\tbackground-color: var(--ck-color-button-on-background);\n\n\t\t\t\t&::after {\n\t\t\t\t\tdisplay: none;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t&.ck-splitbutton_open:hover {\n\t\t\t& > .ck-splitbutton__action:not(.ck-disabled),\n\t\t\t& > .ck-splitbutton__arrow:not(.ck-disabled),\n\t\t\t& > .ck-splitbutton__arrow:not(.ck-disabled):not(:hover) {\n\t\t\t\tbackground-color: var(--ck-color-button-on-hover-background);\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9899:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-image-upload-complete-icon{border-radius:50%;display:block;position:absolute;right:min(var(--ck-spacing-medium),6%);top:min(var(--ck-spacing-medium),6%);z-index:1}.ck-image-upload-complete-icon:after{content:\"\";position:absolute}:root{--ck-color-image-upload-icon:#fff;--ck-color-image-upload-icon-background:#008a00;--ck-image-upload-icon-size:20;--ck-image-upload-icon-width:2px;--ck-image-upload-icon-is-visible:clamp(0px,100% - 50px,1px)}.ck-image-upload-complete-icon{animation-delay:0ms,3s;animation-duration:.5s,.5s;animation-fill-mode:forwards,forwards;animation-name:ck-upload-complete-icon-show,ck-upload-complete-icon-hide;background:var(--ck-color-image-upload-icon-background);font-size:calc(1px*var(--ck-image-upload-icon-size));height:calc(var(--ck-image-upload-icon-is-visible)*var(--ck-image-upload-icon-size));opacity:0;overflow:hidden;width:calc(var(--ck-image-upload-icon-is-visible)*var(--ck-image-upload-icon-size))}.ck-image-upload-complete-icon:after{animation-delay:.5s;animation-duration:.5s;animation-fill-mode:forwards;animation-name:ck-upload-complete-icon-check;border-right:var(--ck-image-upload-icon-width) solid var(--ck-color-image-upload-icon);border-top:var(--ck-image-upload-icon-width) solid var(--ck-color-image-upload-icon);box-sizing:border-box;height:0;left:25%;opacity:0;top:50%;transform:scaleX(-1) rotate(135deg);transform-origin:left top;width:0}@keyframes ck-upload-complete-icon-show{0%{opacity:0}to{opacity:1}}@keyframes ck-upload-complete-icon-hide{0%{opacity:1}to{opacity:0}}@keyframes ck-upload-complete-icon-check{0%{height:0;opacity:1;width:0}33%{height:0;width:.3em}to{height:.45em;opacity:1;width:.3em}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadicon.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-image/imageuploadicon.css"],"names":[],"mappings":"AAKA,+BAUC,iBAAkB,CATlB,aAAc,CACd,iBAAkB,CAOlB,sCAAwC,CADxC,oCAAsC,CAGtC,SAMD,CAJC,qCACC,UAAW,CACX,iBACD,CChBD,MACC,iCAA8C,CAC9C,+CAA4D,CAG5D,8BAA+B,CAC/B,gCAAiC,CACjC,4DACD,CAEA,+BAWC,sBAA4B,CAN5B,0BAAgC,CADhC,qCAAuC,CADvC,wEAA0E,CAD1E,uDAAwD,CAMxD,oDAAuD,CAWvD,oFAAuF,CAlBvF,SAAU,CAgBV,eAAgB,CAChB,mFA0BD,CAtBC,qCAgBC,mBAAsB,CADtB,sBAAyB,CAEzB,4BAA6B,CAH7B,4CAA6C,CAF7C,sFAAuF,CADvF,oFAAqF,CASrF,qBAAsB,CAdtB,QAAS,CAJT,QAAS,CAGT,SAAU,CADV,OAAQ,CAKR,mCAAoC,CACpC,yBAA0B,CAH1B,OAcD,CAGD,wCACC,GACC,SACD,CAEA,GACC,SACD,CACD,CAEA,wCACC,GACC,SACD,CAEA,GACC,SACD,CACD,CAEA,yCACC,GAGC,QAAS,CAFT,SAAU,CACV,OAED,CACA,IAEC,QAAS,CADT,UAED,CACA,GAGC,YAAc,CAFd,SAAU,CACV,UAED,CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-image-upload-complete-icon {\n\tdisplay: block;\n\tposition: absolute;\n\n\t/*\n\t * Smaller images should have the icon closer to the border.\n\t * Match the icon position with the linked image indicator brought by the link image feature.\n\t */\n\ttop: min(var(--ck-spacing-medium), 6%);\n\tright: min(var(--ck-spacing-medium), 6%);\n\tborder-radius: 50%;\n\tz-index: 1;\n\n\t&::after {\n\t\tcontent: \"\";\n\t\tposition: absolute;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-image-upload-icon: hsl(0, 0%, 100%);\n\t--ck-color-image-upload-icon-background: hsl(120, 100%, 27%);\n\n\t/* Match the icon size with the linked image indicator brought by the link image feature. */\n\t--ck-image-upload-icon-size: 20;\n\t--ck-image-upload-icon-width: 2px;\n\t--ck-image-upload-icon-is-visible: clamp(0px, 100% - 50px, 1px);\n}\n\n.ck-image-upload-complete-icon {\n\topacity: 0;\n\tbackground: var(--ck-color-image-upload-icon-background);\n\tanimation-name: ck-upload-complete-icon-show, ck-upload-complete-icon-hide;\n\tanimation-fill-mode: forwards, forwards;\n\tanimation-duration: 500ms, 500ms;\n\n\t/* To make animation scalable. */\n\tfont-size: calc(1px * var(--ck-image-upload-icon-size));\n\n\t/* Hide completed upload icon after 3 seconds. */\n\tanimation-delay: 0ms, 3000ms;\n\n\t/*\n\t * Use CSS math to simulate container queries.\n\t * https://css-tricks.com/the-raven-technique-one-step-closer-to-container-queries/#what-about-showing-and-hiding-things\n\t */\n\toverflow: hidden;\n\twidth: calc(var(--ck-image-upload-icon-is-visible) * var(--ck-image-upload-icon-size));\n\theight: calc(var(--ck-image-upload-icon-is-visible) * var(--ck-image-upload-icon-size));\n\n\t/* This is check icon element made from border-width mixed with animations. */\n\t&::after {\n\t\t/* Because of border transformation we need to \"hard code\" left position. */\n\t\tleft: 25%;\n\n\t\ttop: 50%;\n\t\topacity: 0;\n\t\theight: 0;\n\t\twidth: 0;\n\n\t\ttransform: scaleX(-1) rotate(135deg);\n\t\ttransform-origin: left top;\n\t\tborder-top: var(--ck-image-upload-icon-width) solid var(--ck-color-image-upload-icon);\n\t\tborder-right: var(--ck-image-upload-icon-width) solid var(--ck-color-image-upload-icon);\n\n\t\tanimation-name: ck-upload-complete-icon-check;\n\t\tanimation-duration: 500ms;\n\t\tanimation-delay: 500ms;\n\t\tanimation-fill-mode: forwards;\n\n\t\t/* #1095. While reset is not providing proper box-sizing for pseudoelements, we need to handle it. */\n\t\tbox-sizing: border-box;\n\t}\n}\n\n@keyframes ck-upload-complete-icon-show {\n\tfrom {\n\t\topacity: 0;\n\t}\n\n\tto {\n\t\topacity: 1;\n\t}\n}\n\n@keyframes ck-upload-complete-icon-hide {\n\tfrom {\n\t\topacity: 1;\n\t}\n\n\tto {\n\t\topacity: 0;\n\t}\n}\n\n@keyframes ck-upload-complete-icon-check {\n\t0% {\n\t\topacity: 1;\n\t\twidth: 0;\n\t\theight: 0;\n\t}\n\t33% {\n\t\twidth: 0.3em;\n\t\theight: 0;\n\t}\n\t100% {\n\t\topacity: 1;\n\t\twidth: 0.3em;\n\t\theight: 0.45em;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9825:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-upload-placeholder-loader{align-items:center;display:flex;justify-content:center;left:0;position:absolute;top:0}.ck .ck-upload-placeholder-loader:before{content:\"\";position:relative}:root{--ck-color-upload-placeholder-loader:#b3b3b3;--ck-upload-placeholder-loader-size:32px;--ck-upload-placeholder-image-aspect-ratio:2.8}.ck .ck-image-upload-placeholder{margin:0;width:100%}.ck .ck-image-upload-placeholder.image-inline{width:calc(var(--ck-upload-placeholder-loader-size)*2*var(--ck-upload-placeholder-image-aspect-ratio))}.ck .ck-image-upload-placeholder img{aspect-ratio:var(--ck-upload-placeholder-image-aspect-ratio)}.ck .ck-upload-placeholder-loader{height:100%;width:100%}.ck .ck-upload-placeholder-loader:before{animation:ck-upload-placeholder-loader 1s linear infinite;border-radius:50%;border-right:2px solid transparent;border-top:3px solid var(--ck-color-upload-placeholder-loader);height:var(--ck-upload-placeholder-loader-size);width:var(--ck-upload-placeholder-loader-size)}@keyframes ck-upload-placeholder-loader{to{transform:rotate(1turn)}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadloader.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-image/imageuploadloader.css"],"names":[],"mappings":"AAKA,kCAGC,kBAAmB,CADnB,YAAa,CAEb,sBAAuB,CAEvB,MAAO,CALP,iBAAkB,CAIlB,KAOD,CAJC,yCACC,UAAW,CACX,iBACD,CCXD,MACC,4CAAqD,CACrD,wCAAyC,CACzC,8CACD,CAEA,iCAGC,QAAS,CADT,UAgBD,CAbC,8CACC,sGACD,CAEA,qCAOC,4DACD,CAGD,kCAEC,WAAY,CADZ,UAWD,CARC,yCAMC,yDAA0D,CAH1D,iBAAkB,CAElB,kCAAmC,CADnC,8DAA+D,CAF/D,+CAAgD,CADhD,8CAMD,CAGD,wCACC,GACC,uBACD,CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck .ck-upload-placeholder-loader {\n\tposition: absolute;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\ttop: 0;\n\tleft: 0;\n\n\t&::before {\n\t\tcontent: '';\n\t\tposition: relative;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-upload-placeholder-loader: hsl(0, 0%, 70%);\n\t--ck-upload-placeholder-loader-size: 32px;\n\t--ck-upload-placeholder-image-aspect-ratio: 2.8;\n}\n\n.ck .ck-image-upload-placeholder {\n\t/* We need to control the full width of the SVG gray background. */\n\twidth: 100%;\n\tmargin: 0;\n\n\t&.image-inline {\n\t\twidth: calc( 2 * var(--ck-upload-placeholder-loader-size) * var(--ck-upload-placeholder-image-aspect-ratio) );\n\t}\n\n\t& img {\n\t\t/*\n\t\t * This is an arbitrary aspect for a 1x1 px GIF to display to the user. Not too tall, not too short.\n\t\t * There's nothing special about this number except that it should make the image placeholder look like\n\t\t * a real image during this short period after the upload started and before the image was read from the\n\t\t * file system (and a rich preview was loaded).\n\t\t */\n\t\taspect-ratio: var(--ck-upload-placeholder-image-aspect-ratio);\n\t}\n}\n\n.ck .ck-upload-placeholder-loader {\n\twidth: 100%;\n\theight: 100%;\n\n\t&::before {\n\t\twidth: var(--ck-upload-placeholder-loader-size);\n\t\theight: var(--ck-upload-placeholder-loader-size);\n\t\tborder-radius: 50%;\n\t\tborder-top: 3px solid var(--ck-color-upload-placeholder-loader);\n\t\tborder-right: 2px solid transparent;\n\t\tanimation: ck-upload-placeholder-loader 1s linear infinite;\n\t}\n}\n\n@keyframes ck-upload-placeholder-loader {\n\tto {\n\t\ttransform: rotate( 360deg );\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5870:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-editor__editable .image,.ck.ck-editor__editable .image-inline{position:relative}.ck.ck-editor__editable .image .ck-progress-bar,.ck.ck-editor__editable .image-inline .ck-progress-bar{left:0;position:absolute;top:0}.ck.ck-editor__editable .image-inline.ck-appear,.ck.ck-editor__editable .image.ck-appear{animation:fadeIn .7s}.ck.ck-editor__editable .image .ck-progress-bar,.ck.ck-editor__editable .image-inline .ck-progress-bar{background:var(--ck-color-upload-bar-background);height:2px;transition:width .1s;width:0}@keyframes fadeIn{0%{opacity:0}to{opacity:1}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadprogress.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-image/imageuploadprogress.css"],"names":[],"mappings":"AAMC,qEAEC,iBACD,CAGA,uGAIC,MAAO,CAFP,iBAAkB,CAClB,KAED,CCRC,yFACC,oBACD,CAID,uGAIC,gDAAiD,CAFjD,UAAW,CAGX,oBAAuB,CAFvB,OAGD,CAGD,kBACC,GAAO,SAAY,CACnB,GAAO,SAAY,CACpB","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-editor__editable {\n\t& .image,\n\t& .image-inline {\n\t\tposition: relative;\n\t}\n\n\t/* Upload progress bar. */\n\t& .image .ck-progress-bar,\n\t& .image-inline .ck-progress-bar {\n\t\tposition: absolute;\n\t\ttop: 0;\n\t\tleft: 0;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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.ck.ck-editor__editable {\n\t& .image,\n\t& .image-inline {\n\t\t/* Showing animation. */\n\t\t&.ck-appear {\n\t\t\tanimation: fadeIn 700ms;\n\t\t}\n\t}\n\n\t/* Upload progress bar. */\n\t& .image .ck-progress-bar,\n\t& .image-inline .ck-progress-bar {\n\t\theight: 2px;\n\t\twidth: 0;\n\t\tbackground: var(--ck-color-upload-bar-background);\n\t\ttransition: width 100ms;\n\t}\n}\n\n@keyframes fadeIn {\n\tfrom { opacity: 0; }\n\tto   { opacity: 1; }\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6831:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-text-alternative-form{display:flex;flex-direction:row;flex-wrap:nowrap}.ck.ck-text-alternative-form .ck-labeled-field-view{display:inline-block}.ck.ck-text-alternative-form .ck-label{display:none}@media screen and (max-width:600px){.ck.ck-text-alternative-form{flex-wrap:wrap}.ck.ck-text-alternative-form .ck-labeled-field-view{flex-basis:100%}.ck.ck-text-alternative-form .ck-button{flex-basis:50%}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-image/theme/textalternativeform.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css"],"names":[],"mappings":"AAOA,6BACC,YAAa,CACb,kBAAmB,CACnB,gBAqBD,CAnBC,oDACC,oBACD,CAEA,uCACC,YACD,CCZA,oCDCD,6BAcE,cAUF,CARE,oDACC,eACD,CAEA,wCACC,cACD,CCrBD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck.ck-text-alternative-form {\n\tdisplay: flex;\n\tflex-direction: row;\n\tflex-wrap: nowrap;\n\n\t& .ck-labeled-field-view {\n\t\tdisplay: inline-block;\n\t}\n\n\t& .ck-label {\n\t\tdisplay: none;\n\t}\n\n\t@mixin ck-media-phone {\n\t\tflex-wrap: wrap;\n\n\t\t& .ck-labeled-field-view {\n\t\t\tflex-basis: 100%;\n\t\t}\n\n\t\t& .ck-button {\n\t\t\tflex-basis: 50%;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4704:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content span[lang]{font-style:italic}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-language/theme/language.css"],"names":[],"mappings":"AAKA,uBACC,iBACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content span[lang] {\n\tfont-style: italic;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 399:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-link_selected{background:var(--ck-color-link-selected-background)}.ck .ck-link_selected span.image-inline{outline:var(--ck-widget-outline-thickness) solid var(--ck-color-link-selected-background)}.ck .ck-fake-link-selection{background:var(--ck-color-link-fake-selection)}.ck .ck-fake-link-selection_collapsed{border-right:1px solid var(--ck-color-base-text);height:100%;margin-right:-1px;outline:1px solid hsla(0,0%,100%,.5)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-link/link.css"],"names":[],"mappings":"AAMA,sBACC,mDAMD,CAHC,wCACC,yFACD,CAOD,4BACC,8CACD,CAGA,sCAEC,gDAAiD,CADjD,WAAY,CAEZ,iBAAkB,CAClB,oCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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/* Class added to span element surrounding currently selected link. */\n.ck .ck-link_selected {\n\tbackground: var(--ck-color-link-selected-background);\n\n\t/* Give linked inline images some outline to let the user know they are also part of the link. */\n\t& span.image-inline {\n\t\toutline: var(--ck-widget-outline-thickness) solid var(--ck-color-link-selected-background);\n\t}\n}\n\n/*\n * Classes used by the \"fake visual selection\" displayed in the content when an input\n * in the link UI has focus (the browser does not render the native selection in this state).\n */\n.ck .ck-fake-link-selection {\n\tbackground: var(--ck-color-link-fake-selection);\n}\n\n/* A collapsed fake visual selection. */\n.ck .ck-fake-link-selection_collapsed {\n\theight: 100%;\n\tborder-right: 1px solid var(--ck-color-base-text);\n\tmargin-right: -1px;\n\toutline: solid 1px hsla(0, 0%, 100%, .5);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9465:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-link-actions{display:flex;flex-direction:row;flex-wrap:nowrap}.ck.ck-link-actions .ck-link-actions__preview{display:inline-block}.ck.ck-link-actions .ck-link-actions__preview .ck-button__label{overflow:hidden}@media screen and (max-width:600px){.ck.ck-link-actions{flex-wrap:wrap}.ck.ck-link-actions .ck-link-actions__preview{flex-basis:100%}.ck.ck-link-actions .ck-button:not(.ck-link-actions__preview){flex-basis:50%}}.ck.ck-link-actions .ck-button.ck-link-actions__preview{padding-left:0;padding-right:0}.ck.ck-link-actions .ck-button.ck-link-actions__preview .ck-button__label{color:var(--ck-color-link-default);cursor:pointer;max-width:var(--ck-input-width);min-width:3em;padding:0 var(--ck-spacing-medium);text-align:center;text-overflow:ellipsis}.ck.ck-link-actions .ck-button.ck-link-actions__preview .ck-button__label:hover{text-decoration:underline}.ck.ck-link-actions .ck-button.ck-link-actions__preview,.ck.ck-link-actions .ck-button.ck-link-actions__preview:active,.ck.ck-link-actions .ck-button.ck-link-actions__preview:focus,.ck.ck-link-actions .ck-button.ck-link-actions__preview:hover{background:none}.ck.ck-link-actions .ck-button.ck-link-actions__preview:active{box-shadow:none}.ck.ck-link-actions .ck-button.ck-link-actions__preview:focus .ck-button__label{text-decoration:underline}[dir=ltr] .ck.ck-link-actions .ck-button:not(:first-child),[dir=rtl] .ck.ck-link-actions .ck-button:not(:last-child){margin-left:var(--ck-spacing-standard)}@media screen and (max-width:600px){.ck.ck-link-actions .ck-button.ck-link-actions__preview{margin:var(--ck-spacing-standard) var(--ck-spacing-standard) 0}.ck.ck-link-actions .ck-button.ck-link-actions__preview .ck-button__label{max-width:100%;min-width:0}[dir=ltr] .ck.ck-link-actions .ck-button:not(.ck-link-actions__preview),[dir=rtl] .ck.ck-link-actions .ck-button:not(.ck-link-actions__preview){margin-left:0}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-link/theme/linkactions.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-link/linkactions.css"],"names":[],"mappings":"AAOA,oBACC,YAAa,CACb,kBAAmB,CACnB,gBAqBD,CAnBC,8CACC,oBAKD,CAHC,gEACC,eACD,CCXD,oCDCD,oBAcE,cAUF,CARE,8CACC,eACD,CAEA,8DACC,cACD,CCrBD,CCIA,wDACC,cAAe,CACf,eAmCD,CAjCC,0EAEC,kCAAmC,CAEnC,cAAe,CAIf,+BAAgC,CAChC,aAAc,CARd,kCAAmC,CASnC,iBAAkB,CAPlB,sBAYD,CAHC,gFACC,yBACD,CAGD,mPAIC,eACD,CAEA,+DACC,eACD,CAGC,gFACC,yBACD,CAWD,qHACC,sCACD,CDtDD,oCC0DC,wDACC,8DAMD,CAJC,0EAEC,cAAe,CADf,WAED,CAGD,gJAME,aAEF,CDzED","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck.ck-link-actions {\n\tdisplay: flex;\n\tflex-direction: row;\n\tflex-wrap: nowrap;\n\n\t& .ck-link-actions__preview {\n\t\tdisplay: inline-block;\n\n\t\t& .ck-button__label {\n\t\t\toverflow: hidden;\n\t\t}\n\t}\n\n\t@mixin ck-media-phone {\n\t\tflex-wrap: wrap;\n\n\t\t& .ck-link-actions__preview {\n\t\t\tflex-basis: 100%;\n\t\t}\n\n\t\t& .ck-button:not(.ck-link-actions__preview) {\n\t\t\tflex-basis: 50%;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_unselectable.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n@import \"../mixins/_focus.css\";\n@import \"../mixins/_shadow.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck.ck-link-actions {\n\t& .ck-button.ck-link-actions__preview {\n\t\tpadding-left: 0;\n\t\tpadding-right: 0;\n\n\t\t& .ck-button__label {\n\t\t\tpadding: 0 var(--ck-spacing-medium);\n\t\t\tcolor: var(--ck-color-link-default);\n\t\t\ttext-overflow: ellipsis;\n\t\t\tcursor: pointer;\n\n\t\t\t/* Match the box model of the link editor form's input so the balloon\n\t\t\tdoes not change width when moving between actions and the form. */\n\t\t\tmax-width: var(--ck-input-width);\n\t\t\tmin-width: 3em;\n\t\t\ttext-align: center;\n\n\t\t\t&:hover {\n\t\t\t\ttext-decoration: underline;\n\t\t\t}\n\t\t}\n\n\t\t&,\n\t\t&:hover,\n\t\t&:focus,\n\t\t&:active {\n\t\t\tbackground: none;\n\t\t}\n\n\t\t&:active {\n\t\t\tbox-shadow: none;\n\t\t}\n\n\t\t&:focus {\n\t\t\t& .ck-button__label {\n\t\t\t\ttext-decoration: underline;\n\t\t\t}\n\t\t}\n\t}\n\n\t@mixin ck-dir ltr {\n\t\t& .ck-button:not(:first-child) {\n\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t}\n\t}\n\n\t@mixin ck-dir rtl {\n\t\t& .ck-button:not(:last-child) {\n\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t}\n\t}\n\n\t@mixin ck-media-phone {\n\t\t& .ck-button.ck-link-actions__preview {\n\t\t\tmargin: var(--ck-spacing-standard) var(--ck-spacing-standard) 0;\n\n\t\t\t& .ck-button__label {\n\t\t\t\tmin-width: 0;\n\t\t\t\tmax-width: 100%;\n\t\t\t}\n\t\t}\n\n\t\t& .ck-button:not(.ck-link-actions__preview) {\n\t\t\t@mixin ck-dir ltr {\n\t\t\t\tmargin-left: 0;\n\t\t\t}\n\n\t\t\t@mixin ck-dir rtl {\n\t\t\t\tmargin-left: 0;\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4827:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-link-form{display:flex}.ck.ck-link-form .ck-label{display:none}@media screen and (max-width:600px){.ck.ck-link-form{flex-wrap:wrap}.ck.ck-link-form .ck-labeled-field-view{flex-basis:100%}.ck.ck-link-form .ck-button{flex-basis:50%}}.ck.ck-link-form_layout-vertical{display:block}.ck.ck-link-form_layout-vertical .ck-button.ck-button-cancel,.ck.ck-link-form_layout-vertical .ck-button.ck-button-save{margin-top:var(--ck-spacing-medium)}.ck.ck-link-form_layout-vertical{min-width:var(--ck-input-width);padding:0}.ck.ck-link-form_layout-vertical .ck-labeled-field-view{margin:var(--ck-spacing-large) var(--ck-spacing-large) var(--ck-spacing-small)}.ck.ck-link-form_layout-vertical .ck-labeled-field-view .ck-input-text{min-width:0;width:100%}.ck.ck-link-form_layout-vertical>.ck-button{border-radius:0;margin:0;padding:var(--ck-spacing-standard);width:50%}.ck.ck-link-form_layout-vertical>.ck-button:not(:focus){border-top:1px solid var(--ck-color-base-border)}[dir=ltr] .ck.ck-link-form_layout-vertical>.ck-button,[dir=rtl] .ck.ck-link-form_layout-vertical>.ck-button{margin-left:0}[dir=rtl] .ck.ck-link-form_layout-vertical>.ck-button:last-of-type{border-right:1px solid var(--ck-color-base-border)}.ck.ck-link-form_layout-vertical .ck.ck-list{margin:var(--ck-spacing-standard) var(--ck-spacing-large)}.ck.ck-link-form_layout-vertical .ck.ck-list .ck-button.ck-switchbutton{padding:0;width:100%}.ck.ck-link-form_layout-vertical .ck.ck-list .ck-button.ck-switchbutton:hover{background:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-link/theme/linkform.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-link/linkform.css"],"names":[],"mappings":"AAOA,iBACC,YAiBD,CAfC,2BACC,YACD,CCNA,oCDCD,iBAQE,cAUF,CARE,wCACC,eACD,CAEA,4BACC,cACD,CCfD,CDuBD,iCACC,aAYD,CALE,wHAEC,mCACD,CE/BF,iCAEC,+BAAgC,CADhC,SAgDD,CA7CC,wDACC,8EAMD,CAJC,uEACC,WAAY,CACZ,UACD,CAGD,4CAIC,eAAgB,CAFhB,QAAS,CADT,kCAAmC,CAEnC,SAkBD,CAfC,wDACC,gDACD,CARD,4GAeE,aAMF,CAJE,mEACC,kDACD,CAKF,6CACC,yDAUD,CARC,wEACC,SAAU,CACV,UAKD,CAHC,8EACC,eACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck.ck-link-form {\n\tdisplay: flex;\n\n\t& .ck-label {\n\t\tdisplay: none;\n\t}\n\n\t@mixin ck-media-phone {\n\t\tflex-wrap: wrap;\n\n\t\t& .ck-labeled-field-view {\n\t\t\tflex-basis: 100%;\n\t\t}\n\n\t\t& .ck-button {\n\t\t\tflex-basis: 50%;\n\t\t}\n\t}\n}\n\n/*\n * Style link form differently when manual decorators are available.\n * See: https://github.com/ckeditor/ckeditor5-link/issues/186.\n */\n.ck.ck-link-form_layout-vertical {\n\tdisplay: block;\n\n\t/*\n\t * Whether the form is in the responsive mode or not, if there are decorator buttons\n\t * keep the top margin of action buttons medium.\n\t */\n\t& .ck-button {\n\t\t&.ck-button-save,\n\t\t&.ck-button-cancel {\n\t\t\tmargin-top: var(--ck-spacing-medium);\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n/*\n * Style link form differently when manual decorators are available.\n * See: https://github.com/ckeditor/ckeditor5-link/issues/186.\n */\n.ck.ck-link-form_layout-vertical {\n\tpadding: 0;\n\tmin-width: var(--ck-input-width);\n\n\t& .ck-labeled-field-view {\n\t\tmargin: var(--ck-spacing-large) var(--ck-spacing-large) var(--ck-spacing-small);\n\n\t\t& .ck-input-text {\n\t\t\tmin-width: 0;\n\t\t\twidth: 100%;\n\t\t}\n\t}\n\n\t& > .ck-button {\n\t\tpadding: var(--ck-spacing-standard);\n\t\tmargin: 0;\n\t\twidth: 50%;\n\t\tborder-radius: 0;\n\n\t\t&:not(:focus) {\n\t\t\tborder-top: 1px solid var(--ck-color-base-border);\n\t\t}\n\n\t\t@mixin ck-dir ltr {\n\t\t\tmargin-left: 0;\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\tmargin-left: 0;\n\n\t\t\t&:last-of-type {\n\t\t\t\tborder-right: 1px solid var(--ck-color-base-border);\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Using additional `.ck` class for stronger CSS specificity than `.ck.ck-link-form > :not(:first-child)`. */\n\t& .ck.ck-list {\n\t\tmargin: var(--ck-spacing-standard) var(--ck-spacing-large);\n\n\t\t& .ck-button.ck-switchbutton {\n\t\t\tpadding: 0;\n\t\t\twidth: 100%;\n\n\t\t\t&:hover {\n\t\t\t\tbackground: none;\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3858:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-editor__editable a span.image-inline:after,.ck.ck-editor__editable figure.image>a:after{display:block;position:absolute}:root{--ck-link-image-indicator-icon-size:20;--ck-link-image-indicator-icon-is-visible:clamp(0px,100% - 50px,1px)}.ck.ck-editor__editable a span.image-inline:after,.ck.ck-editor__editable figure.image>a:after{background-color:rgba(0,0,0,.4);background-image:url(\"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0ibTExLjA3NyAxNSAuOTkxLTEuNDE2YS43NS43NSAwIDEgMSAxLjIyOS44NmwtMS4xNDggMS42NGEuNzQ4Ljc0OCAwIDAgMS0uMjE3LjIwNiA1LjI1MSA1LjI1MSAwIDAgMS04LjUwMy01Ljk1NS43NDEuNzQxIDAgMCAxIC4xMi0uMjc0bDEuMTQ3LTEuNjM5YS43NS43NSAwIDEgMSAxLjIyOC44Nkw0LjkzMyAxMC43bC4wMDYuMDAzYTMuNzUgMy43NSAwIDAgMCA2LjEzMiA0LjI5NGwuMDA2LjAwNHptNS40OTQtNS4zMzVhLjc0OC43NDggMCAwIDEtLjEyLjI3NGwtMS4xNDcgMS42MzlhLjc1Ljc1IDAgMSAxLTEuMjI4LS44NmwuODYtMS4yM2EzLjc1IDMuNzUgMCAwIDAtNi4xNDQtNC4zMDFsLS44NiAxLjIyOWEuNzUuNzUgMCAwIDEtMS4yMjktLjg2bDEuMTQ4LTEuNjRhLjc0OC43NDggMCAwIDEgLjIxNy0uMjA2IDUuMjUxIDUuMjUxIDAgMCAxIDguNTAzIDUuOTU1em0tNC41NjMtMi41MzJhLjc1Ljc1IDAgMCAxIC4xODQgMS4wNDVsLTMuMTU1IDQuNTA1YS43NS43NSAwIDEgMS0xLjIyOS0uODZsMy4xNTUtNC41MDZhLjc1Ljc1IDAgMCAxIDEuMDQ1LS4xODR6Ii8+PC9zdmc+\");background-position:50%;background-repeat:no-repeat;background-size:14px;border-radius:100%;content:\"\";height:calc(var(--ck-link-image-indicator-icon-is-visible)*var(--ck-link-image-indicator-icon-size));overflow:hidden;right:min(var(--ck-spacing-medium),6%);top:min(var(--ck-spacing-medium),6%);width:calc(var(--ck-link-image-indicator-icon-is-visible)*var(--ck-link-image-indicator-icon-size))}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-link/theme/linkimage.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-link/linkimage.css"],"names":[],"mappings":"AASE,+FACC,aAAc,CACd,iBACD,CCPF,MAEC,sCAAuC,CACvC,oEACD,CAME,+FAUC,+BAAqC,CACrC,83BAA+3B,CAG/3B,uBAA2B,CAD3B,2BAA4B,CAD5B,oBAAqB,CAGrB,kBAAmB,CAdnB,UAAW,CAsBX,oGAAuG,CAFvG,eAAgB,CAbhB,sCAAwC,CADxC,oCAAsC,CAetC,mGAED","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-editor__editable {\n\t/* Linked image indicator */\n\t& figure.image > a,\n\t& a span.image-inline {\n\t\t&::after {\n\t\t\tdisplay: block;\n\t\t\tposition: absolute;\n\t\t}\n\t}\n}\n\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t/* Match the icon size with the upload indicator brought by the image upload feature. */\n\t--ck-link-image-indicator-icon-size: 20;\n\t--ck-link-image-indicator-icon-is-visible: clamp(0px, 100% - 50px, 1px);\n}\n\n.ck.ck-editor__editable {\n\t/* Linked image indicator */\n\t& figure.image > a,\n\t& a span.image-inline {\n\t\t&::after {\n\t\t\tcontent: \"\";\n\n\t\t\t/*\n\t\t\t * Smaller images should have the icon closer to the border.\n\t\t\t * Match the icon position with the upload indicator brought by the image upload feature.\n\t\t\t */\n\t\t\ttop: min(var(--ck-spacing-medium), 6%);\n\t\t\tright: min(var(--ck-spacing-medium), 6%);\n\n\t\t\tbackground-color: hsla(0, 0%, 0%, .4);\n\t\t\tbackground-image: url(\"data:image/svg+xml;base64,PHN2ZyB2aWV3Qm94PSIwIDAgMjAgMjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PHBhdGggZmlsbD0iI2ZmZiIgZD0ibTExLjA3NyAxNSAuOTkxLTEuNDE2YS43NS43NSAwIDEgMSAxLjIyOS44NmwtMS4xNDggMS42NGEuNzQ4Ljc0OCAwIDAgMS0uMjE3LjIwNiA1LjI1MSA1LjI1MSAwIDAgMS04LjUwMy01Ljk1NS43NDEuNzQxIDAgMCAxIC4xMi0uMjc0bDEuMTQ3LTEuNjM5YS43NS43NSAwIDEgMSAxLjIyOC44Nkw0LjkzMyAxMC43bC4wMDYuMDAzYTMuNzUgMy43NSAwIDAgMCA2LjEzMiA0LjI5NGwuMDA2LjAwNHptNS40OTQtNS4zMzVhLjc0OC43NDggMCAwIDEtLjEyLjI3NGwtMS4xNDcgMS42MzlhLjc1Ljc1IDAgMSAxLTEuMjI4LS44NmwuODYtMS4yM2EzLjc1IDMuNzUgMCAwIDAtNi4xNDQtNC4zMDFsLS44NiAxLjIyOWEuNzUuNzUgMCAwIDEtMS4yMjktLjg2bDEuMTQ4LTEuNjRhLjc0OC43NDggMCAwIDEgLjIxNy0uMjA2IDUuMjUxIDUuMjUxIDAgMCAxIDguNTAzIDUuOTU1em0tNC41NjMtMi41MzJhLjc1Ljc1IDAgMCAxIC4xODQgMS4wNDVsLTMuMTU1IDQuNTA1YS43NS43NSAwIDEgMS0xLjIyOS0uODZsMy4xNTUtNC41MDZhLjc1Ljc1IDAgMCAxIDEuMDQ1LS4xODR6Ii8+PC9zdmc+\");\n\t\t\tbackground-size: 14px;\n\t\t\tbackground-repeat: no-repeat;\n\t\t\tbackground-position: center;\n\t\t\tborder-radius: 100%;\n\n\t\t\t/*\n\t\t\t* Use CSS math to simulate container queries.\n\t\t\t* https://css-tricks.com/the-raven-technique-one-step-closer-to-container-queries/#what-about-showing-and-hiding-things\n\t\t\t*/\n\t\t\toverflow: hidden;\n\t\t\twidth: calc(var(--ck-link-image-indicator-icon-is-visible) * var(--ck-link-image-indicator-icon-size));\n\t\t\theight: calc(var(--ck-link-image-indicator-icon-is-visible) * var(--ck-link-image-indicator-icon-size));\n\t\t}\n\t}\n}\n\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3195:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-collapsible.ck-collapsible_collapsed>.ck-collapsible__children{display:none}:root{--ck-collapsible-arrow-size:calc(var(--ck-icon-size)*0.5)}.ck.ck-collapsible>.ck.ck-button{border-radius:0;color:inherit;font-weight:700;padding:var(--ck-spacing-medium) var(--ck-spacing-large);width:100%}.ck.ck-collapsible>.ck.ck-button:focus{background:transparent}.ck.ck-collapsible>.ck.ck-button:active,.ck.ck-collapsible>.ck.ck-button:hover:not(:focus),.ck.ck-collapsible>.ck.ck-button:not(:focus){background:transparent;border-color:transparent;box-shadow:none}.ck.ck-collapsible>.ck.ck-button>.ck-icon{margin-right:var(--ck-spacing-medium);width:var(--ck-collapsible-arrow-size)}.ck.ck-collapsible>.ck-collapsible__children{padding:0 var(--ck-spacing-large) var(--ck-spacing-large)}.ck.ck-collapsible.ck-collapsible_collapsed>.ck.ck-button .ck-icon{transform:rotate(-90deg)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-list/theme/collapsible.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-list/collapsible.css"],"names":[],"mappings":"AAMC,sEACC,YACD,CCHD,MACC,yDACD,CAGC,iCAIC,eAAgB,CAChB,aAAc,CAHd,eAAiB,CACjB,wDAAyD,CAFzD,UAoBD,CAdC,uCACC,sBACD,CAEA,wIACC,sBAAuB,CACvB,wBAAyB,CACzB,eACD,CAEA,0CACC,qCAAsC,CACtC,sCACD,CAGD,6CACC,yDACD,CAGC,mEACC,wBACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-collapsible.ck-collapsible_collapsed {\n\t& > .ck-collapsible__children {\n\t\tdisplay: none;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-collapsible-arrow-size: calc(0.5 * var(--ck-icon-size));\n}\n\n.ck.ck-collapsible {\n\t& > .ck.ck-button {\n\t\twidth: 100%;\n\t\tfont-weight: bold;\n\t\tpadding: var(--ck-spacing-medium) var(--ck-spacing-large);\n\t\tborder-radius: 0;\n\t\tcolor: inherit;\n\n\t\t&:focus {\n\t\t\tbackground: transparent;\n\t\t}\n\n\t\t&:active, &:not(:focus), &:hover:not(:focus) {\n\t\t\tbackground: transparent;\n\t\t\tborder-color: transparent;\n\t\t\tbox-shadow: none;\n\t\t}\n\n\t\t& > .ck-icon {\n\t\t\tmargin-right: var(--ck-spacing-medium);\n\t\t\twidth: var(--ck-collapsible-arrow-size);\n\t\t}\n\t}\n\n\t& > .ck-collapsible__children {\n\t\tpadding: 0 var(--ck-spacing-large) var(--ck-spacing-large);\n\t}\n\n\t&.ck-collapsible_collapsed {\n\t\t& > .ck.ck-button .ck-icon {\n\t\t\ttransform: rotate(-90deg);\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8676:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-editor__editable .ck-list-bogus-paragraph{display:block}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-list/theme/documentlist.css"],"names":[],"mappings":"AAKA,8CACC,aACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-editor__editable .ck-list-bogus-paragraph {\n\tdisplay: block;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 7133:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-list-properties.ck-list-properties_without-styles{padding:var(--ck-spacing-large)}.ck.ck-list-properties.ck-list-properties_without-styles>*{min-width:14em}.ck.ck-list-properties.ck-list-properties_without-styles>*+*{margin-top:var(--ck-spacing-standard)}.ck.ck-list-properties.ck-list-properties_with-numbered-properties>.ck-list-styles-list{grid-template-columns:repeat(4,auto)}.ck.ck-list-properties.ck-list-properties_with-numbered-properties>.ck-collapsible{border-top:1px solid var(--ck-color-base-border)}.ck.ck-list-properties.ck-list-properties_with-numbered-properties>.ck-collapsible>.ck-collapsible__children>*{width:100%}.ck.ck-list-properties.ck-list-properties_with-numbered-properties>.ck-collapsible>.ck-collapsible__children>*+*{margin-top:var(--ck-spacing-standard)}.ck.ck-list-properties .ck.ck-numbered-list-properties__start-index .ck-input{min-width:auto;width:100%}.ck.ck-list-properties .ck.ck-numbered-list-properties__reversed-order{background:transparent;margin-bottom:calc(var(--ck-spacing-tiny)*-1);padding-left:0;padding-right:0}.ck.ck-list-properties .ck.ck-numbered-list-properties__reversed-order:active,.ck.ck-list-properties .ck.ck-numbered-list-properties__reversed-order:hover{background:none;border-color:transparent;box-shadow:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-list/listproperties.css"],"names":[],"mappings":"AAOC,yDACC,+BASD,CAPC,2DACC,cAKD,CAHC,6DACC,qCACD,CASD,wFACC,oCACD,CAGA,mFACC,gDAWD,CARE,+GACC,UAKD,CAHC,iHACC,qCACD,CAMJ,8EACC,cAAe,CACf,UACD,CAEA,uEACC,sBAAuB,CAGvB,6CAAgD,CAFhD,cAAe,CACf,eAQD,CALC,2JAGC,eAAgB,CADhB,wBAAyB,CADzB,eAGD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-list-properties {\n\t/* When there are no list styles and there is no collapsible. */\n\t&.ck-list-properties_without-styles {\n\t\tpadding: var(--ck-spacing-large);\n\n\t\t& > * {\n\t\t\tmin-width: 14em;\n\n\t\t\t& + * {\n\t\t\t\tmargin-top: var(--ck-spacing-standard);\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * When the numbered list property fields (start at, reversed) should be displayed,\n\t * more horizontal space is needed. Reconfigure the style grid to create that space.\n\t */\n\t&.ck-list-properties_with-numbered-properties {\n\t\t& > .ck-list-styles-list {\n\t\t\tgrid-template-columns: repeat( 4, auto );\n\t\t}\n\n\t\t/* When list styles are rendered and property fields are in a collapsible. */\n\t\t& > .ck-collapsible {\n\t\t\tborder-top: 1px solid var(--ck-color-base-border);\n\n\t\t\t& > .ck-collapsible__children {\n\t\t\t\t& > * {\n\t\t\t\t\twidth: 100%;\n\n\t\t\t\t\t& + * {\n\t\t\t\t\t\tmargin-top: var(--ck-spacing-standard);\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t& .ck.ck-numbered-list-properties__start-index .ck-input {\n\t\tmin-width: auto;\n\t\twidth: 100%;\n\t}\n\n\t& .ck.ck-numbered-list-properties__reversed-order {\n\t\tbackground: transparent;\n\t\tpadding-left: 0;\n\t\tpadding-right: 0;\n\t\tmargin-bottom: calc(-1 * var(--ck-spacing-tiny));\n\n\t\t&:active, &:hover {\n\t\t\tbox-shadow: none;\n\t\t\tborder-color: transparent;\n\t\t\tbackground: none;\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4553:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-list-styles-list{display:grid}.ck-content ol{list-style-type:decimal}.ck-content ol ol{list-style-type:lower-latin}.ck-content ol ol ol{list-style-type:lower-roman}.ck-content ol ol ol ol{list-style-type:upper-latin}.ck-content ol ol ol ol ol{list-style-type:upper-roman}.ck-content ul{list-style-type:circle}.ck-content ul ul{list-style-type:disc}.ck-content ul ul ul,.ck-content ul ul ul ul{list-style-type:square}:root{--ck-list-style-button-size:44px}.ck.ck-list-styles-list{column-gap:var(--ck-spacing-medium);grid-template-columns:repeat(3,auto);padding:var(--ck-spacing-large);row-gap:var(--ck-spacing-medium)}.ck.ck-list-styles-list .ck-button{box-sizing:content-box;margin:0;padding:0}.ck.ck-list-styles-list .ck-button,.ck.ck-list-styles-list .ck-button .ck-icon{height:var(--ck-list-style-button-size);width:var(--ck-list-style-button-size)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-list/theme/liststyles.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-list/liststyles.css"],"names":[],"mappings":"AAKA,wBACC,YACD,CAEA,eACC,uBAiBD,CAfC,kBACC,2BAaD,CAXC,qBACC,2BASD,CAPC,wBACC,2BAKD,CAHC,2BACC,2BACD,CAMJ,eACC,sBAaD,CAXC,kBACC,oBASD,CAJE,6CACC,sBACD,CCnCH,MACC,gCACD,CAEA,wBAGC,mCAAoC,CAFpC,oCAAwC,CAGxC,+BAAgC,CAFhC,gCA4BD,CAxBC,mCAiBC,sBAAuB,CAPvB,QAAS,CANT,SAmBD,CAJC,+EAhBA,uCAAwC,CADxC,sCAoBA","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-list-styles-list {\n\tdisplay: grid;\n}\n\n.ck-content ol {\n\tlist-style-type: decimal;\n\n\t& ol {\n\t\tlist-style-type: lower-latin;\n\n\t\t& ol {\n\t\t\tlist-style-type: lower-roman;\n\n\t\t\t& ol {\n\t\t\t\tlist-style-type: upper-latin;\n\n\t\t\t\t& ol {\n\t\t\t\t\tlist-style-type: upper-roman;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\n.ck-content ul {\n\tlist-style-type: circle;\n\n\t& ul {\n\t\tlist-style-type: disc;\n\n\t\t& ul {\n\t\t\tlist-style-type: square;\n\n\t\t\t& ul {\n\t\t\t\tlist-style-type: square;\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-list-style-button-size: 44px;\n}\n\n.ck.ck-list-styles-list {\n\tgrid-template-columns: repeat( 3, auto );\n\trow-gap: var(--ck-spacing-medium);\n\tcolumn-gap: var(--ck-spacing-medium);\n\tpadding: var(--ck-spacing-large);\n\n\t& .ck-button {\n\t\t/* Make the button look like a thumbnail (the icon \"takes it all\"). */\n\t\twidth: var(--ck-list-style-button-size);\n\t\theight: var(--ck-list-style-button-size);\n\t\tpadding: 0;\n\n\t\t/*\n\t\t * Buttons are aligned by the grid so disable default button margins to not collide with the\n\t\t * gaps in the grid.\n\t\t */\n\t\tmargin: 0;\n\n\t\t/*\n\t\t * Make sure the button border (which is displayed on focus, BTW) does not steal pixels\n\t\t * from the button dimensions and, as a result, decrease the size of the icon\n\t\t * (which becomes blurry as it scales down).\n\t\t */\n\t\tbox-sizing: content-box;\n\n\t\t& .ck-icon {\n\t\t\twidth: var(--ck-list-style-button-size);\n\t\t\theight: var(--ck-list-style-button-size);\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5777:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content .media{clear:both;display:block;margin:.9em 0;min-width:15em}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaembed.css"],"names":[],"mappings":"AAKA,mBAGC,UAAW,CASX,aAAc,CAJd,aAAe,CAQf,cACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content .media {\n\t/* Don't allow floated content overlap the media.\n\thttps://github.com/ckeditor/ckeditor5-media-embed/issues/53 */\n\tclear: both;\n\n\t/* Make sure there is some space between the content and the media. */\n\t/* The first value should be equal to --ck-spacing-large variable if used in the editor context\n\tto avoid the content jumping (See https://github.com/ckeditor/ckeditor5/issues/9825). */\n\tmargin: 0.9em 0;\n\n\t/* Make sure media is not overriden with Bootstrap default `flex` value.\n\tSee: https://github.com/ckeditor/ckeditor5/issues/1373. */\n\tdisplay: block;\n\n\t/* Give the media some minimal width in the content to prevent them\n\tfrom being \"squashed\" in tight spaces, e.g. in table cells (#44) */\n\tmin-width: 15em;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 952:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-media__wrapper .ck-media__placeholder{align-items:center;display:flex;flex-direction:column}.ck-media__wrapper .ck-media__placeholder .ck-media__placeholder__url{max-width:100%;position:relative}.ck-media__wrapper .ck-media__placeholder .ck-media__placeholder__url .ck-media__placeholder__url__text{display:block;overflow:hidden}.ck-media__wrapper[data-oembed-url*=\"facebook.com\"] .ck-media__placeholder__icon *,.ck-media__wrapper[data-oembed-url*=\"goo.gl/maps\"] .ck-media__placeholder__icon *,.ck-media__wrapper[data-oembed-url*=\"google.com/maps\"] .ck-media__placeholder__icon *,.ck-media__wrapper[data-oembed-url*=\"instagram.com\"] .ck-media__placeholder__icon *,.ck-media__wrapper[data-oembed-url*=\"maps.app.goo.gl\"] .ck-media__placeholder__icon *,.ck-media__wrapper[data-oembed-url*=\"maps.google.com\"] .ck-media__placeholder__icon *,.ck-media__wrapper[data-oembed-url*=\"twitter.com\"] .ck-media__placeholder__icon *{display:none}.ck-editor__editable:not(.ck-read-only) .ck-media__wrapper>:not(.ck-media__placeholder),.ck-editor__editable:not(.ck-read-only) .ck-widget:not(.ck-widget_selected) .ck-media__placeholder{pointer-events:none}:root{--ck-media-embed-placeholder-icon-size:3em;--ck-color-media-embed-placeholder-url-text:#757575;--ck-color-media-embed-placeholder-url-text-hover:var(--ck-color-base-text)}.ck-media__wrapper{margin:0 auto}.ck-media__wrapper .ck-media__placeholder{background:var(--ck-color-base-foreground);padding:calc(var(--ck-spacing-standard)*3)}.ck-media__wrapper .ck-media__placeholder .ck-media__placeholder__icon{background-position:50%;background-size:cover;height:var(--ck-media-embed-placeholder-icon-size);margin-bottom:var(--ck-spacing-large);min-width:var(--ck-media-embed-placeholder-icon-size)}.ck-media__wrapper .ck-media__placeholder .ck-media__placeholder__icon .ck-icon{height:100%;width:100%}.ck-media__wrapper .ck-media__placeholder .ck-media__placeholder__url__text{color:var(--ck-color-media-embed-placeholder-url-text);font-style:italic;text-align:center;text-overflow:ellipsis;white-space:nowrap}.ck-media__wrapper .ck-media__placeholder .ck-media__placeholder__url__text:hover{color:var(--ck-color-media-embed-placeholder-url-text-hover);cursor:pointer;text-decoration:underline}.ck-media__wrapper[data-oembed-url*=\"open.spotify.com\"]{max-height:380px;max-width:300px}.ck-media__wrapper[data-oembed-url*=\"goo.gl/maps\"] .ck-media__placeholder__icon,.ck-media__wrapper[data-oembed-url*=\"google.com/maps\"] .ck-media__placeholder__icon,.ck-media__wrapper[data-oembed-url*=\"maps.app.goo.gl\"] .ck-media__placeholder__icon,.ck-media__wrapper[data-oembed-url*=\"maps.google.com\"] .ck-media__placeholder__icon{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTAuMzc4IiBoZWlnaHQ9IjI1NC4xNjciIHZpZXdCb3g9IjAgMCA2Ni4yNDYgNjcuMjQ4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTcyLjUzMSAtMjE4LjQ1NSkgc2NhbGUoLjk4MDEyKSI+PHJlY3Qgcnk9IjUuMjM4IiByeD0iNS4yMzgiIHk9IjIzMS4zOTkiIHg9IjE3Ni4wMzEiIGhlaWdodD0iNjAuMDk5IiB3aWR0aD0iNjAuMDk5IiBmaWxsPSIjMzRhNjY4IiBwYWludC1vcmRlcj0ibWFya2VycyBzdHJva2UgZmlsbCIvPjxwYXRoIGQ9Im0yMDYuNDc3IDI2MC45LTI4Ljk4NyAyOC45ODdhNS4yMTggNS4yMTggMCAwIDAgMy43OCAxLjYxaDQ5LjYyMWMxLjY5NCAwIDMuMTktLjc5OCA0LjE0Ni0yLjAzN3oiIGZpbGw9IiM1Yzg4YzUiLz48cGF0aCBkPSJNMjI2Ljc0MiAyMjIuOTg4Yy05LjI2NiAwLTE2Ljc3NyA3LjE3LTE2Ljc3NyAxNi4wMTQuMDA3IDIuNzYyLjY2MyA1LjQ3NCAyLjA5MyA3Ljg3NS40My43MDMuODMgMS40MDggMS4xOSAyLjEwNy4zMzMuNTAyLjY1IDEuMDA1Ljk1IDEuNTA4LjM0My40NzcuNjczLjk1Ny45ODggMS40NCAxLjMxIDEuNzY5IDIuNSAzLjUwMiAzLjYzNyA1LjE2OC43OTMgMS4yNzUgMS42ODMgMi42NCAyLjQ2NiAzLjk5IDIuMzYzIDQuMDk0IDQuMDA3IDguMDkyIDQuNiAxMy45MTR2LjAxMmMuMTgyLjQxMi41MTYuNjY2Ljg3OS42NjcuNDAzLS4wMDEuNzY4LS4zMTQuOTMtLjc5OS42MDMtNS43NTYgMi4yMzgtOS43MjkgNC41ODUtMTMuNzk0Ljc4Mi0xLjM1IDEuNjczLTIuNzE1IDIuNDY1LTMuOTkgMS4xMzctMS42NjYgMi4zMjgtMy40IDMuNjM4LTUuMTY5LjMxNS0uNDgyLjY0NS0uOTYyLjk4OC0xLjQzOS4zLS41MDMuNjE3LTEuMDA2Ljk1LTEuNTA4LjM1OS0uNy43Ni0xLjQwNCAxLjE5LTIuMTA3IDEuNDI2LTIuNDAyIDItNS4xMTQgMi4wMDQtNy44NzUgMC04Ljg0NC03LjUxMS0xNi4wMTQtMTYuNzc2LTE2LjAxNHoiIGZpbGw9IiNkZDRiM2UiIHBhaW50LW9yZGVyPSJtYXJrZXJzIHN0cm9rZSBmaWxsIi8+PGVsbGlwc2Ugcnk9IjUuNTY0IiByeD0iNS44MjgiIGN5PSIyMzkuMDAyIiBjeD0iMjI2Ljc0MiIgZmlsbD0iIzgwMmQyNyIgcGFpbnQtb3JkZXI9Im1hcmtlcnMgc3Ryb2tlIGZpbGwiLz48cGF0aCBkPSJNMTkwLjMwMSAyMzcuMjgzYy00LjY3IDAtOC40NTcgMy44NTMtOC40NTcgOC42MDZzMy43ODYgOC42MDcgOC40NTcgOC42MDdjMy4wNDMgMCA0LjgwNi0uOTU4IDYuMzM3LTIuNTE2IDEuNTMtMS41NTcgMi4wODctMy45MTMgMi4wODctNi4yOSAwLS4zNjItLjAyMy0uNzIyLS4wNjQtMS4wNzloLTguMjU3djMuMDQzaDQuODVjLS4xOTcuNzU5LS41MzEgMS40NS0xLjA1OCAxLjk4Ni0uOTQyLjk1OC0yLjAyOCAxLjU0OC0zLjkwMSAxLjU0OC0yLjg3NiAwLTUuMjA4LTIuMzcyLTUuMjA4LTUuMjk5IDAtMi45MjYgMi4zMzItNS4yOTkgNS4yMDgtNS4yOTkgMS4zOTkgMCAyLjYxOC40MDcgMy41ODQgMS4yOTNsMi4zODEtMi4zOGMwLS4wMDItLjAwMy0uMDA0LS4wMDQtLjAwNS0xLjU4OC0xLjUyNC0zLjYyLTIuMjE1LTUuOTU1LTIuMjE1em00LjQzIDUuNjYuMDAzLjAwNnYtLjAwM3oiIGZpbGw9IiNmZmYiIHBhaW50LW9yZGVyPSJtYXJrZXJzIHN0cm9rZSBmaWxsIi8+PHBhdGggZD0ibTIxNS4xODQgMjUxLjkyOS03Ljk4IDcuOTc5IDI4LjQ3NyAyOC40NzVhNS4yMzMgNS4yMzMgMCAwIDAgLjQ0OS0yLjEyM3YtMzEuMTY1Yy0uNDY5LjY3NS0uOTM0IDEuMzQ5LTEuMzgyIDIuMDA1LS43OTIgMS4yNzUtMS42ODIgMi42NC0yLjQ2NSAzLjk5LTIuMzQ3IDQuMDY1LTMuOTgyIDguMDM4LTQuNTg1IDEzLjc5NC0uMTYyLjQ4NS0uNTI3Ljc5OC0uOTMuNzk5LS4zNjMtLjAwMS0uNjk3LS4yNTUtLjg3OS0uNjY3di0uMDEyYy0uNTkzLTUuODIyLTIuMjM3LTkuODItNC42LTEzLjkxNC0uNzgzLTEuMzUtMS42NzMtMi43MTUtMi40NjYtMy45OS0xLjEzNy0xLjY2Ni0yLjMyNy0zLjQtMy42MzctNS4xNjlsLS4wMDItLjAwM3oiIGZpbGw9IiNjM2MzYzMiLz48cGF0aCBkPSJtMjEyLjk4MyAyNDguNDk1LTM2Ljk1MiAzNi45NTN2LjgxMmE1LjIyNyA1LjIyNyAwIDAgMCA1LjIzOCA1LjIzOGgxLjAxNWwzNS42NjYtMzUuNjY2YTEzNi4yNzUgMTM2LjI3NSAwIDAgMC0yLjc2NC0zLjkgMzcuNTc1IDM3LjU3NSAwIDAgMC0uOTg5LTEuNDQgMzUuMTI3IDM1LjEyNyAwIDAgMC0uOTUtMS41MDhjLS4wODMtLjE2Mi0uMTc2LS4zMjYtLjI2NC0uNDg5eiIgZmlsbD0iI2ZkZGM0ZiIgcGFpbnQtb3JkZXI9Im1hcmtlcnMgc3Ryb2tlIGZpbGwiLz48cGF0aCBkPSJtMjExLjk5OCAyNjEuMDgzLTYuMTUyIDYuMTUxIDI0LjI2NCAyNC4yNjRoLjc4MWE1LjIyNyA1LjIyNyAwIDAgMCA1LjIzOS01LjIzOHYtMS4wNDV6IiBmaWxsPSIjZmZmIiBwYWludC1vcmRlcj0ibWFya2VycyBzdHJva2UgZmlsbCIvPjwvZz48L3N2Zz4=)}.ck-media__wrapper[data-oembed-url*=\"facebook.com\"] .ck-media__placeholder{background:#4268b3}.ck-media__wrapper[data-oembed-url*=\"facebook.com\"] .ck-media__placeholder .ck-media__placeholder__icon{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMTAyNCIgaGVpZ2h0PSIxMDI0IiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciPjxwYXRoIGQ9Ik05NjcuNDg0IDBINTYuNTE3QzI1LjMwNCAwIDAgMjUuMzA0IDAgNTYuNTE3djkxMC45NjZDMCA5OTguNjk0IDI1LjI5NyAxMDI0IDU2LjUyMiAxMDI0SDU0N1Y2MjhINDE0VjQ3M2gxMzNWMzU5LjAyOWMwLTEzMi4yNjIgODAuNzczLTIwNC4yODIgMTk4Ljc1Ni0yMDQuMjgyIDU2LjUxMyAwIDEwNS4wODYgNC4yMDggMTE5LjI0NCA2LjA4OVYyOTlsLTgxLjYxNi4wMzdjLTYzLjk5MyAwLTc2LjM4NCAzMC40OTItNzYuMzg0IDc1LjIzNlY0NzNoMTUzLjQ4N2wtMTkuOTg2IDE1NUg3MDd2Mzk2aDI2MC40ODRjMzEuMjEzIDAgNTYuNTE2LTI1LjMwMyA1Ni41MTYtNTYuNTE2VjU2LjUxNUMxMDI0IDI1LjMwMyA5OTguNjk3IDAgOTY3LjQ4NCAwIiBmaWxsPSIjRkZGRkZFIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiLz48L3N2Zz4=)}.ck-media__wrapper[data-oembed-url*=\"facebook.com\"] .ck-media__placeholder .ck-media__placeholder__url__text{color:#cdf}.ck-media__wrapper[data-oembed-url*=\"facebook.com\"] .ck-media__placeholder .ck-media__placeholder__url__text:hover{color:#fff}.ck-media__wrapper[data-oembed-url*=\"instagram.com\"] .ck-media__placeholder{background:linear-gradient(-135deg,#1400c7,#b800b1,#f50000)}.ck-media__wrapper[data-oembed-url*=\"instagram.com\"] .ck-media__placeholder .ck-media__placeholder__icon{background-image:url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTA0IiBoZWlnaHQ9IjUwNCIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+PGRlZnM+PHBhdGggaWQ9ImEiIGQ9Ik0wIC4xNTloNTAzLjg0MVY1MDMuOTRIMHoiLz48L2RlZnM+PGcgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj48bWFzayBpZD0iYiIgZmlsbD0iI2ZmZiI+PHVzZSB4bGluazpocmVmPSIjYSIvPjwvbWFzaz48cGF0aCBkPSJNMjUxLjkyMS4xNTljLTY4LjQxOCAwLTc2Ljk5Ny4yOS0xMDMuODY3IDEuNTE2LTI2LjgxNCAxLjIyMy00NS4xMjcgNS40ODItNjEuMTUxIDExLjcxLTE2LjU2NiA2LjQzNy0zMC42MTUgMTUuMDUxLTQ0LjYyMSAyOS4wNTYtMTQuMDA1IDE0LjAwNi0yMi42MTkgMjguMDU1LTI5LjA1NiA0NC42MjEtNi4yMjggMTYuMDI0LTEwLjQ4NyAzNC4zMzctMTEuNzEgNjEuMTUxQy4yOSAxNzUuMDgzIDAgMTgzLjY2MiAwIDI1Mi4wOGMwIDY4LjQxNy4yOSA3Ni45OTYgMS41MTYgMTAzLjg2NiAxLjIyMyAyNi44MTQgNS40ODIgNDUuMTI3IDExLjcxIDYxLjE1MSA2LjQzNyAxNi41NjYgMTUuMDUxIDMwLjYxNSAyOS4wNTYgNDQuNjIxIDE0LjAwNiAxNC4wMDUgMjguMDU1IDIyLjYxOSA0NC42MjEgMjkuMDU3IDE2LjAyNCA2LjIyNyAzNC4zMzcgMTAuNDg2IDYxLjE1MSAxMS43MDkgMjYuODcgMS4yMjYgMzUuNDQ5IDEuNTE2IDEwMy44NjcgMS41MTYgNjguNDE3IDAgNzYuOTk2LS4yOSAxMDMuODY2LTEuNTE2IDI2LjgxNC0xLjIyMyA0NS4xMjctNS40ODIgNjEuMTUxLTExLjcwOSAxNi41NjYtNi40MzggMzAuNjE1LTE1LjA1MiA0NC42MjEtMjkuMDU3IDE0LjAwNS0xNC4wMDYgMjIuNjE5LTI4LjA1NSAyOS4wNTctNDQuNjIxIDYuMjI3LTE2LjAyNCAxMC40ODYtMzQuMzM3IDExLjcwOS02MS4xNTEgMS4yMjYtMjYuODcgMS41MTYtMzUuNDQ5IDEuNTE2LTEwMy44NjYgMC02OC40MTgtLjI5LTc2Ljk5Ny0xLjUxNi0xMDMuODY3LTEuMjIzLTI2LjgxNC01LjQ4Mi00NS4xMjctMTEuNzA5LTYxLjE1MS02LjQzOC0xNi41NjYtMTUuMDUyLTMwLjYxNS0yOS4wNTctNDQuNjIxLTE0LjAwNi0xNC4wMDUtMjguMDU1LTIyLjYxOS00NC42MjEtMjkuMDU2LTE2LjAyNC02LjIyOC0zNC4zMzctMTAuNDg3LTYxLjE1MS0xMS43MUMzMjguOTE3LjQ0OSAzMjAuMzM4LjE1OSAyNTEuOTIxLjE1OVptMCA0NS4zOTFjNjcuMjY1IDAgNzUuMjMzLjI1NyAxMDEuNzk3IDEuNDY5IDI0LjU2MiAxLjEyIDM3LjkwMSA1LjIyNCA0Ni43NzggOC42NzQgMTEuNzU5IDQuNTcgMjAuMTUxIDEwLjAyOSAyOC45NjYgMTguODQ1IDguODE2IDguODE1IDE0LjI3NSAxNy4yMDcgMTguODQ1IDI4Ljk2NiAzLjQ1IDguODc3IDcuNTU0IDIyLjIxNiA4LjY3NCA0Ni43NzggMS4yMTIgMjYuNTY0IDEuNDY5IDM0LjUzMiAxLjQ2OSAxMDEuNzk4IDAgNjcuMjY1LS4yNTcgNzUuMjMzLTEuNDY5IDEwMS43OTctMS4xMiAyNC41NjItNS4yMjQgMzcuOTAxLTguNjc0IDQ2Ljc3OC00LjU3IDExLjc1OS0xMC4wMjkgMjAuMTUxLTE4Ljg0NSAyOC45NjYtOC44MTUgOC44MTYtMTcuMjA3IDE0LjI3NS0yOC45NjYgMTguODQ1LTguODc3IDMuNDUtMjIuMjE2IDcuNTU0LTQ2Ljc3OCA4LjY3NC0yNi41NiAxLjIxMi0zNC41MjcgMS40NjktMTAxLjc5NyAxLjQ2OS02Ny4yNzEgMC03NS4yMzctLjI1Ny0xMDEuNzk4LTEuNDY5LTI0LjU2Mi0xLjEyLTM3LjkwMS01LjIyNC00Ni43NzgtOC42NzQtMTEuNzU5LTQuNTctMjAuMTUxLTEwLjAyOS0yOC45NjYtMTguODQ1LTguODE1LTguODE1LTE0LjI3NS0xNy4yMDctMTguODQ1LTI4Ljk2Ni0zLjQ1LTguODc3LTcuNTU0LTIyLjIxNi04LjY3NC00Ni43NzgtMS4yMTItMjYuNTY0LTEuNDY5LTM0LjUzMi0xLjQ2OS0xMDEuNzk3IDAtNjcuMjY2LjI1Ny03NS4yMzQgMS40NjktMTAxLjc5OCAxLjEyLTI0LjU2MiA1LjIyNC0zNy45MDEgOC42NzQtNDYuNzc4IDQuNTctMTEuNzU5IDEwLjAyOS0yMC4xNTEgMTguODQ1LTI4Ljk2NiA4LjgxNS04LjgxNiAxNy4yMDctMTQuMjc1IDI4Ljk2Ni0xOC44NDUgOC44NzctMy40NSAyMi4yMTYtNy41NTQgNDYuNzc4LTguNjc0IDI2LjU2NC0xLjIxMiAzNC41MzItMS40NjkgMTAxLjc5OC0xLjQ2OVoiIGZpbGw9IiNGRkYiIG1hc2s9InVybCgjYikiLz48cGF0aCBkPSJNMjUxLjkyMSAzMzYuMDUzYy00Ni4zNzggMC04My45NzQtMzcuNTk2LTgzLjk3NC04My45NzMgMC00Ni4zNzggMzcuNTk2LTgzLjk3NCA4My45NzQtODMuOTc0IDQ2LjM3NyAwIDgzLjk3MyAzNy41OTYgODMuOTczIDgzLjk3NCAwIDQ2LjM3Ny0zNy41OTYgODMuOTczLTgzLjk3MyA4My45NzNabTAtMjEzLjMzOGMtNzEuNDQ3IDAtMTI5LjM2NSA1Ny45MTgtMTI5LjM2NSAxMjkuMzY1IDAgNzEuNDQ2IDU3LjkxOCAxMjkuMzY0IDEyOS4zNjUgMTI5LjM2NCA3MS40NDYgMCAxMjkuMzY0LTU3LjkxOCAxMjkuMzY0LTEyOS4zNjQgMC03MS40NDctNTcuOTE4LTEyOS4zNjUtMTI5LjM2NC0xMjkuMzY1Wk00MTYuNjI3IDExNy42MDRjMCAxNi42OTYtMTMuNTM1IDMwLjIzLTMwLjIzMSAzMC4yMy0xNi42OTUgMC0zMC4yMy0xMy41MzQtMzAuMjMtMzAuMjMgMC0xNi42OTYgMTMuNTM1LTMwLjIzMSAzMC4yMy0zMC4yMzEgMTYuNjk2IDAgMzAuMjMxIDEzLjUzNSAzMC4yMzEgMzAuMjMxIiBmaWxsPSIjRkZGIi8+PC9nPjwvc3ZnPg==)}.ck-media__wrapper[data-oembed-url*=\"instagram.com\"] .ck-media__placeholder .ck-media__placeholder__url__text{color:#ffe0fe}.ck-media__wrapper[data-oembed-url*=\"instagram.com\"] .ck-media__placeholder .ck-media__placeholder__url__text:hover{color:#fff}.ck-media__wrapper[data-oembed-url*=\"twitter.com\"] .ck.ck-media__placeholder{background:linear-gradient(90deg,#71c6f4,#0d70a5)}.ck-media__wrapper[data-oembed-url*=\"twitter.com\"] .ck.ck-media__placeholder .ck-media__placeholder__icon{background-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCA0MDAgNDAwIiBzdHlsZT0iZW5hYmxlLWJhY2tncm91bmQ6bmV3IDAgMCA0MDAgNDAwIiB4bWw6c3BhY2U9InByZXNlcnZlIj48cGF0aCBkPSJNNDAwIDIwMGMwIDExMC41LTg5LjUgMjAwLTIwMCAyMDBTMCAzMTAuNSAwIDIwMCA4OS41IDAgMjAwIDBzMjAwIDg5LjUgMjAwIDIwMHpNMTYzLjQgMzA1LjVjODguNyAwIDEzNy4yLTczLjUgMTM3LjItMTM3LjIgMC0yLjEgMC00LjItLjEtNi4yIDkuNC02LjggMTcuNi0xNS4zIDI0LjEtMjUtOC42IDMuOC0xNy45IDYuNC0yNy43IDcuNiAxMC02IDE3LjYtMTUuNCAyMS4yLTI2LjctOS4zIDUuNS0xOS42IDkuNS0zMC42IDExLjctOC44LTkuNC0yMS4zLTE1LjItMzUuMi0xNS4yLTI2LjYgMC00OC4yIDIxLjYtNDguMiA0OC4yIDAgMy44LjQgNy41IDEuMyAxMS00MC4xLTItNzUuNi0yMS4yLTk5LjQtNTAuNC00LjEgNy4xLTYuNSAxNS40LTYuNSAyNC4yIDAgMTYuNyA4LjUgMzEuNSAyMS41IDQwLjEtNy45LS4yLTE1LjMtMi40LTIxLjgtNnYuNmMwIDIzLjQgMTYuNiA0Mi44IDM4LjcgNDcuMy00IDEuMS04LjMgMS43LTEyLjcgMS43LTMuMSAwLTYuMS0uMy05LjEtLjkgNi4xIDE5LjIgMjMuOSAzMy4xIDQ1IDMzLjUtMTYuNSAxMi45LTM3LjMgMjAuNi01OS45IDIwLjYtMy45IDAtNy43LS4yLTExLjUtLjcgMjEuMSAxMy44IDQ2LjUgMjEuOCA3My43IDIxLjgiIHN0eWxlPSJmaWxsOiNmZmYiLz48L3N2Zz4=)}.ck-media__wrapper[data-oembed-url*=\"twitter.com\"] .ck.ck-media__placeholder .ck-media__placeholder__url__text{color:#b8e6ff}.ck-media__wrapper[data-oembed-url*=\"twitter.com\"] .ck.ck-media__placeholder .ck-media__placeholder__url__text:hover{color:#fff}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaembedediting.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-media-embed/mediaembedediting.css"],"names":[],"mappings":"AAMC,0CAGC,kBAAmB,CAFnB,YAAa,CACb,qBAcD,CAXC,sEAEC,cAAe,CAEf,iBAMD,CAJC,wGAEC,aAAc,CADd,eAED,CAWD,6kBACC,YACD,CAYF,2LACC,mBACD,CC1CA,MACC,0CAA2C,CAE3C,mDAA4D,CAC5D,2EACD,CAEA,mBACC,aA+FD,CA7FC,0CAEC,0CAA2C,CAD3C,0CA6BD,CA1BC,uEAIC,uBAA2B,CAC3B,qBAAsB,CAHtB,kDAAmD,CACnD,qCAAsC,CAFtC,qDAUD,CAJC,gFAEC,WAAY,CADZ,UAED,CAGD,4EACC,sDAAuD,CAGvD,iBAAkB,CADlB,iBAAkB,CAElB,sBAAuB,CAHvB,kBAUD,CALC,kFACC,4DAA6D,CAC7D,cAAe,CACf,yBACD,CAIF,wDAEC,gBAAiB,CADjB,eAED,CAEA,4UAIC,wvGACD,CAEA,2EACC,kBAaD,CAXC,wGACC,orBACD,CAEA,6GACC,UAKD,CAHC,mHACC,UACD,CAIF,4EACC,2DAcD,CAZC,yGACC,4jHACD,CAGA,8GACC,aAKD,CAHC,oHACC,UACD,CAIF,6EAEC,iDAaD,CAXC,0GACC,wiCACD,CAEA,+GACC,aAKD,CAHC,qHACC,UACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-media__wrapper {\n\t& .ck-media__placeholder {\n\t\tdisplay: flex;\n\t\tflex-direction: column;\n\t\talign-items: center;\n\n\t\t& .ck-media__placeholder__url {\n\t\t\t/* Otherwise the URL will overflow when the content is very narrow. */\n\t\t\tmax-width: 100%;\n\n\t\t\tposition: relative;\n\n\t\t\t& .ck-media__placeholder__url__text {\n\t\t\t\toverflow: hidden;\n\t\t\t\tdisplay: block;\n\t\t\t}\n\t\t}\n\t}\n\n\t&[data-oembed-url*=\"twitter.com\"],\n\t&[data-oembed-url*=\"google.com/maps\"],\n\t&[data-oembed-url*=\"goo.gl/maps\"],\n\t&[data-oembed-url*=\"maps.google.com\"],\n\t&[data-oembed-url*=\"maps.app.goo.gl\"],\n\t&[data-oembed-url*=\"facebook.com\"],\n\t&[data-oembed-url*=\"instagram.com\"] {\n\t\t& .ck-media__placeholder__icon * {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n}\n\n/* Disable all mouse interaction as long as the editor is not read–only.\n   https://github.com/ckeditor/ckeditor5-media-embed/issues/58 */\n.ck-editor__editable:not(.ck-read-only) .ck-media__wrapper > *:not(.ck-media__placeholder) {\n\tpointer-events: none;\n}\n\n/* Disable all mouse interaction when the widget is not selected (e.g. to avoid opening links by accident).\n   https://github.com/ckeditor/ckeditor5-media-embed/issues/18 */\n.ck-editor__editable:not(.ck-read-only) .ck-widget:not(.ck-widget_selected) .ck-media__placeholder {\n\tpointer-events: none;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-media-embed-placeholder-icon-size: 3em;\n\n\t--ck-color-media-embed-placeholder-url-text: hsl(0, 0%, 46%);\n\t--ck-color-media-embed-placeholder-url-text-hover: var(--ck-color-base-text);\n}\n\n.ck-media__wrapper {\n\tmargin: 0 auto;\n\n\t& .ck-media__placeholder {\n\t\tpadding: calc( 3 * var(--ck-spacing-standard) );\n\t\tbackground: var(--ck-color-base-foreground);\n\n\t\t& .ck-media__placeholder__icon {\n\t\t\tmin-width: var(--ck-media-embed-placeholder-icon-size);\n\t\t\theight: var(--ck-media-embed-placeholder-icon-size);\n\t\t\tmargin-bottom: var(--ck-spacing-large);\n\t\t\tbackground-position: center;\n\t\t\tbackground-size: cover;\n\n\t\t\t& .ck-icon {\n\t\t\t\twidth: 100%;\n\t\t\t\theight: 100%;\n\t\t\t}\n\t\t}\n\n\t\t& .ck-media__placeholder__url__text {\n\t\t\tcolor: var(--ck-color-media-embed-placeholder-url-text);\n\t\t\twhite-space: nowrap;\n\t\t\ttext-align: center;\n\t\t\tfont-style: italic;\n\t\t\ttext-overflow: ellipsis;\n\n\t\t\t&:hover {\n\t\t\t\tcolor: var(--ck-color-media-embed-placeholder-url-text-hover);\n\t\t\t\tcursor: pointer;\n\t\t\t\ttext-decoration: underline;\n\t\t\t}\n\t\t}\n\t}\n\n\t&[data-oembed-url*=\"open.spotify.com\"] {\n\t\tmax-width: 300px;\n\t\tmax-height: 380px;\n\t}\n\n\t&[data-oembed-url*=\"google.com/maps\"] .ck-media__placeholder__icon,\n\t&[data-oembed-url*=\"goo.gl/maps\"] .ck-media__placeholder__icon,\n\t&[data-oembed-url*=\"maps.google.com\"] .ck-media__placeholder__icon,\n\t&[data-oembed-url*=\"maps.app.goo.gl\"] .ck-media__placeholder__icon {\n\t\tbackground-image: url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyNTAuMzc4IiBoZWlnaHQ9IjI1NC4xNjciIHZpZXdCb3g9IjAgMCA2Ni4yNDYgNjcuMjQ4Ij48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTcyLjUzMSAtMjE4LjQ1NSkgc2NhbGUoLjk4MDEyKSI+PHJlY3Qgcnk9IjUuMjM4IiByeD0iNS4yMzgiIHk9IjIzMS4zOTkiIHg9IjE3Ni4wMzEiIGhlaWdodD0iNjAuMDk5IiB3aWR0aD0iNjAuMDk5IiBmaWxsPSIjMzRhNjY4IiBwYWludC1vcmRlcj0ibWFya2VycyBzdHJva2UgZmlsbCIvPjxwYXRoIGQ9Ik0yMDYuNDc3IDI2MC45bC0yOC45ODcgMjguOTg3YTUuMjE4IDUuMjE4IDAgMCAwIDMuNzggMS42MWg0OS42MjFjMS42OTQgMCAzLjE5LS43OTggNC4xNDYtMi4wMzd6IiBmaWxsPSIjNWM4OGM1Ii8+PHBhdGggZD0iTTIyNi43NDIgMjIyLjk4OGMtOS4yNjYgMC0xNi43NzcgNy4xNy0xNi43NzcgMTYuMDE0LjAwNyAyLjc2Mi42NjMgNS40NzQgMi4wOTMgNy44NzUuNDMuNzAzLjgzIDEuNDA4IDEuMTkgMi4xMDcuMzMzLjUwMi42NSAxLjAwNS45NSAxLjUwOC4zNDMuNDc3LjY3My45NTcuOTg4IDEuNDQgMS4zMSAxLjc2OSAyLjUgMy41MDIgMy42MzcgNS4xNjguNzkzIDEuMjc1IDEuNjgzIDIuNjQgMi40NjYgMy45OSAyLjM2MyA0LjA5NCA0LjAwNyA4LjA5MiA0LjYgMTMuOTE0di4wMTJjLjE4Mi40MTIuNTE2LjY2Ni44NzkuNjY3LjQwMy0uMDAxLjc2OC0uMzE0LjkzLS43OTkuNjAzLTUuNzU2IDIuMjM4LTkuNzI5IDQuNTg1LTEzLjc5NC43ODItMS4zNSAxLjY3My0yLjcxNSAyLjQ2NS0zLjk5IDEuMTM3LTEuNjY2IDIuMzI4LTMuNCAzLjYzOC01LjE2OS4zMTUtLjQ4Mi42NDUtLjk2Mi45ODgtMS40MzkuMy0uNTAzLjYxNy0xLjAwNi45NS0xLjUwOC4zNTktLjcuNzYtMS40MDQgMS4xOS0yLjEwNyAxLjQyNi0yLjQwMiAyLTUuMTE0IDIuMDA0LTcuODc1IDAtOC44NDQtNy41MTEtMTYuMDE0LTE2Ljc3Ni0xNi4wMTR6IiBmaWxsPSIjZGQ0YjNlIiBwYWludC1vcmRlcj0ibWFya2VycyBzdHJva2UgZmlsbCIvPjxlbGxpcHNlIHJ5PSI1LjU2NCIgcng9IjUuODI4IiBjeT0iMjM5LjAwMiIgY3g9IjIyNi43NDIiIGZpbGw9IiM4MDJkMjciIHBhaW50LW9yZGVyPSJtYXJrZXJzIHN0cm9rZSBmaWxsIi8+PHBhdGggZD0iTTE5MC4zMDEgMjM3LjI4M2MtNC42NyAwLTguNDU3IDMuODUzLTguNDU3IDguNjA2czMuNzg2IDguNjA3IDguNDU3IDguNjA3YzMuMDQzIDAgNC44MDYtLjk1OCA2LjMzNy0yLjUxNiAxLjUzLTEuNTU3IDIuMDg3LTMuOTEzIDIuMDg3LTYuMjkgMC0uMzYyLS4wMjMtLjcyMi0uMDY0LTEuMDc5aC04LjI1N3YzLjA0M2g0Ljg1Yy0uMTk3Ljc1OS0uNTMxIDEuNDUtMS4wNTggMS45ODYtLjk0Mi45NTgtMi4wMjggMS41NDgtMy45MDEgMS41NDgtMi44NzYgMC01LjIwOC0yLjM3Mi01LjIwOC01LjI5OSAwLTIuOTI2IDIuMzMyLTUuMjk5IDUuMjA4LTUuMjk5IDEuMzk5IDAgMi42MTguNDA3IDMuNTg0IDEuMjkzbDIuMzgxLTIuMzhjMC0uMDAyLS4wMDMtLjAwNC0uMDA0LS4wMDUtMS41ODgtMS41MjQtMy42Mi0yLjIxNS01Ljk1NS0yLjIxNXptNC40MyA1LjY2bC4wMDMuMDA2di0uMDAzeiIgZmlsbD0iI2ZmZiIgcGFpbnQtb3JkZXI9Im1hcmtlcnMgc3Ryb2tlIGZpbGwiLz48cGF0aCBkPSJNMjE1LjE4NCAyNTEuOTI5bC03Ljk4IDcuOTc5IDI4LjQ3NyAyOC40NzVjLjI4Ny0uNjQ5LjQ0OS0xLjM2Ni40NDktMi4xMjN2LTMxLjE2NWMtLjQ2OS42NzUtLjkzNCAxLjM0OS0xLjM4MiAyLjAwNS0uNzkyIDEuMjc1LTEuNjgyIDIuNjQtMi40NjUgMy45OS0yLjM0NyA0LjA2NS0zLjk4MiA4LjAzOC00LjU4NSAxMy43OTQtLjE2Mi40ODUtLjUyNy43OTgtLjkzLjc5OS0uMzYzLS4wMDEtLjY5Ny0uMjU1LS44NzktLjY2N3YtLjAxMmMtLjU5My01LjgyMi0yLjIzNy05LjgyLTQuNi0xMy45MTQtLjc4My0xLjM1LTEuNjczLTIuNzE1LTIuNDY2LTMuOTktMS4xMzctMS42NjYtMi4zMjctMy40LTMuNjM3LTUuMTY5bC0uMDAyLS4wMDN6IiBmaWxsPSIjYzNjM2MzIi8+PHBhdGggZD0iTTIxMi45ODMgMjQ4LjQ5NWwtMzYuOTUyIDM2Ljk1M3YuODEyYTUuMjI3IDUuMjI3IDAgMCAwIDUuMjM4IDUuMjM4aDEuMDE1bDM1LjY2Ni0zNS42NjZhMTM2LjI3NSAxMzYuMjc1IDAgMCAwLTIuNzY0LTMuOSAzNy41NzUgMzcuNTc1IDAgMCAwLS45ODktMS40NGMtLjI5OS0uNTAzLS42MTYtMS4wMDYtLjk1LTEuNTA4LS4wODMtLjE2Mi0uMTc2LS4zMjYtLjI2NC0uNDg5eiIgZmlsbD0iI2ZkZGM0ZiIgcGFpbnQtb3JkZXI9Im1hcmtlcnMgc3Ryb2tlIGZpbGwiLz48cGF0aCBkPSJNMjExLjk5OCAyNjEuMDgzbC02LjE1MiA2LjE1MSAyNC4yNjQgMjQuMjY0aC43ODFhNS4yMjcgNS4yMjcgMCAwIDAgNS4yMzktNS4yMzh2LTEuMDQ1eiIgZmlsbD0iI2ZmZiIgcGFpbnQtb3JkZXI9Im1hcmtlcnMgc3Ryb2tlIGZpbGwiLz48L2c+PC9zdmc+);\n\t}\n\n\t&[data-oembed-url*=\"facebook.com\"] .ck-media__placeholder {\n\t\tbackground: hsl(220, 46%, 48%);\n\n\t\t& .ck-media__placeholder__icon {\n\t\t\tbackground-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSIxMDI0cHgiIGhlaWdodD0iMTAyNHB4IiB2aWV3Qm94PSIwIDAgMTAyNCAxMDI0IiB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPiAgICAgICAgPHRpdGxlPkZpbGwgMTwvdGl0bGU+ICAgIDxkZXNjPkNyZWF0ZWQgd2l0aCBTa2V0Y2guPC9kZXNjPiAgICA8ZGVmcz48L2RlZnM+ICAgIDxnIGlkPSJQYWdlLTEiIHN0cm9rZT0ibm9uZSIgc3Ryb2tlLXdpZHRoPSIxIiBmaWxsPSJub25lIiBmaWxsLXJ1bGU9ImV2ZW5vZGQiPiAgICAgICAgPGcgaWQ9ImZMb2dvX1doaXRlIiBmaWxsPSIjRkZGRkZFIj4gICAgICAgICAgICA8cGF0aCBkPSJNOTY3LjQ4NCwwIEw1Ni41MTcsMCBDMjUuMzA0LDAgMCwyNS4zMDQgMCw1Ni41MTcgTDAsOTY3LjQ4MyBDMCw5OTguNjk0IDI1LjI5NywxMDI0IDU2LjUyMiwxMDI0IEw1NDcsMTAyNCBMNTQ3LDYyOCBMNDE0LDYyOCBMNDE0LDQ3MyBMNTQ3LDQ3MyBMNTQ3LDM1OS4wMjkgQzU0NywyMjYuNzY3IDYyNy43NzMsMTU0Ljc0NyA3NDUuNzU2LDE1NC43NDcgQzgwMi4yNjksMTU0Ljc0NyA4NTAuODQyLDE1OC45NTUgODY1LDE2MC44MzYgTDg2NSwyOTkgTDc4My4zODQsMjk5LjAzNyBDNzE5LjM5MSwyOTkuMDM3IDcwNywzMjkuNTI5IDcwNywzNzQuMjczIEw3MDcsNDczIEw4NjAuNDg3LDQ3MyBMODQwLjUwMSw2MjggTDcwNyw2MjggTDcwNywxMDI0IEw5NjcuNDg0LDEwMjQgQzk5OC42OTcsMTAyNCAxMDI0LDk5OC42OTcgMTAyNCw5NjcuNDg0IEwxMDI0LDU2LjUxNSBDMTAyNCwyNS4zMDMgOTk4LjY5NywwIDk2Ny40ODQsMCIgaWQ9IkZpbGwtMSI+PC9wYXRoPiAgICAgICAgPC9nPiAgICA8L2c+PC9zdmc+);\n\t\t}\n\n\t\t& .ck-media__placeholder__url__text {\n\t\t\tcolor: hsl(220, 100%, 90%);\n\n\t\t\t&:hover {\n\t\t\t\tcolor: hsl(0, 0%, 100%);\n\t\t\t}\n\t\t}\n\t}\n\n\t&[data-oembed-url*=\"instagram.com\"] .ck-media__placeholder {\n\t\tbackground: linear-gradient(-135deg,hsl(246, 100%, 39%),hsl(302, 100%, 36%),hsl(0, 100%, 48%));\n\n\t\t& .ck-media__placeholder__icon {\n\t\t\tbackground-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHdpZHRoPSI1MDRweCIgaGVpZ2h0PSI1MDRweCIgdmlld0JveD0iMCAwIDUwNCA1MDQiIHZlcnNpb249IjEuMSIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB4bWxuczp4bGluaz0iaHR0cDovL3d3dy53My5vcmcvMTk5OS94bGluayI+ICAgICAgICA8dGl0bGU+Z2x5cGgtbG9nb19NYXkyMDE2PC90aXRsZT4gICAgPGRlc2M+Q3JlYXRlZCB3aXRoIFNrZXRjaC48L2Rlc2M+ICAgIDxkZWZzPiAgICAgICAgPHBvbHlnb24gaWQ9InBhdGgtMSIgcG9pbnRzPSIwIDAuMTU5IDUwMy44NDEgMC4xNTkgNTAzLjg0MSA1MDMuOTQgMCA1MDMuOTQiPjwvcG9seWdvbj4gICAgPC9kZWZzPiAgICA8ZyBpZD0iZ2x5cGgtbG9nb19NYXkyMDE2IiBzdHJva2U9Im5vbmUiIHN0cm9rZS13aWR0aD0iMSIgZmlsbD0ibm9uZSIgZmlsbC1ydWxlPSJldmVub2RkIj4gICAgICAgIDxnIGlkPSJHcm91cC0zIj4gICAgICAgICAgICA8bWFzayBpZD0ibWFzay0yIiBmaWxsPSJ3aGl0ZSI+ICAgICAgICAgICAgICAgIDx1c2UgeGxpbms6aHJlZj0iI3BhdGgtMSI+PC91c2U+ICAgICAgICAgICAgPC9tYXNrPiAgICAgICAgICAgIDxnIGlkPSJDbGlwLTIiPjwvZz4gICAgICAgICAgICA8cGF0aCBkPSJNMjUxLjkyMSwwLjE1OSBDMTgzLjUwMywwLjE1OSAxNzQuOTI0LDAuNDQ5IDE0OC4wNTQsMS42NzUgQzEyMS4yNCwyLjg5OCAxMDIuOTI3LDcuMTU3IDg2LjkwMywxMy4zODUgQzcwLjMzNywxOS44MjIgNTYuMjg4LDI4LjQzNiA0Mi4yODIsNDIuNDQxIEMyOC4yNzcsNTYuNDQ3IDE5LjY2Myw3MC40OTYgMTMuMjI2LDg3LjA2MiBDNi45OTgsMTAzLjA4NiAyLjczOSwxMjEuMzk5IDEuNTE2LDE0OC4yMTMgQzAuMjksMTc1LjA4MyAwLDE4My42NjIgMCwyNTIuMDggQzAsMzIwLjQ5NyAwLjI5LDMyOS4wNzYgMS41MTYsMzU1Ljk0NiBDMi43MzksMzgyLjc2IDYuOTk4LDQwMS4wNzMgMTMuMjI2LDQxNy4wOTcgQzE5LjY2Myw0MzMuNjYzIDI4LjI3Nyw0NDcuNzEyIDQyLjI4Miw0NjEuNzE4IEM1Ni4yODgsNDc1LjcyMyA3MC4zMzcsNDg0LjMzNyA4Ni45MDMsNDkwLjc3NSBDMTAyLjkyNyw0OTcuMDAyIDEyMS4yNCw1MDEuMjYxIDE0OC4wNTQsNTAyLjQ4NCBDMTc0LjkyNCw1MDMuNzEgMTgzLjUwMyw1MDQgMjUxLjkyMSw1MDQgQzMyMC4zMzgsNTA0IDMyOC45MTcsNTAzLjcxIDM1NS43ODcsNTAyLjQ4NCBDMzgyLjYwMSw1MDEuMjYxIDQwMC45MTQsNDk3LjAwMiA0MTYuOTM4LDQ5MC43NzUgQzQzMy41MDQsNDg0LjMzNyA0NDcuNTUzLDQ3NS43MjMgNDYxLjU1OSw0NjEuNzE4IEM0NzUuNTY0LDQ0Ny43MTIgNDg0LjE3OCw0MzMuNjYzIDQ5MC42MTYsNDE3LjA5NyBDNDk2Ljg0Myw0MDEuMDczIDUwMS4xMDIsMzgyLjc2IDUwMi4zMjUsMzU1Ljk0NiBDNTAzLjU1MSwzMjkuMDc2IDUwMy44NDEsMzIwLjQ5NyA1MDMuODQxLDI1Mi4wOCBDNTAzLjg0MSwxODMuNjYyIDUwMy41NTEsMTc1LjA4MyA1MDIuMzI1LDE0OC4yMTMgQzUwMS4xMDIsMTIxLjM5OSA0OTYuODQzLDEwMy4wODYgNDkwLjYxNiw4Ny4wNjIgQzQ4NC4xNzgsNzAuNDk2IDQ3NS41NjQsNTYuNDQ3IDQ2MS41NTksNDIuNDQxIEM0NDcuNTUzLDI4LjQzNiA0MzMuNTA0LDE5LjgyMiA0MTYuOTM4LDEzLjM4NSBDNDAwLjkxNCw3LjE1NyAzODIuNjAxLDIuODk4IDM1NS43ODcsMS42NzUgQzMyOC45MTcsMC40NDkgMzIwLjMzOCwwLjE1OSAyNTEuOTIxLDAuMTU5IFogTTI1MS45MjEsNDUuNTUgQzMxOS4xODYsNDUuNTUgMzI3LjE1NCw0NS44MDcgMzUzLjcxOCw0Ny4wMTkgQzM3OC4yOCw0OC4xMzkgMzkxLjYxOSw1Mi4yNDMgNDAwLjQ5Niw1NS42OTMgQzQxMi4yNTUsNjAuMjYzIDQyMC42NDcsNjUuNzIyIDQyOS40NjIsNzQuNTM4IEM0MzguMjc4LDgzLjM1MyA0NDMuNzM3LDkxLjc0NSA0NDguMzA3LDEwMy41MDQgQzQ1MS43NTcsMTEyLjM4MSA0NTUuODYxLDEyNS43MiA0NTYuOTgxLDE1MC4yODIgQzQ1OC4xOTMsMTc2Ljg0NiA0NTguNDUsMTg0LjgxNCA0NTguNDUsMjUyLjA4IEM0NTguNDUsMzE5LjM0NSA0NTguMTkzLDMyNy4zMTMgNDU2Ljk4MSwzNTMuODc3IEM0NTUuODYxLDM3OC40MzkgNDUxLjc1NywzOTEuNzc4IDQ0OC4zMDcsNDAwLjY1NSBDNDQzLjczNyw0MTIuNDE0IDQzOC4yNzgsNDIwLjgwNiA0MjkuNDYyLDQyOS42MjEgQzQyMC42NDcsNDM4LjQzNyA0MTIuMjU1LDQ0My44OTYgNDAwLjQ5Niw0NDguNDY2IEMzOTEuNjE5LDQ1MS45MTYgMzc4LjI4LDQ1Ni4wMiAzNTMuNzE4LDQ1Ny4xNCBDMzI3LjE1OCw0NTguMzUyIDMxOS4xOTEsNDU4LjYwOSAyNTEuOTIxLDQ1OC42MDkgQzE4NC42NSw0NTguNjA5IDE3Ni42ODQsNDU4LjM1MiAxNTAuMTIzLDQ1Ny4xNCBDMTI1LjU2MSw0NTYuMDIgMTEyLjIyMiw0NTEuOTE2IDEwMy4zNDUsNDQ4LjQ2NiBDOTEuNTg2LDQ0My44OTYgODMuMTk0LDQzOC40MzcgNzQuMzc5LDQyOS42MjEgQzY1LjU2NCw0MjAuODA2IDYwLjEwNCw0MTIuNDE0IDU1LjUzNCw0MDAuNjU1IEM1Mi4wODQsMzkxLjc3OCA0Ny45OCwzNzguNDM5IDQ2Ljg2LDM1My44NzcgQzQ1LjY0OCwzMjcuMzEzIDQ1LjM5MSwzMTkuMzQ1IDQ1LjM5MSwyNTIuMDggQzQ1LjM5MSwxODQuODE0IDQ1LjY0OCwxNzYuODQ2IDQ2Ljg2LDE1MC4yODIgQzQ3Ljk4LDEyNS43MiA1Mi4wODQsMTEyLjM4MSA1NS41MzQsMTAzLjUwNCBDNjAuMTA0LDkxLjc0NSA2NS41NjMsODMuMzUzIDc0LjM3OSw3NC41MzggQzgzLjE5NCw2NS43MjIgOTEuNTg2LDYwLjI2MyAxMDMuMzQ1LDU1LjY5MyBDMTEyLjIyMiw1Mi4yNDMgMTI1LjU2MSw0OC4xMzkgMTUwLjEyMyw0Ny4wMTkgQzE3Ni42ODcsNDUuODA3IDE4NC42NTUsNDUuNTUgMjUxLjkyMSw0NS41NSBaIiBpZD0iRmlsbC0xIiBmaWxsPSIjRkZGRkZGIiBtYXNrPSJ1cmwoI21hc2stMikiPjwvcGF0aD4gICAgICAgIDwvZz4gICAgICAgIDxwYXRoIGQ9Ik0yNTEuOTIxLDMzNi4wNTMgQzIwNS41NDMsMzM2LjA1MyAxNjcuOTQ3LDI5OC40NTcgMTY3Ljk0NywyNTIuMDggQzE2Ny45NDcsMjA1LjcwMiAyMDUuNTQzLDE2OC4xMDYgMjUxLjkyMSwxNjguMTA2IEMyOTguMjk4LDE2OC4xMDYgMzM1Ljg5NCwyMDUuNzAyIDMzNS44OTQsMjUyLjA4IEMzMzUuODk0LDI5OC40NTcgMjk4LjI5OCwzMzYuMDUzIDI1MS45MjEsMzM2LjA1MyBaIE0yNTEuOTIxLDEyMi43MTUgQzE4MC40NzQsMTIyLjcxNSAxMjIuNTU2LDE4MC42MzMgMTIyLjU1NiwyNTIuMDggQzEyMi41NTYsMzIzLjUyNiAxODAuNDc0LDM4MS40NDQgMjUxLjkyMSwzODEuNDQ0IEMzMjMuMzY3LDM4MS40NDQgMzgxLjI4NSwzMjMuNTI2IDM4MS4yODUsMjUyLjA4IEMzODEuMjg1LDE4MC42MzMgMzIzLjM2NywxMjIuNzE1IDI1MS45MjEsMTIyLjcxNSBaIiBpZD0iRmlsbC00IiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+ICAgICAgICA8cGF0aCBkPSJNNDE2LjYyNywxMTcuNjA0IEM0MTYuNjI3LDEzNC4zIDQwMy4wOTIsMTQ3LjgzNCAzODYuMzk2LDE0Ny44MzQgQzM2OS43MDEsMTQ3LjgzNCAzNTYuMTY2LDEzNC4zIDM1Ni4xNjYsMTE3LjYwNCBDMzU2LjE2NiwxMDAuOTA4IDM2OS43MDEsODcuMzczIDM4Ni4zOTYsODcuMzczIEM0MDMuMDkyLDg3LjM3MyA0MTYuNjI3LDEwMC45MDggNDE2LjYyNywxMTcuNjA0IiBpZD0iRmlsbC01IiBmaWxsPSIjRkZGRkZGIj48L3BhdGg+ICAgIDwvZz48L3N2Zz4=);\n\t\t}\n\n\t\t/* stylelint-disable-next-line no-descending-specificity */\n\t\t& .ck-media__placeholder__url__text {\n\t\t\tcolor: hsl(302, 100%, 94%);\n\n\t\t\t&:hover {\n\t\t\t\tcolor: hsl(0, 0%, 100%);\n\t\t\t}\n\t\t}\n\t}\n\n\t&[data-oembed-url*=\"twitter.com\"] .ck.ck-media__placeholder {\n\t\t/* Use gradient to contrast with focused widget (ckeditor/ckeditor5-media-embed#22). */\n\t\tbackground: linear-gradient( to right, hsl(201, 85%, 70%), hsl(201, 85%, 35%) );\n\n\t\t& .ck-media__placeholder__icon {\n\t\t\tbackground-image: url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgaWQ9IldoaXRlIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiIHk9IjBweCIgdmlld0JveD0iMCAwIDQwMCA0MDAiIHN0eWxlPSJlbmFibGUtYmFja2dyb3VuZDpuZXcgMCAwIDQwMCA0MDA7IiB4bWw6c3BhY2U9InByZXNlcnZlIj48c3R5bGUgdHlwZT0idGV4dC9jc3MiPi5zdDB7ZmlsbDojRkZGRkZGO308L3N0eWxlPjxwYXRoIGNsYXNzPSJzdDAiIGQ9Ik00MDAsMjAwYzAsMTEwLjUtODkuNSwyMDAtMjAwLDIwMFMwLDMxMC41LDAsMjAwUzg5LjUsMCwyMDAsMFM0MDAsODkuNSw0MDAsMjAweiBNMTYzLjQsMzA1LjVjODguNywwLDEzNy4yLTczLjUsMTM3LjItMTM3LjJjMC0yLjEsMC00LjItMC4xLTYuMmM5LjQtNi44LDE3LjYtMTUuMywyNC4xLTI1Yy04LjYsMy44LTE3LjksNi40LTI3LjcsNy42YzEwLTYsMTcuNi0xNS40LDIxLjItMjYuN2MtOS4zLDUuNS0xOS42LDkuNS0zMC42LDExLjdjLTguOC05LjQtMjEuMy0xNS4yLTM1LjItMTUuMmMtMjYuNiwwLTQ4LjIsMjEuNi00OC4yLDQ4LjJjMCwzLjgsMC40LDcuNSwxLjMsMTFjLTQwLjEtMi03NS42LTIxLjItOTkuNC01MC40Yy00LjEsNy4xLTYuNSwxNS40LTYuNSwyNC4yYzAsMTYuNyw4LjUsMzEuNSwyMS41LDQwLjFjLTcuOS0wLjItMTUuMy0yLjQtMjEuOC02YzAsMC4yLDAsMC40LDAsMC42YzAsMjMuNCwxNi42LDQyLjgsMzguNyw0Ny4zYy00LDEuMS04LjMsMS43LTEyLjcsMS43Yy0zLjEsMC02LjEtMC4zLTkuMS0wLjljNi4xLDE5LjIsMjMuOSwzMy4xLDQ1LDMzLjVjLTE2LjUsMTIuOS0zNy4zLDIwLjYtNTkuOSwyMC42Yy0zLjksMC03LjctMC4yLTExLjUtMC43QzExMC44LDI5Ny41LDEzNi4yLDMwNS41LDE2My40LDMwNS41Ii8+PC9zdmc+);\n\t\t}\n\n\t\t& .ck-media__placeholder__url__text {\n\t\t\tcolor: hsl(201, 100%, 86%);\n\n\t\t\t&:hover {\n\t\t\t\tcolor: hsl(0, 0%, 100%);\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3525:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-media-form{align-items:flex-start;display:flex;flex-direction:row;flex-wrap:nowrap}.ck.ck-media-form .ck-labeled-field-view{display:inline-block}.ck.ck-media-form .ck-label{display:none}@media screen and (max-width:600px){.ck.ck-media-form{flex-wrap:wrap}.ck.ck-media-form .ck-labeled-field-view{flex-basis:100%}.ck.ck-media-form .ck-button{flex-basis:50%}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaform.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css"],"names":[],"mappings":"AAOA,kBAEC,sBAAuB,CADvB,YAAa,CAEb,kBAAmB,CACnB,gBAqBD,CAnBC,yCACC,oBACD,CAEA,4BACC,YACD,CCbA,oCDCD,kBAeE,cAUF,CARE,yCACC,eACD,CAEA,6BACC,cACD,CCtBD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck.ck-media-form {\n\tdisplay: flex;\n\talign-items: flex-start;\n\tflex-direction: row;\n\tflex-wrap: nowrap;\n\n\t& .ck-labeled-field-view {\n\t\tdisplay: inline-block;\n\t}\n\n\t& .ck-label {\n\t\tdisplay: none;\n\t}\n\n\t@mixin ck-media-phone {\n\t\tflex-wrap: wrap;\n\n\t\t& .ck-labeled-field-view {\n\t\t\tflex-basis: 100%;\n\t\t}\n\n\t\t& .ck-button {\n\t\t\tflex-basis: 50%;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 7583:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-mention-background:rgba(153,0,48,.1);--ck-color-mention-text:#990030}.ck-content .mention{background:var(--ck-color-mention-background);color:var(--ck-color-mention-text)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-mention/mention.css"],"names":[],"mappings":"AAKA,MACC,+CAAwD,CACxD,+BACD,CAEA,qBACC,6CAA8C,CAC9C,kCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-mention-background: hsla(341, 100%, 30%, 0.1);\n\t--ck-color-mention-text: hsl(341, 100%, 30%);\n}\n\n.ck-content .mention {\n\tbackground: var(--ck-color-mention-background);\n\tcolor: var(--ck-color-mention-text);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6391:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-mention-list-max-height:300px}.ck.ck-mentions{max-height:var(--ck-mention-list-max-height);overflow-x:hidden;overflow-y:auto;overscroll-behavior:contain}.ck.ck-mentions>.ck-list__item{flex-shrink:0;overflow:hidden}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-mention/theme/mentionui.css"],"names":[],"mappings":"AAKA,MACC,kCACD,CAEA,gBACC,4CAA6C,CAM7C,iBAAkB,CAJlB,eAAgB,CAMhB,2BAQD,CAJC,+BAEC,aAAc,CADd,eAED","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-mention-list-max-height: 300px;\n}\n\n.ck.ck-mentions {\n\tmax-height: var(--ck-mention-list-max-height);\n\n\toverflow-y: auto;\n\n\t/* Prevent unnecessary horizontal scrollbar in Safari\n\thttps://github.com/ckeditor/ckeditor5-mention/issues/41 */\n\toverflow-x: hidden;\n\n\toverscroll-behavior: contain;\n\n\t/* Prevent unnecessary vertical scrollbar in Safari\n\thttps://github.com/ckeditor/ckeditor5-mention/issues/41 */\n\t& > .ck-list__item {\n\t\toverflow: hidden;\n\t\tflex-shrink: 0;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6448:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content .page-break{align-items:center;clear:both;display:flex;justify-content:center;padding:5px 0;position:relative}.ck-content .page-break:after{border-bottom:2px dashed #c4c4c4;content:\"\";position:absolute;width:100%}.ck-content .page-break__label{background:#fff;border:1px solid #c4c4c4;border-radius:2px;box-shadow:2px 2px 1px rgba(0,0,0,.15);color:#333;display:block;font-family:Helvetica,Arial,Tahoma,Verdana,Sans-Serif;font-size:.75em;font-weight:700;padding:.3em .6em;position:relative;text-transform:uppercase;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;z-index:1}@media print{.ck-content .page-break{padding:0}.ck-content .page-break:after{display:none}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-page-break/theme/pagebreak.css"],"names":[],"mappings":"AAKA,wBAKC,kBAAmB,CAHnB,UAAW,CAEX,YAAa,CAEb,sBAAuB,CAHvB,aAAc,CAFd,iBAaD,CANC,8BAGC,gCAAyC,CAFzC,UAAW,CACX,iBAAkB,CAElB,UACD,CAGD,+BAYC,eAA4B,CAN5B,wBAAiC,CACjC,iBAAkB,CAMlB,sCAA6C,CAF7C,UAAsB,CAPtB,aAAc,CAId,qDAA0D,CAC1D,eAAiB,CACjB,eAAiB,CAPjB,iBAAkB,CAFlB,iBAAkB,CAIlB,wBAAyB,CAWzB,wBAAyB,CACzB,qBAAsB,CACtB,oBAAqB,CACrB,gBAAiB,CAjBjB,SAkBD,CAGA,aACC,wBACC,SAKD,CAHC,8BACC,YACD,CAEF","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content .page-break {\n\tposition: relative;\n\tclear: both;\n\tpadding: 5px 0;\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n\n\t&::after {\n\t\tcontent: '';\n\t\tposition: absolute;\n\t\tborder-bottom: 2px dashed hsl(0, 0%, 77%);\n\t\twidth: 100%;\n\t}\n}\n\n.ck-content .page-break__label {\n\tposition: relative;\n\tz-index: 1;\n\tpadding: .3em .6em;\n\tdisplay: block;\n\ttext-transform: uppercase;\n\tborder: 1px solid hsl(0, 0%, 77%);\n\tborder-radius: 2px;\n\tfont-family: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;\n\tfont-size: 0.75em;\n\tfont-weight: bold;\n\tcolor: hsl(0, 0%, 20%);\n\tbackground: hsl(0, 0%, 100%);\n\tbox-shadow: 2px 2px 1px hsla(0, 0%, 0%, 0.15);\n\n\t/* Disable the possibility to select the label text by the user. */\n\t-webkit-user-select: none;\n\t-moz-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none;\n}\n\n/* Do not show the page break element inside the print preview window. */\n@media print {\n\t.ck-content .page-break {\n\t\tpadding: 0;\n\n\t\t&::after {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 2353:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-restricted-editing-exception-background:rgba(255,169,77,.2);--ck-color-restricted-editing-exception-hover-background:rgba(255,169,77,.35);--ck-color-restricted-editing-exception-brackets:rgba(204,105,0,.4);--ck-color-restricted-editing-selected-exception-background:rgba(255,169,77,.5);--ck-color-restricted-editing-selected-exception-brackets:rgba(204,105,0,.6)}.ck-editor__editable .restricted-editing-exception{background-color:var(--ck-color-restricted-editing-exception-background);border:1px solid;border-image:linear-gradient(to right,var(--ck-color-restricted-editing-exception-brackets) 0,var(--ck-color-restricted-editing-exception-brackets) 5px,transparent 6px,transparent calc(100% - 6px),var(--ck-color-restricted-editing-exception-brackets) calc(100% - 5px),var(--ck-color-restricted-editing-exception-brackets) 100%) 1;transition:background .2s ease-in-out}.ck-editor__editable .restricted-editing-exception.restricted-editing-exception_selected{background-color:var(--ck-color-restricted-editing-selected-exception-background);border-image:linear-gradient(to right,var(--ck-color-restricted-editing-selected-exception-brackets) 0,var(--ck-color-restricted-editing-selected-exception-brackets) 5px,var(--ck-color-restricted-editing-selected-exception-brackets) calc(100% - 5px),var(--ck-color-restricted-editing-selected-exception-brackets) 100%) 1}.ck-editor__editable .restricted-editing-exception.restricted-editing-exception_collapsed{padding-left:1ch}.ck-restricted-editing_mode_restricted,.ck-restricted-editing_mode_restricted *{cursor:default}.ck-restricted-editing_mode_restricted .restricted-editing-exception,.ck-restricted-editing_mode_restricted .restricted-editing-exception *{cursor:text}.ck-restricted-editing_mode_restricted .restricted-editing-exception:hover{background:var(--ck-color-restricted-editing-exception-hover-background)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-restricted-editing/restrictedediting.css"],"names":[],"mappings":"AAKA,MACC,sEAA2E,CAC3E,6EAAkF,CAClF,mEAAyE,CACzE,+EAAoF,CACpF,4EACD,CAEA,mDAEC,wEAAyE,CACzE,gBAAiB,CACjB,yUAQG,CAXH,qCA4BD,CAfC,yFACC,iFAAkF,CAClF,gUAOD,CAEA,0FAEC,gBACD,CAQA,gFACC,cACD,CAKC,4IACC,WACD,CAEA,2EACC,wEACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-restricted-editing-exception-background: hsla(31, 100%, 65%, .2);\n\t--ck-color-restricted-editing-exception-hover-background: hsla(31, 100%, 65%, .35);\n\t--ck-color-restricted-editing-exception-brackets: hsla(31, 100%, 40%, .4);\n\t--ck-color-restricted-editing-selected-exception-background: hsla(31, 100%, 65%, .5);\n\t--ck-color-restricted-editing-selected-exception-brackets: hsla(31, 100%, 40%, .6);\n}\n\n.ck-editor__editable .restricted-editing-exception {\n\ttransition: .2s ease-in-out background;\n\tbackground-color: var(--ck-color-restricted-editing-exception-background);\n\tborder: 1px solid;\n\tborder-image: linear-gradient(\n\t\tto right,\n\t\tvar(--ck-color-restricted-editing-exception-brackets) 0%,\n\t\tvar(--ck-color-restricted-editing-exception-brackets) 5px,\n\t\thsla(0, 0%, 0%, 0) 6px,\n\t\thsla(0, 0%, 0%, 0) calc(100% - 6px),\n\t\tvar(--ck-color-restricted-editing-exception-brackets) calc(100% - 5px),\n\t\tvar(--ck-color-restricted-editing-exception-brackets) 100%\n\t) 1;\n\n\t&.restricted-editing-exception_selected {\n\t\tbackground-color: var(--ck-color-restricted-editing-selected-exception-background);\n\t\tborder-image: linear-gradient(\n\t\t\tto right,\n\t\t\tvar(--ck-color-restricted-editing-selected-exception-brackets) 0%,\n\t\t\tvar(--ck-color-restricted-editing-selected-exception-brackets) 5px,\n\t\t\tvar(--ck-color-restricted-editing-selected-exception-brackets) calc(100% - 5px),\n\t\t\tvar(--ck-color-restricted-editing-selected-exception-brackets) 100%\n\t\t) 1;\n\t}\n\n\t&.restricted-editing-exception_collapsed {\n\t\t/* Empty exception should have the same width as exception with at least 1 char */\n\t\tpadding-left: 1ch;\n\t}\n}\n\n.ck-restricted-editing_mode_restricted {\n\tcursor: default;\n\n\t/* We also have to override all elements inside the restricted editable to prevent cursor switching between default and text\n\tduring the pointer movement. */\n\t& * {\n\t\tcursor: default;\n\t}\n\n\t& .restricted-editing-exception {\n\t\tcursor: text;\n\n\t\t& * {\n\t\t\tcursor: text;\n\t\t}\n\n\t\t&:hover {\n\t\t\tbackground: var(--ck-color-restricted-editing-exception-hover-background);\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 671:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-source-editing-area{overflow:hidden;position:relative}.ck-source-editing-area textarea,.ck-source-editing-area:after{border:1px solid transparent;font-family:monospace;font-size:var(--ck-font-size-normal);line-height:var(--ck-line-height-base);margin:0;padding:var(--ck-spacing-large);white-space:pre-wrap}.ck-source-editing-area:after{content:attr(data-value) \" \";display:block;visibility:hidden}.ck-source-editing-area textarea{border-color:var(--ck-color-base-border);border-radius:0;box-sizing:border-box;height:100%;outline:none;overflow:hidden;position:absolute;resize:none;width:100%}.ck-rounded-corners .ck-source-editing-area textarea,.ck-source-editing-area textarea.ck-rounded-corners{border-radius:var(--ck-border-radius);border-top-left-radius:0;border-top-right-radius:0}.ck-source-editing-area textarea:not([readonly]):focus{border:var(--ck-focus-ring);box-shadow:var(--ck-inner-shadow),0 0;outline:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-source-editing/theme/sourceediting.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_focus.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css"],"names":[],"mappings":"AASA,wBAEC,eAAgB,CADhB,iBAED,CAEA,+DAIC,4BAA6B,CAG7B,qBAAsB,CADtB,oCAAqC,CADrC,sCAAuC,CAFvC,QAAS,CADT,+BAAgC,CAMhC,oBACD,CAEA,8BACC,4BAA6B,CAE7B,aAAc,CADd,iBAED,CAEA,iCASC,wCAAyC,CC7BzC,eAAgB,CD2BhB,qBAAsB,CAJtB,WAAY,CAEZ,YAAa,CACb,eAAgB,CALhB,iBAAkB,CAGlB,WAAY,CAFZ,UAkBD,CApBA,yGChBE,qCAAsC,CD4BtC,wBAAyB,CACzB,yBAOF,CAJC,uDEpCA,2BAA2B,CCF3B,qCAA8B,CDC9B,YFwCA","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css\";\n@import \"@ckeditor/ckeditor5-theme-lark/theme/mixins/_focus.css\";\n@import \"@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css\";\n\n.ck-source-editing-area {\n\tposition: relative;\n\toverflow: hidden;\n}\n\n.ck-source-editing-area::after,\n.ck-source-editing-area textarea {\n\tpadding: var(--ck-spacing-large);\n\tmargin: 0;\n\tborder: 1px solid transparent;\n\tline-height: var(--ck-line-height-base);\n\tfont-size: var(--ck-font-size-normal);\n\tfont-family: monospace;\n\twhite-space: pre-wrap;\n}\n\n.ck-source-editing-area::after {\n\tcontent: attr(data-value) \" \";\n\tvisibility: hidden;\n\tdisplay: block;\n}\n\n.ck-source-editing-area textarea {\n\tposition: absolute;\n\twidth: 100%;\n\theight: 100%;\n\tresize: none;\n\toutline: none;\n\toverflow: hidden;\n\tbox-sizing: border-box;\n\n\tborder-color: var(--ck-color-base-border);\n\n\t@mixin ck-rounded-corners {\n\t\tborder-top-left-radius: 0;\n\t\tborder-top-right-radius: 0;\n\t}\n\n\t&:not([readonly]):focus {\n\t\t@mixin ck-focus-ring;\n\t\t@mixin ck-box-shadow var(--ck-inner-shadow);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A visual style of focused element's border.\n */\n@define-mixin ck-focus-ring {\n\t/* Disable native outline. */\n\toutline: none;\n\tborder: var(--ck-focus-ring)\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4046:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-character-grid{max-width:100%}.ck.ck-character-grid .ck-character-grid__tiles{display:grid}:root{--ck-character-grid-tile-size:24px}.ck.ck-character-grid{max-height:200px;overflow-x:hidden;overflow-y:auto;width:350px}@media screen and (max-width:600px){.ck.ck-character-grid{width:190px}}.ck.ck-character-grid .ck-character-grid__tiles{grid-gap:var(--ck-spacing-standard);grid-template-columns:repeat(10,1fr);margin:var(--ck-spacing-standard) var(--ck-spacing-large)}@media screen and (max-width:600px){.ck.ck-character-grid .ck-character-grid__tiles{grid-template-columns:repeat(5,1fr)}}.ck.ck-character-grid .ck-character-grid__tile{border:0;font-size:1.2em;height:var(--ck-character-grid-tile-size);min-height:var(--ck-character-grid-tile-size);min-width:var(--ck-character-grid-tile-size);padding:0;transition:box-shadow .2s ease;width:var(--ck-character-grid-tile-size)}.ck.ck-character-grid .ck-character-grid__tile:focus:not(.ck-disabled),.ck.ck-character-grid .ck-character-grid__tile:hover:not(.ck-disabled){border:0;box-shadow:inset 0 0 0 1px var(--ck-color-base-background),0 0 0 2px var(--ck-color-focus-border)}.ck.ck-character-grid .ck-character-grid__tile .ck-button__label{line-height:var(--ck-character-grid-tile-size);text-align:center;width:100%}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-special-characters/theme/charactergrid.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-special-characters/charactergrid.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css"],"names":[],"mappings":"AAKA,sBACC,cAKD,CAHC,gDACC,YACD,CCFD,MACC,kCACD,CAEA,sBAIC,gBAAiB,CAFjB,iBAAkB,CADlB,eAAgB,CAEhB,WAyCD,CClDC,oCDMD,sBAOE,WAqCF,CChDC,CDcA,gDAGC,mCAAoC,CAFpC,oCAAsC,CACtC,yDAMD,CCxBA,oCDgBA,gDAME,mCAEF,CCtBA,CDwBA,+CAQC,QAAS,CAHT,eAAgB,CAHhB,yCAA0C,CAE1C,6CAA8C,CAD9C,4CAA6C,CAG7C,SAAU,CACV,8BAA+B,CAN/B,wCAsBD,CAbC,8IAGC,QAAS,CACT,iGACD,CAGA,iEACC,8CAA+C,CAE/C,iBAAkB,CADlB,UAED","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-character-grid {\n\tmax-width: 100%;\n\t\n\t& .ck-character-grid__tiles {\n\t\tdisplay: grid;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n@import \"../mixins/_rounded.css\";\n\n:root {\n\t--ck-character-grid-tile-size: 24px;\n}\n\n.ck.ck-character-grid {\n\toverflow-y: auto;\n\toverflow-x: hidden;\n\twidth: 350px;\n\tmax-height: 200px;\n\n\t@mixin ck-media-phone {\n\t\twidth: 190px;\n\t}\n\n\t& .ck-character-grid__tiles {\n\t\tgrid-template-columns: repeat(10, 1fr);\n\t\tmargin: var(--ck-spacing-standard) var(--ck-spacing-large);\n\t\tgrid-gap: var(--ck-spacing-standard);\n\n\t\t@mixin ck-media-phone {\n\t\t\tgrid-template-columns: repeat(5, 1fr);\n\t\t}\n\t}\n\n\t& .ck-character-grid__tile {\n\t\twidth: var(--ck-character-grid-tile-size);\n\t\theight: var(--ck-character-grid-tile-size);\n\t\tmin-width: var(--ck-character-grid-tile-size);\n\t\tmin-height: var(--ck-character-grid-tile-size);\n\t\tfont-size: 1.2em;\n\t\tpadding: 0;\n\t\ttransition: .2s ease box-shadow;\n\t\tborder: 0;\n\n\t\t&:focus:not( .ck-disabled ),\n\t\t&:hover:not( .ck-disabled ) {\n\t\t\t/* Disable the default .ck-button's border ring. */\n\t\t\tborder: 0;\n\t\t\tbox-shadow: inset 0 0 0 1px var(--ck-color-base-background), 0 0 0 2px var(--ck-color-focus-border);\n\t\t}\n\n\t\t/* Make sure the glyph is rendered in the center of the button */\n\t\t& .ck-button__label {\n\t\t\tline-height: var(--ck-character-grid-tile-size);\n\t\t\twidth: 100%;\n\t\t\ttext-align: center;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4779:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-character-info{border-top:1px solid var(--ck-color-base-border);display:flex;justify-content:space-between;padding:var(--ck-spacing-small) var(--ck-spacing-large)}.ck.ck-character-info>*{font-size:var(--ck-font-size-small);text-transform:uppercase}.ck.ck-character-info .ck-character-info__name{max-width:280px;overflow:hidden;text-overflow:ellipsis}.ck.ck-character-info .ck-character-info__code{opacity:.6}@media screen and (max-width:600px){.ck.ck-character-info{max-width:190px}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-special-characters/theme/characterinfo.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-special-characters/characterinfo.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css"],"names":[],"mappings":"AAKA,sBCIC,gDAAiD,CDHjD,YAAa,CACb,6BAA8B,CCC9B,uDDAD,CCGC,wBAEC,mCAAoC,CADpC,wBAED,CAEA,+CACC,eAAgB,CAEhB,eAAgB,CADhB,sBAED,CAEA,+CACC,UACD,CClBA,oCDCD,sBAoBE,eAEF,CCrBC","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-character-info {\n\tdisplay: flex;\n\tjustify-content: space-between;\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck.ck-character-info {\n\tpadding: var(--ck-spacing-small) var(--ck-spacing-large);\n\tborder-top: 1px solid var(--ck-color-base-border);\n\n\t& > * {\n\t\ttext-transform: uppercase;\n\t\tfont-size: var(--ck-font-size-small);\n\t}\n\n\t& .ck-character-info__name {\n\t\tmax-width: 280px;\n\t\ttext-overflow: ellipsis;\n\t\toverflow: hidden;\n\t}\n\n\t& .ck-character-info__code {\n\t\topacity: .6;\n\t}\n\n\t@mixin ck-media-phone {\n\t\tmax-width: 190px;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8170:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-special-characters-navigation>.ck-label{max-width:160px;overflow:hidden;text-overflow:ellipsis}.ck.ck-special-characters-navigation>.ck-dropdown .ck-dropdown__panel{max-height:250px;overflow-x:hidden;overflow-y:auto}@media screen and (max-width:600px){.ck.ck-special-characters-navigation{max-width:190px}.ck.ck-special-characters-navigation>.ck-form__header__label{overflow:hidden;text-overflow:ellipsis}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-special-characters/specialcharacters.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css"],"names":[],"mappings":"AAUC,+CACC,eAAgB,CAEhB,eAAgB,CADhB,sBAED,CAEA,sEAEC,gBAAiB,CAEjB,iBAAkB,CADlB,eAED,CCfA,oCDED,qCAgBE,eAOF,CALE,6DAEC,eAAgB,CADhB,sBAED,CCrBD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck.ck-special-characters-navigation {\n\n\t& > .ck-label {\n\t\tmax-width: 160px;\n\t\ttext-overflow: ellipsis;\n\t\toverflow: hidden;\n\t}\n\n\t& > .ck-dropdown .ck-dropdown__panel {\n\t\t/* There could be dozens of categories available. Use scroll to prevent a 10e6px dropdown. */\n\t\tmax-height: 250px;\n\t\toverflow-y: auto;\n\t\toverflow-x: hidden;\n\t}\n\n\t@mixin ck-media-phone {\n\t\tmax-width: 190px;\n\n\t\t& > .ck-form__header__label {\n\t\t\ttext-overflow: ellipsis;\n\t\t\toverflow: hidden;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4082:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-input-color{display:flex;flex-direction:row-reverse;width:100%}.ck.ck-input-color>input.ck.ck-input-text{flex-grow:1;min-width:auto}.ck.ck-input-color>div.ck.ck-dropdown{min-width:auto}.ck.ck-input-color>div.ck.ck-dropdown>.ck-input-color__button .ck-dropdown__arrow{display:none}.ck.ck-input-color .ck.ck-input-color__button{display:flex}.ck.ck-input-color .ck.ck-input-color__button .ck.ck-input-color__button__preview{overflow:hidden;position:relative}.ck.ck-input-color .ck.ck-input-color__button .ck.ck-input-color__button__preview>.ck.ck-input-color__button__preview__no-color-indicator{display:block;position:absolute}[dir=ltr] .ck.ck-input-color>.ck.ck-input-text{border-bottom-right-radius:0;border-top-right-radius:0}[dir=rtl] .ck.ck-input-color>.ck.ck-input-text{border-bottom-left-radius:0;border-top-left-radius:0}.ck.ck-input-color>.ck.ck-input-text:focus{z-index:0}.ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button{padding:0}[dir=ltr] .ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button{border-bottom-left-radius:0;border-top-left-radius:0}[dir=ltr] .ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button:not(:focus){border-left:1px solid transparent}[dir=rtl] .ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button{border-bottom-right-radius:0;border-top-right-radius:0}[dir=rtl] .ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button:not(:focus){border-right:1px solid transparent}.ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button.ck-disabled{background:var(--ck-color-input-disabled-background)}.ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button>.ck.ck-input-color__button__preview{border-radius:0}.ck-rounded-corners .ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button>.ck.ck-input-color__button__preview,.ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button>.ck.ck-input-color__button__preview.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button>.ck.ck-input-color__button__preview{border:1px solid var(--ck-color-input-border);height:20px;width:20px}.ck.ck-input-color>.ck.ck-dropdown>.ck.ck-button.ck-input-color__button>.ck.ck-input-color__button__preview>.ck.ck-input-color__button__preview__no-color-indicator{background:red;border-radius:2px;height:150%;left:50%;top:-30%;transform:rotate(45deg);transform-origin:50%;width:8%}.ck.ck-input-color .ck.ck-input-color__remove-color{border-bottom-left-radius:0;border-bottom-right-radius:0;padding:calc(var(--ck-spacing-standard)/2) var(--ck-spacing-standard);width:100%}.ck.ck-input-color .ck.ck-input-color__remove-color:not(:focus){border-bottom:1px solid var(--ck-color-input-border)}[dir=ltr] .ck.ck-input-color .ck.ck-input-color__remove-color{border-top-right-radius:0}[dir=rtl] .ck.ck-input-color .ck.ck-input-color__remove-color{border-top-left-radius:0}.ck.ck-input-color .ck.ck-input-color__remove-color .ck.ck-icon{margin-right:var(--ck-spacing-standard)}[dir=rtl] .ck.ck-input-color .ck.ck-input-color__remove-color .ck.ck-icon{margin-left:var(--ck-spacing-standard);margin-right:0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/colorinput.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/colorinput.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAKA,mBAEC,YAAa,CACb,0BAA2B,CAF3B,UAgCD,CA5BC,0CAEC,WAAY,CADZ,cAED,CAEA,sCACC,cAMD,CAHC,kFACC,YACD,CAGD,8CAEC,YAWD,CATC,kFAEC,eAAgB,CADhB,iBAOD,CAJC,0IAEC,aAAc,CADd,iBAED,CC1BF,+CAGE,4BAA6B,CAD7B,yBAcF,CAhBA,+CAQE,2BAA4B,CAD5B,wBASF,CAHC,2CACC,SACD,CAIA,wEACC,SA0CD,CA3CA,kFAKE,2BAA4B,CAD5B,wBAuCF,CApCE,8FACC,iCACD,CATF,kFAcE,4BAA6B,CAD7B,yBA8BF,CA3BE,8FACC,kCACD,CAGD,oFACC,oDACD,CAEA,4GC1CF,eD2DE,CAjBA,+PCtCD,qCDuDC,CAjBA,4GAKC,6CAA8C,CAD9C,WAAY,CADZ,UAcD,CAVC,oKAKC,cAA6B,CAC7B,iBAAkB,CAHlB,WAAY,CADZ,QAAS,CADT,QAAS,CAMT,uBAAwB,CACxB,oBAAqB,CAJrB,QAKD,CAKH,oDAIC,2BAA4B,CAC5B,4BAA6B,CAH7B,qEAAwE,CADxE,UA0BD,CApBC,gEACC,oDACD,CATD,8DAYE,yBAeF,CA3BA,8DAgBE,wBAWF,CARC,gEACC,uCAMD,CAPA,0EAKE,sCAAuC,CADvC,cAGF","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-input-color {\n\twidth: 100%;\n\tdisplay: flex;\n\tflex-direction: row-reverse;\n\n\t& > input.ck.ck-input-text {\n\t\tmin-width: auto;\n\t\tflex-grow: 1;\n\t}\n\n\t& > div.ck.ck-dropdown {\n\t\tmin-width: auto;\n\n\t\t/* This dropdown has no arrow but a color preview instead. */\n\t\t& > .ck-input-color__button .ck-dropdown__arrow {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t& .ck.ck-input-color__button {\n\t\t/* Resolving issue with misaligned buttons on Safari (see #10589) */\n\t\tdisplay: flex;\n\n\t\t& .ck.ck-input-color__button__preview {\n\t\t\tposition: relative;\n\t\t\toverflow: hidden;\n\n\t\t\t& > .ck.ck-input-color__button__preview__no-color-indicator {\n\t\t\t\tposition: absolute;\n\t\t\t\tdisplay: block;\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n@import \"../mixins/_rounded.css\";\n\n.ck.ck-input-color {\n\t& > .ck.ck-input-text {\n\t\t@mixin ck-dir ltr {\n\t\t\tborder-top-right-radius: 0;\n\t\t\tborder-bottom-right-radius: 0;\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\tborder-top-left-radius: 0;\n\t\t\tborder-bottom-left-radius: 0;\n\t\t}\n\n\t\t/* Make sure the focused input is always on top of the dropdown button so its\n\t\t   outline and border are never cropped (also when the input is read-only). */\n\t\t&:focus {\n\t\t\tz-index: 0;\n\t\t}\n\t}\n\n\t& > .ck.ck-dropdown {\n\t\t& > .ck.ck-button.ck-input-color__button {\n\t\t\tpadding: 0;\n\n\t\t\t@mixin ck-dir ltr {\n\t\t\t\tborder-top-left-radius: 0;\n\t\t\t\tborder-bottom-left-radius: 0;\n\n\t\t\t\t&:not(:focus) {\n\t\t\t\t\tborder-left: 1px solid transparent;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@mixin ck-dir rtl {\n\t\t\t\tborder-top-right-radius: 0;\n\t\t\t\tborder-bottom-right-radius: 0;\n\n\t\t\t\t&:not(:focus) {\n\t\t\t\t\tborder-right: 1px solid transparent;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t&.ck-disabled {\n\t\t\t\tbackground: var(--ck-color-input-disabled-background);\n\t\t\t}\n\n\t\t\t& > .ck.ck-input-color__button__preview {\n\t\t\t\t@mixin ck-rounded-corners;\n\n\t\t\t\twidth: 20px;\n\t\t\t\theight: 20px;\n\t\t\t\tborder: 1px solid var(--ck-color-input-border);\n\n\t\t\t\t& > .ck.ck-input-color__button__preview__no-color-indicator {\n\t\t\t\t\ttop: -30%;\n\t\t\t\t\tleft: 50%;\n\t\t\t\t\theight: 150%;\n\t\t\t\t\twidth: 8%;\n\t\t\t\t\tbackground: hsl(0, 100%, 50%);\n\t\t\t\t\tborder-radius: 2px;\n\t\t\t\t\ttransform: rotate(45deg);\n\t\t\t\t\ttransform-origin: 50%;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t& .ck.ck-input-color__remove-color {\n\t\twidth: 100%;\n\t\tpadding: calc(var(--ck-spacing-standard) / 2) var(--ck-spacing-standard);\n\n\t\tborder-bottom-left-radius: 0;\n\t\tborder-bottom-right-radius: 0;\n\n\t\t&:not(:focus) {\n\t\t\tborder-bottom: 1px solid var(--ck-color-input-border);\n\t\t}\n\n\t\t@mixin ck-dir ltr {\n\t\t\tborder-top-right-radius: 0;\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\tborder-top-left-radius: 0;\n\t\t}\n\n\t\t& .ck.ck-icon {\n\t\t\tmargin-right: var(--ck-spacing-standard);\n\n\t\t\t@mixin ck-dir rtl {\n\t\t\t\tmargin-right: 0;\n\t\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4880:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-form{padding:0 0 var(--ck-spacing-large)}.ck.ck-form:focus{outline:none}.ck.ck-form .ck.ck-input-text{min-width:100%;width:0}.ck.ck-form .ck.ck-dropdown{min-width:100%}.ck.ck-form .ck.ck-dropdown .ck-dropdown__button:not(:focus){border:1px solid var(--ck-color-base-border)}.ck.ck-form .ck.ck-dropdown .ck-dropdown__button .ck-button__label{width:100%}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/form.css"],"names":[],"mappings":"AAKA,YACC,mCAyBD,CAvBC,kBAEC,YACD,CAEA,8BACC,cAAe,CACf,OACD,CAEA,4BACC,cAWD,CARE,6DACC,4CACD,CAEA,mEACC,UACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-form {\n\tpadding: 0 0 var(--ck-spacing-large);\n\n\t&:focus {\n\t\t/* See: https://github.com/ckeditor/ckeditor5/issues/4773 */\n\t\toutline: none;\n\t}\n\n\t& .ck.ck-input-text {\n\t\tmin-width: 100%;\n\t\twidth: 0;\n\t}\n\n\t& .ck.ck-dropdown {\n\t\tmin-width: 100%;\n\n\t\t& .ck-dropdown__button {\n\t\t\t&:not(:focus) {\n\t\t\t\tborder: 1px solid var(--ck-color-base-border);\n\t\t\t}\n\n\t\t\t& .ck-button__label {\n\t\t\t\twidth: 100%;\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9865:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-form__row{display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:space-between}.ck.ck-form__row>:not(.ck-label){flex-grow:1}.ck.ck-form__row.ck-table-form__action-row .ck-button-cancel,.ck.ck-form__row.ck-table-form__action-row .ck-button-save{justify-content:center}.ck.ck-form__row{padding:var(--ck-spacing-standard) var(--ck-spacing-large) 0}[dir=ltr] .ck.ck-form__row>:not(.ck-label)+*{margin-left:var(--ck-spacing-large)}[dir=rtl] .ck.ck-form__row>:not(.ck-label)+*{margin-right:var(--ck-spacing-large)}.ck.ck-form__row>.ck-label{min-width:100%;width:100%}.ck.ck-form__row.ck-table-form__action-row{margin-top:var(--ck-spacing-large)}.ck.ck-form__row.ck-table-form__action-row .ck-button .ck-button__label{color:var(--ck-color-text)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/formrow.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/formrow.css"],"names":[],"mappings":"AAKA,iBACC,YAAa,CACb,kBAAmB,CACnB,gBAAiB,CACjB,6BAaD,CAVC,iCACC,WACD,CAGC,wHAEC,sBACD,CCbF,iBACC,4DA2BD,CAvBE,6CAEE,mCAMF,CARA,6CAME,oCAEF,CAGD,2BAEC,cAAe,CADf,UAED,CAEA,2CACC,kCAKD,CAHC,wEACC,0BACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-form__row {\n\tdisplay: flex;\n\tflex-direction: row;\n\tflex-wrap: nowrap;\n\tjustify-content: space-between;\n\n\t/* Ignore labels that work as fieldset legends */\n\t& > *:not(.ck-label) {\n\t\tflex-grow: 1;\n\t}\n\n\t&.ck-table-form__action-row {\n\t\t& .ck-button-save,\n\t\t& .ck-button-cancel {\n\t\t\tjustify-content: center;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n.ck.ck-form__row {\n\tpadding: var(--ck-spacing-standard) var(--ck-spacing-large) 0;\n\n\t/* Ignore labels that work as fieldset legends */\n\t& > *:not(.ck-label) {\n\t\t& + * {\n\t\t\t@mixin ck-dir ltr {\n\t\t\t\tmargin-left: var(--ck-spacing-large);\n\t\t\t}\n\n\t\t\t@mixin ck-dir rtl {\n\t\t\t\tmargin-right: var(--ck-spacing-large);\n\t\t\t}\n\t\t}\n\t}\n\n\t& > .ck-label {\n\t\twidth: 100%;\n\t\tmin-width: 100%;\n\t}\n\n\t&.ck-table-form__action-row {\n\t\tmargin-top: var(--ck-spacing-large);\n\n\t\t& .ck-button .ck-button__label {\n\t\t\tcolor: var(--ck-color-text);\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8085:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-insert-table-dropdown__grid{display:flex;flex-direction:row;flex-wrap:wrap}:root{--ck-insert-table-dropdown-padding:10px;--ck-insert-table-dropdown-box-height:11px;--ck-insert-table-dropdown-box-width:12px;--ck-insert-table-dropdown-box-margin:1px}.ck .ck-insert-table-dropdown__grid{padding:var(--ck-insert-table-dropdown-padding) var(--ck-insert-table-dropdown-padding) 0;width:calc(var(--ck-insert-table-dropdown-box-width)*10 + var(--ck-insert-table-dropdown-box-margin)*20 + var(--ck-insert-table-dropdown-padding)*2)}.ck .ck-insert-table-dropdown__label{text-align:center}.ck .ck-insert-table-dropdown-grid-box{border:1px solid var(--ck-color-base-border);border-radius:1px;margin:var(--ck-insert-table-dropdown-box-margin);min-height:var(--ck-insert-table-dropdown-box-height);min-width:var(--ck-insert-table-dropdown-box-width);outline:none;transition:none}.ck .ck-insert-table-dropdown-grid-box:focus{box-shadow:none}.ck .ck-insert-table-dropdown-grid-box.ck-on{background:var(--ck-color-focus-outer-shadow);border-color:var(--ck-color-focus-border)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/inserttable.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/inserttable.css"],"names":[],"mappings":"AAKA,oCACC,YAAa,CACb,kBAAmB,CACnB,cACD,CCJA,MACC,uCAAwC,CACxC,0CAA2C,CAC3C,yCAA0C,CAC1C,yCACD,CAEA,oCAGC,yFAA0F,CAD1F,oJAED,CAEA,qCACC,iBACD,CAEA,uCAIC,4CAA6C,CAC7C,iBAAkB,CAFlB,iDAAkD,CADlD,qDAAsD,CADtD,mDAAoD,CAKpD,YAAa,CACb,eAUD,CARC,6CACC,eACD,CAEA,6CAEC,6CAA8C,CAD9C,yCAED","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck .ck-insert-table-dropdown__grid {\n\tdisplay: flex;\n\tflex-direction: row;\n\tflex-wrap: wrap;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-insert-table-dropdown-padding: 10px;\n\t--ck-insert-table-dropdown-box-height: 11px;\n\t--ck-insert-table-dropdown-box-width: 12px;\n\t--ck-insert-table-dropdown-box-margin: 1px;\n}\n\n.ck .ck-insert-table-dropdown__grid {\n\t/* The width of a container should match 10 items in a row so there will be a 10x10 grid. */\n\twidth: calc(var(--ck-insert-table-dropdown-box-width) * 10 + var(--ck-insert-table-dropdown-box-margin) * 20 + var(--ck-insert-table-dropdown-padding) * 2);\n\tpadding: var(--ck-insert-table-dropdown-padding) var(--ck-insert-table-dropdown-padding) 0;\n}\n\n.ck .ck-insert-table-dropdown__label {\n\ttext-align: center;\n}\n\n.ck .ck-insert-table-dropdown-grid-box {\n\tmin-width: var(--ck-insert-table-dropdown-box-width);\n\tmin-height: var(--ck-insert-table-dropdown-box-height);\n\tmargin: var(--ck-insert-table-dropdown-box-margin);\n\tborder: 1px solid var(--ck-color-base-border);\n\tborder-radius: 1px;\n\toutline: none;\n\ttransition: none;\n\n\t&:focus {\n\t\tbox-shadow: none;\n\t}\n\n\t&.ck-on {\n\t\tborder-color: var(--ck-color-focus-border);\n\t\tbackground: var(--ck-color-focus-outer-shadow);\n\t}\n}\n\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4104:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-content .table{display:table;margin:.9em auto}.ck-content .table table{border:1px double #b3b3b3;border-collapse:collapse;border-spacing:0;height:100%;width:100%}.ck-content .table table td,.ck-content .table table th{border:1px solid #bfbfbf;min-width:2em;padding:.4em}.ck-content .table table th{background:rgba(0,0,0,.05);font-weight:700}.ck-content[dir=rtl] .table th{text-align:right}.ck-content[dir=ltr] .table th{text-align:left}.ck-editor__editable .ck-table-bogus-paragraph{display:inline-block;width:100%}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/table.css"],"names":[],"mappings":"AAKA,mBAKC,aAAc,CADd,gBAiCD,CA9BC,yBAYC,yBAAkC,CAVlC,wBAAyB,CACzB,gBAAiB,CAKjB,WAAY,CADZ,UAsBD,CAfC,wDAQC,wBAAiC,CANjC,aAAc,CACd,YAMD,CAEA,4BAEC,0BAA+B,CAD/B,eAED,CAMF,+BACC,gBACD,CAEA,+BACC,eACD,CAEA,+CAKC,oBAAqB,CAMrB,UACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck-content .table {\n\t/* Give the table widget some air and center it horizontally */\n\t/* The first value should be equal to --ck-spacing-large variable if used in the editor context\n\tto avoid the content jumping (See https://github.com/ckeditor/ckeditor5/issues/9825). */\n\tmargin: 0.9em auto;\n\tdisplay: table;\n\n\t& table {\n\t\t/* The table cells should have slight borders */\n\t\tborder-collapse: collapse;\n\t\tborder-spacing: 0;\n\n\t\t/* Table width and height are set on the parent <figure>. Make sure the table inside stretches\n\t\tto the full dimensions of the container (https://github.com/ckeditor/ckeditor5/issues/6186). */\n\t\twidth: 100%;\n\t\theight: 100%;\n\n\t\t/* The outer border of the table should be slightly darker than the inner lines.\n\t\tAlso see https://github.com/ckeditor/ckeditor5-table/issues/50. */\n\t\tborder: 1px double hsl(0, 0%, 70%);\n\n\t\t& td,\n\t\t& th {\n\t\t\tmin-width: 2em;\n\t\t\tpadding: .4em;\n\n\t\t\t/* The border is inherited from .ck-editor__nested-editable styles, so theoretically it's not necessary here.\n\t\t\tHowever, the border is a content style, so it should use .ck-content (so it works outside the editor).\n\t\t\tHence, the duplication. See https://github.com/ckeditor/ckeditor5/issues/6314 */\n\t\t\tborder: 1px solid hsl(0, 0%, 75%);\n\t\t}\n\n\t\t& th {\n\t\t\tfont-weight: bold;\n\t\t\tbackground: hsla(0, 0%, 0%, 5%);\n\t\t}\n\t}\n}\n\n/* Text alignment of the table header should match the editor settings and override the native browser styling,\nwhen content is available outside the editor. See https://github.com/ckeditor/ckeditor5/issues/6638 */\n.ck-content[dir=\"rtl\"] .table th {\n\ttext-align: right;\n}\n\n.ck-content[dir=\"ltr\"] .table th {\n\ttext-align: left;\n}\n\n.ck-editor__editable .ck-table-bogus-paragraph {\n\t/*\n\t * Use display:inline-block to force Chrome/Safari to limit text mutations to this element.\n\t * See https://github.com/ckeditor/ckeditor5/issues/6062.\n\t */\n\tdisplay: inline-block;\n\n\t/*\n\t * Inline HTML elements nested in the span should always be dimensioned in relation to the whole cell width.\n\t * See https://github.com/ckeditor/ckeditor5/issues/9117.\n\t */\n\twidth: 100%;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9888:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-table-caption-background:#f7f7f7;--ck-color-table-caption-text:#333;--ck-color-table-caption-highlighted-background:#fd0}.ck-content .table>figcaption{background-color:var(--ck-color-table-caption-background);caption-side:top;color:var(--ck-color-table-caption-text);display:table-caption;font-size:.75em;outline-offset:-1px;padding:.6em;text-align:center;word-break:break-word}.ck.ck-editor__editable .table>figcaption.table__caption_highlighted{animation:ck-table-caption-highlight .6s ease-out}.ck.ck-editor__editable .table>figcaption.ck-placeholder:before{overflow:hidden;padding-left:inherit;padding-right:inherit;text-overflow:ellipsis;white-space:nowrap}@keyframes ck-table-caption-highlight{0%{background-color:var(--ck-color-table-caption-highlighted-background)}to{background-color:var(--ck-color-table-caption-background)}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/tablecaption.css"],"names":[],"mappings":"AAKA,MACC,2CAAoD,CACpD,kCAA8C,CAC9C,oDACD,CAGA,8BAMC,yDAA0D,CAJ1D,gBAAiB,CAGjB,wCAAyC,CAJzC,qBAAsB,CAOtB,eAAgB,CAChB,mBAAoB,CAFpB,YAAa,CAHb,iBAAkB,CADlB,qBAOD,CAIC,qEACC,iDACD,CAEA,gEASC,eAAgB,CARhB,oBAAqB,CACrB,qBAAsB,CAQtB,sBAAuB,CAFvB,kBAGD,CAGD,sCACC,GACC,qEACD,CAEA,GACC,yDACD,CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-table-caption-background: hsl(0, 0%, 97%);\n\t--ck-color-table-caption-text: hsl(0, 0%, 20%);\n\t--ck-color-table-caption-highlighted-background: hsl(52deg 100% 50%);\n}\n\n/* Content styles */\n.ck-content .table > figcaption {\n\tdisplay: table-caption;\n\tcaption-side: top;\n\tword-break: break-word;\n\ttext-align: center;\n\tcolor: var(--ck-color-table-caption-text);\n\tbackground-color: var(--ck-color-table-caption-background);\n\tpadding: .6em;\n\tfont-size: .75em;\n\toutline-offset: -1px;\n}\n\n/* Editing styles */\n.ck.ck-editor__editable .table > figcaption {\n\t&.table__caption_highlighted {\n\t\tanimation: ck-table-caption-highlight .6s ease-out;\n\t}\n\n\t&.ck-placeholder::before {\n\t\tpadding-left: inherit;\n\t\tpadding-right: inherit;\n\n\t\t/*\n\t\t * Make sure the table caption placeholder doesn't overflow the placeholder area.\n\t\t * See https://github.com/ckeditor/ckeditor5/issues/9162.\n\t\t */\n\t\twhite-space: nowrap;\n\t\toverflow: hidden;\n\t\ttext-overflow: ellipsis;\n\t}\n}\n\n@keyframes ck-table-caption-highlight {\n\t0% {\n\t\tbackground-color: var(--ck-color-table-caption-highlighted-background);\n\t}\n\n\t100% {\n\t\tbackground-color: var(--ck-color-table-caption-background);\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5737:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-table-cell-properties-form .ck-form__row.ck-table-cell-properties-form__alignment-row{flex-wrap:wrap}.ck.ck-table-cell-properties-form .ck-form__row.ck-table-cell-properties-form__alignment-row .ck.ck-toolbar:first-of-type{flex-grow:0.57}.ck.ck-table-cell-properties-form .ck-form__row.ck-table-cell-properties-form__alignment-row .ck.ck-toolbar:last-of-type{flex-grow:0.43}.ck.ck-table-cell-properties-form .ck-form__row.ck-table-cell-properties-form__alignment-row .ck.ck-toolbar .ck-button{flex-grow:1}.ck.ck-table-cell-properties-form{width:320px}.ck.ck-table-cell-properties-form .ck-form__row.ck-table-cell-properties-form__padding-row{align-self:flex-end;padding:0;width:25%}.ck.ck-table-cell-properties-form .ck-form__row.ck-table-cell-properties-form__alignment-row .ck.ck-toolbar{background:none;margin-top:var(--ck-spacing-standard)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/tablecellproperties.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/tablecellproperties.css"],"names":[],"mappings":"AAOE,6FACC,cAiBD,CAdE,0HAEC,cACD,CAEA,yHAEC,cACD,CAEA,uHACC,WACD,CClBJ,kCACC,WAkBD,CAfE,2FACC,mBAAoB,CACpB,SAAU,CACV,SACD,CAGC,4GACC,eAAgB,CAGhB,qCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-table-cell-properties-form {\n\t& .ck-form__row {\n\t\t&.ck-table-cell-properties-form__alignment-row {\n\t\t\tflex-wrap: wrap;\n\n\t\t\t& .ck.ck-toolbar {\n\t\t\t\t&:first-of-type {\n\t\t\t\t\t/* 4 buttons out of 7 (h-alignment + v-alignment) = 0.57 */\n\t\t\t\t\tflex-grow: 0.57;\n\t\t\t\t}\n\n\t\t\t\t&:last-of-type {\n\t\t\t\t\t/* 3 buttons out of 7 (h-alignment + v-alignment) = 0.43 */\n\t\t\t\t\tflex-grow: 0.43;\n\t\t\t\t}\n\n\t\t\t\t& .ck-button {\n\t\t\t\t\tflex-grow: 1;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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.ck.ck-table-cell-properties-form {\n\twidth: 320px;\n\n\t& .ck-form__row {\n\t\t&.ck-table-cell-properties-form__padding-row {\n\t\t\talign-self: flex-end;\n\t\t\tpadding: 0;\n\t\t\twidth: 25%;\n\t\t}\n\n\t\t&.ck-table-cell-properties-form__alignment-row {\n\t\t\t& .ck.ck-toolbar {\n\t\t\t\tbackground: none;\n\n\t\t\t\t/* Compensate for missing input label that would push the margin (toolbar has no inputs). */\n\t\t\t\tmargin-top: var(--ck-spacing-standard);\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 728:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-table-column-resizer-hover:var(--ck-color-base-active);--ck-table-column-resizer-width:7px;--ck-table-column-resizer-position-offset:calc(var(--ck-table-column-resizer-width)*-0.5 - 0.5px)}.ck-content .table .ck-table-resized{table-layout:fixed}.ck-content .table table{overflow:hidden}.ck-content .table td,.ck-content .table th{position:relative}.ck.ck-editor__editable .table .ck-table-column-resizer{bottom:-999999px;cursor:col-resize;position:absolute;right:var(--ck-table-column-resizer-position-offset);top:-999999px;user-select:none;width:var(--ck-table-column-resizer-width);z-index:var(--ck-z-default)}.ck.ck-editor__editable .table[draggable] .ck-table-column-resizer,.ck.ck-editor__editable.ck-column-resize_disabled .table .ck-table-column-resizer{display:none}.ck.ck-editor__editable .table .ck-table-column-resizer:hover,.ck.ck-editor__editable .table .ck-table-column-resizer__active{background-color:var(--ck-color-table-column-resizer-hover);opacity:.25}.ck.ck-editor__editable[dir=rtl] .table .ck-table-column-resizer{left:var(--ck-table-column-resizer-position-offset);right:unset}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/tablecolumnresize.css"],"names":[],"mappings":"AAKA,MACC,iEAAkE,CAClE,mCAAoC,CAIpC,iGACD,CAEA,qCACC,kBACD,CAEA,yBACC,eACD,CAEA,4CAEC,iBACD,CAEA,wDAOC,gBAAiB,CAGjB,iBAAkB,CATlB,iBAAkB,CAOlB,oDAAqD,CAFrD,aAAc,CAKd,gBAAiB,CAFjB,0CAA2C,CAG3C,2BACD,CAQA,qJACC,YACD,CAEA,8HAEC,2DAA4D,CAC5D,WACD,CAEA,iEACC,mDAAoD,CACpD,WACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-table-column-resizer-hover: var(--ck-color-base-active);\n\t--ck-table-column-resizer-width: 7px;\n\n\t/* The offset used for absolute positioning of the resizer element, so that it is placed exactly above the cell border.\n\t   The value is: minus half the width of the resizer decreased additionaly by the half the width of the border (0.5px). */\n\t--ck-table-column-resizer-position-offset: calc(var(--ck-table-column-resizer-width) * -0.5 - 0.5px);\n}\n\n.ck-content .table .ck-table-resized {\n\ttable-layout: fixed;\n}\n\n.ck-content .table table {\n\toverflow: hidden;\n}\n\n.ck-content .table td,\n.ck-content .table th {\n\tposition: relative;\n}\n\n.ck.ck-editor__editable .table .ck-table-column-resizer {\n\tposition: absolute;\n\t/* The resizer element resides in each cell so to occupy the entire height of the table, which is unknown from a CSS point of view,\n\t   it is extended to an extremely high height. Even for screens with a very high pixel density, the resizer will fulfill its role as\n\t   it should, i.e. for a screen of 476 ppi the total height of the resizer will take over 350 sheets of A4 format, which is totally\n\t   unrealistic height for a single table. */\n\ttop: -999999px;\n\tbottom: -999999px;\n\tright: var(--ck-table-column-resizer-position-offset);\n\twidth: var(--ck-table-column-resizer-width);\n\tcursor: col-resize;\n\tuser-select: none;\n\tz-index: var(--ck-z-default);\n}\n\n.ck.ck-editor__editable.ck-column-resize_disabled .table .ck-table-column-resizer {\n\tdisplay: none;\n}\n\n/* The resizer elements, which are extended to an extremely high height, break the drag & drop feature in Chrome. To make it work again,\n   all resizers must be hidden while the table is dragged. */\n.ck.ck-editor__editable .table[draggable] .ck-table-column-resizer {\n\tdisplay: none;\n}\n\n.ck.ck-editor__editable .table .ck-table-column-resizer:hover,\n.ck.ck-editor__editable .table .ck-table-column-resizer__active {\n\tbackground-color: var(--ck-color-table-column-resizer-hover);\n\topacity: 0.25;\n}\n\n.ck.ck-editor__editable[dir=rtl] .table .ck-table-column-resizer {\n\tleft: var(--ck-table-column-resizer-position-offset);\n\tright: unset;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4777:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-table-focused-cell-background:rgba(158,201,250,.3)}.ck-widget.table td.ck-editor__nested-editable.ck-editor__nested-editable_focused,.ck-widget.table td.ck-editor__nested-editable:focus,.ck-widget.table th.ck-editor__nested-editable.ck-editor__nested-editable_focused,.ck-widget.table th.ck-editor__nested-editable:focus{background:var(--ck-color-table-focused-cell-background);border-style:none;outline:1px solid var(--ck-color-focus-border);outline-offset:-1px}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/tableediting.css"],"names":[],"mappings":"AAKA,MACC,6DACD,CAKE,8QAGC,wDAAyD,CAKzD,iBAAkB,CAClB,8CAA+C,CAC/C,mBACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-table-focused-cell-background: hsla(212, 90%, 80%, .3);\n}\n\n.ck-widget.table {\n\t& td,\n\t& th {\n\t\t&.ck-editor__nested-editable.ck-editor__nested-editable_focused,\n\t\t&.ck-editor__nested-editable:focus {\n\t\t\t/* A very slight background to highlight the focused cell */\n\t\t\tbackground: var(--ck-color-table-focused-cell-background);\n\n\t\t\t/* Fixes the problem where surrounding cells cover the focused cell's border.\n\t\t\tIt does not fix the problem in all places but the UX is improved.\n\t\t\tSee https://github.com/ckeditor/ckeditor5-table/issues/29. */\n\t\t\tborder-style: none;\n\t\t\toutline: 1px solid var(--ck-color-focus-border);\n\t\t\toutline-offset: -1px; /* progressive enhancement - no IE support */\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 198:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-table-form .ck-form__row.ck-table-form__background-row,.ck.ck-table-form .ck-form__row.ck-table-form__border-row{flex-wrap:wrap}.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row{align-items:center;flex-wrap:wrap}.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row .ck-labeled-field-view{align-items:center;display:flex;flex-direction:column-reverse}.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row .ck-labeled-field-view .ck.ck-dropdown,.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row .ck-table-form__dimension-operator{flex-grow:0}.ck.ck-table-form .ck.ck-labeled-field-view{position:relative}.ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status{bottom:calc(var(--ck-table-properties-error-arrow-size)*-1);left:50%;position:absolute;transform:translate(-50%,100%);z-index:1}.ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status:after{content:\"\";left:50%;position:absolute;top:calc(var(--ck-table-properties-error-arrow-size)*-1);transform:translateX(-50%)}:root{--ck-table-properties-error-arrow-size:6px;--ck-table-properties-min-error-width:150px}.ck.ck-table-form .ck-form__row.ck-table-form__border-row .ck-labeled-field-view>.ck-label{font-size:var(--ck-font-size-tiny);text-align:center}.ck.ck-table-form .ck-form__row.ck-table-form__border-row .ck-table-form__border-style,.ck.ck-table-form .ck-form__row.ck-table-form__border-row .ck-table-form__border-width{max-width:80px;min-width:80px;width:80px}.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row{padding:0}.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row .ck-table-form__dimensions-row__height,.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row .ck-table-form__dimensions-row__width{margin:0}.ck.ck-table-form .ck-form__row.ck-table-form__dimensions-row .ck-table-form__dimension-operator{align-self:flex-end;display:inline-block;height:var(--ck-ui-component-min-height);line-height:var(--ck-ui-component-min-height);margin:0 var(--ck-spacing-small)}.ck.ck-table-form .ck.ck-labeled-field-view{padding-top:var(--ck-spacing-standard)}.ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status{border-radius:0}.ck-rounded-corners .ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status,.ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status{background:var(--ck-color-base-error);color:var(--ck-color-base-background);min-width:var(--ck-table-properties-min-error-width);padding:var(--ck-spacing-small) var(--ck-spacing-medium);text-align:center}.ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status:after{border-color:transparent transparent var(--ck-color-base-error) transparent;border-style:solid;border-width:0 var(--ck-table-properties-error-arrow-size) var(--ck-table-properties-error-arrow-size) var(--ck-table-properties-error-arrow-size)}.ck.ck-table-form .ck.ck-labeled-field-view .ck.ck-labeled-field-view__status{animation:ck-table-form-labeled-view-status-appear .15s ease both}.ck.ck-table-form .ck.ck-labeled-field-view .ck-input.ck-error:not(:focus)+.ck.ck-labeled-field-view__status{display:none}@keyframes ck-table-form-labeled-view-status-appear{0%{opacity:0}to{opacity:1}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/tableform.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/tableform.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAWE,wHACC,cACD,CAEA,8DAEC,kBAAmB,CADnB,cAgBD,CAbC,qFAGC,kBAAmB,CAFnB,YAAa,CACb,6BAMD,CAEA,sMACC,WACD,CAIF,4CAEC,iBAoBD,CAlBC,8EAGC,2DAAgE,CADhE,QAAS,CADT,iBAAkB,CAGlB,8BAA+B,CAG/B,SAUD,CAPC,oFACC,UAAW,CAGX,QAAS,CAFT,iBAAkB,CAClB,wDAA6D,CAE7D,0BACD,CChDH,MACC,0CAA2C,CAC3C,2CACD,CAMI,2FACC,kCAAmC,CACnC,iBACD,CAGD,8KAIC,cAAe,CADf,cAAe,CADf,UAGD,CAGD,8DACC,SAcD,CAZC,yMAEC,QACD,CAEA,iGACC,mBAAoB,CACpB,oBAAqB,CACrB,wCAAyC,CACzC,6CAA8C,CAC9C,gCACD,CAIF,4CACC,sCAyBD,CAvBC,8ECxCD,eDyDC,CAjBA,mMCpCA,qCDqDA,CAjBA,8EAGC,qCAAsC,CACtC,qCAAsC,CAEtC,oDAAqD,CADrD,wDAAyD,CAEzD,iBAUD,CAPC,oFACC,2EAA4E,CAE5E,kBAAmB,CADnB,kJAED,CAdD,8EAgBC,iEACD,CAGA,6GACC,YACD,CAIF,oDACC,GACC,SACD,CAEA,GACC,SACD,CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-table-form {\n\t& .ck-form__row {\n\t\t&.ck-table-form__border-row {\n\t\t\tflex-wrap: wrap;\n\t\t}\n\n\t\t&.ck-table-form__background-row {\n\t\t\tflex-wrap: wrap;\n\t\t}\n\n\t\t&.ck-table-form__dimensions-row {\n\t\t\tflex-wrap: wrap;\n\t\t\talign-items: center;\n\n\t\t\t& .ck-labeled-field-view {\n\t\t\t\tdisplay: flex;\n\t\t\t\tflex-direction: column-reverse;\n\t\t\t\talign-items: center;\n\n\t\t\t\t& .ck.ck-dropdown {\n\t\t\t\t\tflex-grow: 0;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t& .ck-table-form__dimension-operator {\n\t\t\t\tflex-grow: 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t& .ck.ck-labeled-field-view {\n\t\t/* Allow absolute positioning of the status (error) balloons. */\n\t\tposition: relative;\n\n\t\t& .ck.ck-labeled-field-view__status {\n\t\t\tposition: absolute;\n\t\t\tleft: 50%;\n\t\t\tbottom: calc( -1 * var(--ck-table-properties-error-arrow-size) );\n\t\t\ttransform: translate(-50%,100%);\n\n\t\t\t/* Make sure the balloon status stays on top of other form elements. */\n\t\t\tz-index: 1;\n\n\t\t\t/* The arrow pointing towards the field. */\n\t\t\t&::after {\n\t\t\t\tcontent: \"\";\n\t\t\t\tposition: absolute;\n\t\t\t\ttop: calc( -1 * var(--ck-table-properties-error-arrow-size) );\n\t\t\t\tleft: 50%;\n\t\t\t\ttransform: translateX( -50% );\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../mixins/_rounded.css\";\n\n:root {\n\t--ck-table-properties-error-arrow-size: 6px;\n\t--ck-table-properties-min-error-width: 150px;\n}\n\n.ck.ck-table-form {\n\t& .ck-form__row {\n\t\t&.ck-table-form__border-row {\n\t\t\t& .ck-labeled-field-view {\n\t\t\t\t& > .ck-label {\n\t\t\t\t\tfont-size: var(--ck-font-size-tiny);\n\t\t\t\t\ttext-align: center;\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t& .ck-table-form__border-style,\n\t\t\t& .ck-table-form__border-width {\n\t\t\t\twidth: 80px;\n\t\t\t\tmin-width: 80px;\n\t\t\t\tmax-width: 80px;\n\t\t\t}\n\t\t}\n\n\t\t&.ck-table-form__dimensions-row {\n\t\t\tpadding: 0;\n\n\t\t\t& .ck-table-form__dimensions-row__width,\n\t\t\t& .ck-table-form__dimensions-row__height {\n\t\t\t\tmargin: 0\n\t\t\t}\n\n\t\t\t& .ck-table-form__dimension-operator {\n\t\t\t\talign-self: flex-end;\n\t\t\t\tdisplay: inline-block;\n\t\t\t\theight: var(--ck-ui-component-min-height);\n\t\t\t\tline-height: var(--ck-ui-component-min-height);\n\t\t\t\tmargin: 0 var(--ck-spacing-small);\n\t\t\t}\n\t\t}\n\t}\n\n\t& .ck.ck-labeled-field-view {\n\t\tpadding-top: var(--ck-spacing-standard);\n\n\t\t& .ck.ck-labeled-field-view__status {\n\t\t\t@mixin ck-rounded-corners;\n\n\t\t\tbackground: var(--ck-color-base-error);\n\t\t\tcolor: var(--ck-color-base-background);\n\t\t\tpadding: var(--ck-spacing-small) var(--ck-spacing-medium);\n\t\t\tmin-width: var(--ck-table-properties-min-error-width);\n\t\t\ttext-align: center;\n\n\t\t\t/* The arrow pointing towards the field. */\n\t\t\t&::after {\n\t\t\t\tborder-color: transparent transparent var(--ck-color-base-error) transparent;\n\t\t\t\tborder-width: 0 var(--ck-table-properties-error-arrow-size) var(--ck-table-properties-error-arrow-size) var(--ck-table-properties-error-arrow-size);\n\t\t\t\tborder-style: solid;\n\t\t\t}\n\n\t\t\tanimation: ck-table-form-labeled-view-status-appear .15s ease both;\n\t\t}\n\n\t\t/* Hide the error balloon when the field is blurred. Makes the experience much more clear. */\n\t\t& .ck-input.ck-error:not(:focus) + .ck.ck-labeled-field-view__status {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n}\n\n@keyframes ck-table-form-labeled-view-status-appear {\n\t0% {\n\t\topacity: 0;\n\t}\n\n\t100% {\n\t\topacity: 1;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9221:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-table-properties-form .ck-form__row.ck-table-properties-form__alignment-row{align-content:baseline;flex-basis:0;flex-wrap:wrap}.ck.ck-table-properties-form .ck-form__row.ck-table-properties-form__alignment-row .ck.ck-toolbar .ck-toolbar__items{flex-wrap:nowrap}.ck.ck-table-properties-form{width:320px}.ck.ck-table-properties-form .ck-form__row.ck-table-properties-form__alignment-row{align-self:flex-end;padding:0}.ck.ck-table-properties-form .ck-form__row.ck-table-properties-form__alignment-row .ck.ck-toolbar{background:none;margin-top:var(--ck-spacing-standard)}.ck.ck-table-properties-form .ck-form__row.ck-table-properties-form__alignment-row .ck.ck-toolbar .ck-toolbar__items>*{width:40px}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-table/theme/tableproperties.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/tableproperties.css"],"names":[],"mappings":"AAOE,mFAGC,sBAAuB,CADvB,YAAa,CADb,cAOD,CAHC,qHACC,gBACD,CCTH,6BACC,WAmBD,CAhBE,mFACC,mBAAoB,CACpB,SAYD,CAVC,kGACC,eAAgB,CAGhB,qCAKD,CAHC,uHACC,UACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-table-properties-form {\n\t& .ck-form__row {\n\t\t&.ck-table-properties-form__alignment-row {\n\t\t\tflex-wrap: wrap;\n\t\t\tflex-basis: 0;\n\t\t\talign-content: baseline;\n\n\t\t\t& .ck.ck-toolbar .ck-toolbar__items {\n\t\t\t\tflex-wrap: nowrap;\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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.ck.ck-table-properties-form {\n\twidth: 320px;\n\n\t& .ck-form__row {\n\t\t&.ck-table-properties-form__alignment-row {\n\t\t\talign-self: flex-end;\n\t\t\tpadding: 0;\n\n\t\t\t& .ck.ck-toolbar {\n\t\t\t\tbackground: none;\n\n\t\t\t\t/* Compensate for missing input label that would push the margin (toolbar has no inputs). */\n\t\t\t\tmargin-top: var(--ck-spacing-standard);\n\n\t\t\t\t& .ck-toolbar__items > * {\n\t\t\t\t\twidth: 40px;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5593:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-table-selected-cell-background:rgba(158,207,250,.3)}.ck.ck-editor__editable .table table td.ck-editor__editable_selected,.ck.ck-editor__editable .table table th.ck-editor__editable_selected{box-shadow:unset;caret-color:transparent;outline:unset;position:relative}.ck.ck-editor__editable .table table td.ck-editor__editable_selected:after,.ck.ck-editor__editable .table table th.ck-editor__editable_selected:after{background-color:var(--ck-table-selected-cell-background);bottom:0;content:\"\";left:0;pointer-events:none;position:absolute;right:0;top:0}.ck.ck-editor__editable .table table td.ck-editor__editable_selected ::selection,.ck.ck-editor__editable .table table td.ck-editor__editable_selected:focus,.ck.ck-editor__editable .table table th.ck-editor__editable_selected ::selection,.ck.ck-editor__editable .table table th.ck-editor__editable_selected:focus{background-color:transparent}.ck.ck-editor__editable .table table td.ck-editor__editable_selected .ck-widget,.ck.ck-editor__editable .table table th.ck-editor__editable_selected .ck-widget{outline:unset}.ck.ck-editor__editable .table table td.ck-editor__editable_selected .ck-widget>.ck-widget__selection-handle,.ck.ck-editor__editable .table table th.ck-editor__editable_selected .ck-widget>.ck-widget__selection-handle{display:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-table/tableselection.css"],"names":[],"mappings":"AAKA,MACC,wDACD,CAGC,0IAKC,gBAAiB,CAFjB,uBAAwB,CACxB,aAAc,CAFd,iBAiCD,CA3BC,sJAGC,yDAA0D,CAK1D,QAAS,CAPT,UAAW,CAKX,MAAO,CAJP,mBAAoB,CAEpB,iBAAkB,CAGlB,OAAQ,CAFR,KAID,CAEA,wTAEC,4BACD,CAMA,gKACC,aAKD,CAHC,0NACC,YACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-table-selected-cell-background: hsla(208, 90%, 80%, .3);\n}\n\n.ck.ck-editor__editable .table table {\n\t& td.ck-editor__editable_selected,\n\t& th.ck-editor__editable_selected {\n\t\tposition: relative;\n\t\tcaret-color: transparent;\n\t\toutline: unset;\n\t\tbox-shadow: unset;\n\n\t\t/* https://github.com/ckeditor/ckeditor5/issues/6446 */\n\t\t&:after {\n\t\t\tcontent: '';\n\t\t\tpointer-events: none;\n\t\t\tbackground-color: var(--ck-table-selected-cell-background);\n\t\t\tposition: absolute;\n\t\t\ttop: 0;\n\t\t\tleft: 0;\n\t\t\tright: 0;\n\t\t\tbottom: 0;\n\t\t}\n\n\t\t& ::selection,\n\t\t&:focus {\n\t\t\tbackground-color: transparent;\n\t\t}\n\n\t\t/*\n\t\t * To reduce the amount of noise, all widgets in the table selection have no outline and no selection handle.\n\t\t * See https://github.com/ckeditor/ckeditor5/issues/9491.\n\t\t */\n\t\t& .ck-widget {\n\t\t\toutline: unset;\n\n\t\t\t& > .ck-widget__selection-handle {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4499:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-button,a.ck.ck-button{align-items:center;display:inline-flex;justify-content:left;position:relative;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.ck.ck-button .ck-button__label,a.ck.ck-button .ck-button__label{display:none}.ck.ck-button.ck-button_with-text .ck-button__label,a.ck.ck-button.ck-button_with-text .ck-button__label{display:inline-block}.ck.ck-button:not(.ck-button_with-text),a.ck.ck-button:not(.ck-button_with-text){justify-content:center}.ck.ck-button,a.ck.ck-button{background:var(--ck-color-button-default-background)}.ck.ck-button:not(.ck-disabled):hover,a.ck.ck-button:not(.ck-disabled):hover{background:var(--ck-color-button-default-hover-background)}.ck.ck-button:not(.ck-disabled):active,a.ck.ck-button:not(.ck-disabled):active{background:var(--ck-color-button-default-active-background)}.ck.ck-button.ck-disabled,a.ck.ck-button.ck-disabled{background:var(--ck-color-button-default-disabled-background)}.ck.ck-button,a.ck.ck-button{border-radius:0}.ck-rounded-corners .ck.ck-button,.ck-rounded-corners a.ck.ck-button,.ck.ck-button.ck-rounded-corners,a.ck.ck-button.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-button,a.ck.ck-button{-webkit-appearance:none;border:1px solid transparent;cursor:default;font-size:inherit;line-height:1;min-height:var(--ck-ui-component-min-height);min-width:var(--ck-ui-component-min-height);padding:var(--ck-spacing-tiny);text-align:center;transition:box-shadow .2s ease-in-out,border .2s ease-in-out;vertical-align:middle;white-space:nowrap}.ck.ck-button:active,.ck.ck-button:focus,a.ck.ck-button:active,a.ck.ck-button:focus{border:var(--ck-focus-ring);box-shadow:var(--ck-focus-outer-shadow),0 0;outline:none}.ck.ck-button .ck-button__icon use,.ck.ck-button .ck-button__icon use *,a.ck.ck-button .ck-button__icon use,a.ck.ck-button .ck-button__icon use *{color:inherit}.ck.ck-button .ck-button__label,a.ck.ck-button .ck-button__label{color:inherit;cursor:inherit;font-size:inherit;font-weight:inherit;vertical-align:middle}[dir=ltr] .ck.ck-button .ck-button__label,[dir=ltr] a.ck.ck-button .ck-button__label{text-align:left}[dir=rtl] .ck.ck-button .ck-button__label,[dir=rtl] a.ck.ck-button .ck-button__label{text-align:right}.ck.ck-button .ck-button__keystroke,a.ck.ck-button .ck-button__keystroke{color:inherit}[dir=ltr] .ck.ck-button .ck-button__keystroke,[dir=ltr] a.ck.ck-button .ck-button__keystroke{margin-left:var(--ck-spacing-large)}[dir=rtl] .ck.ck-button .ck-button__keystroke,[dir=rtl] a.ck.ck-button .ck-button__keystroke{margin-right:var(--ck-spacing-large)}.ck.ck-button .ck-button__keystroke,a.ck.ck-button .ck-button__keystroke{font-weight:700;opacity:.7}.ck.ck-button.ck-disabled:active,.ck.ck-button.ck-disabled:focus,a.ck.ck-button.ck-disabled:active,a.ck.ck-button.ck-disabled:focus{box-shadow:var(--ck-focus-disabled-outer-shadow),0 0}.ck.ck-button.ck-disabled .ck-button__icon,.ck.ck-button.ck-disabled .ck-button__label,a.ck.ck-button.ck-disabled .ck-button__icon,a.ck.ck-button.ck-disabled .ck-button__label{opacity:var(--ck-disabled-opacity)}.ck.ck-button.ck-disabled .ck-button__keystroke,a.ck.ck-button.ck-disabled .ck-button__keystroke{opacity:.3}.ck.ck-button.ck-button_with-text,a.ck.ck-button.ck-button_with-text{padding:var(--ck-spacing-tiny) var(--ck-spacing-standard)}[dir=ltr] .ck.ck-button.ck-button_with-text .ck-button__icon,[dir=ltr] a.ck.ck-button.ck-button_with-text .ck-button__icon{margin-left:calc(var(--ck-spacing-small)*-1);margin-right:var(--ck-spacing-small)}[dir=rtl] .ck.ck-button.ck-button_with-text .ck-button__icon,[dir=rtl] a.ck.ck-button.ck-button_with-text .ck-button__icon{margin-left:var(--ck-spacing-small);margin-right:calc(var(--ck-spacing-small)*-1)}.ck.ck-button.ck-button_with-keystroke .ck-button__label,a.ck.ck-button.ck-button_with-keystroke .ck-button__label{flex-grow:1}.ck.ck-button.ck-on,a.ck.ck-button.ck-on{background:var(--ck-color-button-on-background)}.ck.ck-button.ck-on:not(.ck-disabled):hover,a.ck.ck-button.ck-on:not(.ck-disabled):hover{background:var(--ck-color-button-on-hover-background)}.ck.ck-button.ck-on:not(.ck-disabled):active,a.ck.ck-button.ck-on:not(.ck-disabled):active{background:var(--ck-color-button-on-active-background)}.ck.ck-button.ck-on.ck-disabled,a.ck.ck-button.ck-on.ck-disabled{background:var(--ck-color-button-on-disabled-background)}.ck.ck-button.ck-on,a.ck.ck-button.ck-on{color:var(--ck-color-button-on-color)}.ck.ck-button.ck-button-save,a.ck.ck-button.ck-button-save{color:var(--ck-color-button-save)}.ck.ck-button.ck-button-cancel,a.ck.ck-button.ck-button-cancel{color:var(--ck-color-button-cancel)}.ck.ck-button-action,a.ck.ck-button-action{background:var(--ck-color-button-action-background)}.ck.ck-button-action:not(.ck-disabled):hover,a.ck.ck-button-action:not(.ck-disabled):hover{background:var(--ck-color-button-action-hover-background)}.ck.ck-button-action:not(.ck-disabled):active,a.ck.ck-button-action:not(.ck-disabled):active{background:var(--ck-color-button-action-active-background)}.ck.ck-button-action.ck-disabled,a.ck.ck-button-action.ck-disabled{background:var(--ck-color-button-action-disabled-background)}.ck.ck-button-action,a.ck.ck-button-action{color:var(--ck-color-button-action-text)}.ck.ck-button-bold,a.ck.ck-button-bold{font-weight:700}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/button/button.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_unselectable.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/button/button.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/mixins/_button.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_focus.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_disabled.css"],"names":[],"mappings":"AAOA,6BAMC,kBAAmB,CADnB,mBAAoB,CAEpB,oBAAqB,CAHrB,iBAAkB,CCFlB,qBAAsB,CACtB,wBAAyB,CACzB,oBAAqB,CACrB,gBDkBD,CAdC,iEACC,YACD,CAGC,yGACC,oBACD,CAID,iFACC,sBACD,CEjBD,6BCAC,oDD4ID,CCzIE,6EACC,0DACD,CAEA,+EACC,2DACD,CAID,qDACC,6DACD,CDfD,6BEDC,eF6ID,CA5IA,wIEGE,qCFyIF,CA5IA,6BA6BC,uBAAwB,CANxB,4BAA6B,CAjB7B,cAAe,CAcf,iBAAkB,CAHlB,aAAc,CAJd,4CAA6C,CAD7C,2CAA4C,CAJ5C,8BAA+B,CAC/B,iBAAkB,CAiBlB,4DAA8D,CAnB9D,qBAAsB,CAFtB,kBAuID,CA7GC,oFGhCA,2BAA2B,CCF3B,2CAA8B,CDC9B,YHqCA,CAIC,kJAEC,aACD,CAGD,iEAIC,aAAc,CACd,cAAe,CAHf,iBAAkB,CAClB,mBAAoB,CAMpB,qBASD,CAlBA,qFAYE,eAMF,CAlBA,qFAgBE,gBAEF,CAEA,yEACC,aAYD,CAbA,6FAIE,mCASF,CAbA,6FAQE,oCAKF,CAbA,yEAWC,eAAiB,CACjB,UACD,CAIC,oIIrFD,oDJyFC,CAOA,gLKhGD,kCLkGC,CAEA,iGACC,UACD,CAGD,qEACC,yDAcD,CAXC,2HAEE,4CAA+C,CAC/C,oCAOF,CAVA,2HAQE,mCAAoC,CADpC,6CAGF,CAKA,mHACC,WACD,CAID,yCC/HA,+CDmIA,CChIC,yFACC,qDACD,CAEA,2FACC,sDACD,CAID,iEACC,wDACD,CDgHA,yCAGC,qCACD,CAEA,2DACC,iCACD,CAEA,+DACC,mCACD,CAID,2CC/IC,mDDoJD,CCjJE,2FACC,yDACD,CAEA,6FACC,0DACD,CAID,mEACC,4DACD,CDgID,2CAIC,wCACD,CAEA,uCAEC,eACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"../../mixins/_unselectable.css\";\n\n.ck.ck-button,\na.ck.ck-button {\n\t@mixin ck-unselectable;\n\n\tposition: relative;\n\tdisplay: inline-flex;\n\talign-items: center;\n\tjustify-content: left;\n\n\t& .ck-button__label {\n\t\tdisplay: none;\n\t}\n\n\t&.ck-button_with-text {\n\t\t& .ck-button__label {\n\t\t\tdisplay: inline-block;\n\t\t}\n\t}\n\n\t/* Center the icon horizontally in a button without text. */\n\t&:not(.ck-button_with-text)  {\n\t\tjustify-content: center;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Makes element unselectable.\n */\n@define-mixin ck-unselectable {\n\t-moz-user-select: none;\n\t-webkit-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_focus.css\";\n@import \"../../../mixins/_shadow.css\";\n@import \"../../../mixins/_disabled.css\";\n@import \"../../../mixins/_rounded.css\";\n@import \"../../mixins/_button.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n.ck.ck-button,\na.ck.ck-button {\n\t@mixin ck-button-colors --ck-color-button-default;\n\t@mixin ck-rounded-corners;\n\n\twhite-space: nowrap;\n\tcursor: default;\n\tvertical-align: middle;\n\tpadding: var(--ck-spacing-tiny);\n\ttext-align: center;\n\n\t/* A very important piece of styling. Go to variable declaration to learn more. */\n\tmin-width: var(--ck-ui-component-min-height);\n\tmin-height: var(--ck-ui-component-min-height);\n\n\t/* Normalize the height of the line. Removing this will break consistent height\n\tamong text and text-less buttons (with icons). */\n\tline-height: 1;\n\n\t/* Enable font size inheritance, which allows fluid UI scaling. */\n\tfont-size: inherit;\n\n\t/* Avoid flickering when the foucs border shows up. */\n\tborder: 1px solid transparent;\n\n\t/* Apply some smooth transition to the box-shadow and border. */\n\ttransition: box-shadow .2s ease-in-out, border .2s ease-in-out;\n\n\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/189 */\n\t-webkit-appearance: none;\n\n\t&:active,\n\t&:focus {\n\t\t@mixin ck-focus-ring;\n\t\t@mixin ck-box-shadow var(--ck-focus-outer-shadow);\n\t}\n\n\t/* Allow icon coloring using the text \"color\" property. */\n\t& .ck-button__icon {\n\t\t& use,\n\t\t& use * {\n\t\t\tcolor: inherit;\n\t\t}\n\t}\n\n\t& .ck-button__label {\n\t\t/* Enable font size inheritance, which allows fluid UI scaling. */\n\t\tfont-size: inherit;\n\t\tfont-weight: inherit;\n\t\tcolor: inherit;\n\t\tcursor: inherit;\n\n\t\t/* Must be consistent with .ck-icon's vertical align. Otherwise, buttons with and\n\t\twithout labels (but with icons) have different sizes in Chrome */\n\t\tvertical-align: middle;\n\n\t\t@mixin ck-dir ltr {\n\t\t\ttext-align: left;\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\ttext-align: right;\n\t\t}\n\t}\n\n\t& .ck-button__keystroke {\n\t\tcolor: inherit;\n\n\t\t@mixin ck-dir ltr {\n\t\t\tmargin-left: var(--ck-spacing-large);\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\tmargin-right: var(--ck-spacing-large);\n\t\t}\n\n\t\tfont-weight: bold;\n\t\topacity: .7;\n\t}\n\n\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/70 */\n\t&.ck-disabled {\n\t\t&:active,\n\t\t&:focus {\n\t\t\t/* The disabled button should have a slightly less visible shadow when focused. */\n\t\t\t@mixin ck-box-shadow var(--ck-focus-disabled-outer-shadow);\n\t\t}\n\n\t\t& .ck-button__icon {\n\t\t\t@mixin ck-disabled;\n\t\t}\n\n\t\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/98 */\n\t\t& .ck-button__label {\n\t\t\t@mixin ck-disabled;\n\t\t}\n\n\t\t& .ck-button__keystroke {\n\t\t\topacity: .3;\n\t\t}\n\t}\n\n\t&.ck-button_with-text {\n\t\tpadding: var(--ck-spacing-tiny) var(--ck-spacing-standard);\n\n\t\t/* stylelint-disable-next-line no-descending-specificity */\n\t\t& .ck-button__icon {\n\t\t\t@mixin ck-dir ltr {\n\t\t\t\tmargin-left: calc(-1 * var(--ck-spacing-small));\n\t\t\t\tmargin-right: var(--ck-spacing-small);\n\t\t\t}\n\n\t\t\t@mixin ck-dir rtl {\n\t\t\t\tmargin-right: calc(-1 * var(--ck-spacing-small));\n\t\t\t\tmargin-left: var(--ck-spacing-small);\n\t\t\t}\n\t\t}\n\t}\n\n\t&.ck-button_with-keystroke {\n\t\t/* stylelint-disable-next-line no-descending-specificity */\n\t\t& .ck-button__label {\n\t\t\tflex-grow: 1;\n\t\t}\n\t}\n\n\t/* A style of the button which is currently on, e.g. its feature is active. */\n\t&.ck-on {\n\t\t@mixin ck-button-colors --ck-color-button-on;\n\n\t\tcolor: var(--ck-color-button-on-color);\n\t}\n\n\t&.ck-button-save {\n\t\tcolor: var(--ck-color-button-save);\n\t}\n\n\t&.ck-button-cancel {\n\t\tcolor: var(--ck-color-button-cancel);\n\t}\n}\n\n/* A style of the button which handles the primary action. */\n.ck.ck-button-action,\na.ck.ck-button-action {\n\t@mixin ck-button-colors --ck-color-button-action;\n\n\tcolor: var(--ck-color-button-action-text);\n}\n\n.ck.ck-button-bold,\na.ck.ck-button-bold {\n\tfont-weight: bold;\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements a button of given background color.\n *\n * @param {String} $background - Background color of the button.\n * @param {String} $border - Border color of the button.\n */\n@define-mixin ck-button-colors $prefix {\n\tbackground: var($(prefix)-background);\n\n\t&:not(.ck-disabled) {\n\t\t&:hover {\n\t\t\tbackground: var($(prefix)-hover-background);\n\t\t}\n\n\t\t&:active {\n\t\t\tbackground: var($(prefix)-active-background);\n\t\t}\n\t}\n\n\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/98 */\n\t&.ck-disabled {\n\t\tbackground: var($(prefix)-disabled-background);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A visual style of focused element's border.\n */\n@define-mixin ck-focus-ring {\n\t/* Disable native outline. */\n\toutline: none;\n\tborder: var(--ck-focus-ring)\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A class which indicates that an element holding it is disabled.\n */\n@define-mixin ck-disabled {\n\topacity: var(--ck-disabled-opacity);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9681:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-button.ck-switchbutton .ck-button__toggle,.ck.ck-button.ck-switchbutton .ck-button__toggle .ck-button__toggle__inner{display:block}:root{--ck-switch-button-toggle-width:2.6153846154em;--ck-switch-button-toggle-inner-size:calc(1.07692em + 1px);--ck-switch-button-translation:calc(var(--ck-switch-button-toggle-width) - var(--ck-switch-button-toggle-inner-size) - 2px);--ck-switch-button-inner-hover-shadow:0 0 0 5px var(--ck-color-switch-button-inner-shadow)}.ck.ck-button.ck-switchbutton,.ck.ck-button.ck-switchbutton.ck-on:active,.ck.ck-button.ck-switchbutton.ck-on:focus,.ck.ck-button.ck-switchbutton.ck-on:hover,.ck.ck-button.ck-switchbutton:active,.ck.ck-button.ck-switchbutton:focus,.ck.ck-button.ck-switchbutton:hover{background:transparent;color:inherit}[dir=ltr] .ck.ck-button.ck-switchbutton .ck-button__label{margin-right:calc(var(--ck-spacing-large)*2)}[dir=rtl] .ck.ck-button.ck-switchbutton .ck-button__label{margin-left:calc(var(--ck-spacing-large)*2)}.ck.ck-button.ck-switchbutton .ck-button__toggle{border-radius:0}.ck-rounded-corners .ck.ck-button.ck-switchbutton .ck-button__toggle,.ck.ck-button.ck-switchbutton .ck-button__toggle.ck-rounded-corners{border-radius:var(--ck-border-radius)}[dir=ltr] .ck.ck-button.ck-switchbutton .ck-button__toggle{margin-left:auto}[dir=rtl] .ck.ck-button.ck-switchbutton .ck-button__toggle{margin-right:auto}.ck.ck-button.ck-switchbutton .ck-button__toggle{background:var(--ck-color-switch-button-off-background);border:1px solid transparent;transition:background .4s ease,box-shadow .2s ease-in-out,outline .2s ease-in-out;width:var(--ck-switch-button-toggle-width)}.ck.ck-button.ck-switchbutton .ck-button__toggle .ck-button__toggle__inner{border-radius:0}.ck-rounded-corners .ck.ck-button.ck-switchbutton .ck-button__toggle .ck-button__toggle__inner,.ck.ck-button.ck-switchbutton .ck-button__toggle .ck-button__toggle__inner.ck-rounded-corners{border-radius:var(--ck-border-radius);border-radius:calc(var(--ck-border-radius)*.5)}.ck.ck-button.ck-switchbutton .ck-button__toggle .ck-button__toggle__inner{background:var(--ck-color-switch-button-inner-background);height:var(--ck-switch-button-toggle-inner-size);transition:all .3s ease;width:var(--ck-switch-button-toggle-inner-size)}.ck.ck-button.ck-switchbutton .ck-button__toggle:hover{background:var(--ck-color-switch-button-off-hover-background)}.ck.ck-button.ck-switchbutton .ck-button__toggle:hover .ck-button__toggle__inner{box-shadow:var(--ck-switch-button-inner-hover-shadow)}.ck.ck-button.ck-switchbutton.ck-disabled .ck-button__toggle{opacity:var(--ck-disabled-opacity)}.ck.ck-button.ck-switchbutton:focus{border-color:transparent;box-shadow:none;outline:none}.ck.ck-button.ck-switchbutton:focus .ck-button__toggle{box-shadow:0 0 0 1px var(--ck-color-base-background),0 0 0 5px var(--ck-color-focus-outer-shadow);outline:var(--ck-focus-ring);outline-offset:1px}.ck.ck-button.ck-switchbutton.ck-on .ck-button__toggle{background:var(--ck-color-switch-button-on-background)}.ck.ck-button.ck-switchbutton.ck-on .ck-button__toggle:hover{background:var(--ck-color-switch-button-on-hover-background)}[dir=ltr] .ck.ck-button.ck-switchbutton.ck-on .ck-button__toggle .ck-button__toggle__inner{transform:translateX(var( --ck-switch-button-translation ))}[dir=rtl] .ck.ck-button.ck-switchbutton.ck-on .ck-button__toggle .ck-button__toggle__inner{transform:translateX(calc(var( --ck-switch-button-translation )*-1))}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/button/switchbutton.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/button/switchbutton.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_disabled.css"],"names":[],"mappings":"AASE,4HACC,aACD,CCCF,MAEC,8CAA+C,CAE/C,0DAAgE,CAChE,2HAIC,CACD,0FACD,CAOC,0QAEC,sBAAuB,CADvB,aAED,CAEA,0DAGE,4CAOF,CAVA,0DAQE,2CAEF,CAEA,iDCpCA,eD4EA,CAxCA,yIChCC,qCDwED,CAxCA,2DAKE,gBAmCF,CAxCA,2DAUE,iBA8BF,CAxCA,iDAkBC,uDAAwD,CAFxD,4BAA6B,CAD7B,iFAAsF,CAEtF,0CAuBD,CApBC,2ECxDD,eDmEC,CAXA,6LCpDA,qCAAsC,CDsDpC,8CASF,CAXA,2EAOC,yDAA0D,CAD1D,gDAAiD,CAIjD,uBAA0B,CAL1B,+CAMD,CAEA,uDACC,6DAKD,CAHC,iFACC,qDACD,CAIF,6DEhFA,kCFkFA,CAGA,oCACC,wBAAyB,CAEzB,eAAgB,CADhB,YAQD,CALC,uDACC,iGAAmG,CAEnG,4BAA6B,CAD7B,kBAED,CAKA,uDACC,sDAkBD,CAhBC,6DACC,4DACD,CAEA,2FAKE,2DAMF,CAXA,2FASE,oEAEF","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-button.ck-switchbutton {\n\t& .ck-button__toggle {\n\t\tdisplay: block;\n\n\t\t& .ck-button__toggle__inner {\n\t\t\tdisplay: block;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n@import \"../../../mixins/_disabled.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n/* Note: To avoid rendering issues (aliasing) but to preserve the responsive nature\nof the component, floating–point numbers have been used which, for the default font size\n(see: --ck-font-size-base), will generate simple integers. */\n:root {\n\t/* 34px at 13px font-size */\n\t--ck-switch-button-toggle-width: 2.6153846154em;\n\t/* 14px at 13px font-size */\n\t--ck-switch-button-toggle-inner-size: calc(1.0769230769em + 1px);\n\t--ck-switch-button-translation: calc(\n\t\tvar(--ck-switch-button-toggle-width) -\n\t\tvar(--ck-switch-button-toggle-inner-size) -\n\t\t2px /* Border */\n\t);\n\t--ck-switch-button-inner-hover-shadow: 0 0 0 5px var(--ck-color-switch-button-inner-shadow);\n}\n\n.ck.ck-button.ck-switchbutton {\n\t/* Unlike a regular button, the switch button text color and background should never change.\n\t * Changing toggle switch (background, outline) is enough to carry the information about the\n\t * state of the entire component (https://github.com/ckeditor/ckeditor5/issues/12519)\n\t */\n\t&, &:hover, &:focus, &:active, &.ck-on:hover, &.ck-on:focus, &.ck-on:active {\n\t\tcolor: inherit;\n\t\tbackground: transparent;\n\t}\n\n\t& .ck-button__label {\n\t\t@mixin ck-dir ltr {\n\t\t\t/* Separate the label from the switch */\n\t\t\tmargin-right: calc(2 * var(--ck-spacing-large));\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\t/* Separate the label from the switch */\n\t\t\tmargin-left: calc(2 * var(--ck-spacing-large));\n\t\t}\n\t}\n\n\t& .ck-button__toggle {\n\t\t@mixin ck-rounded-corners;\n\n\t\t@mixin ck-dir ltr {\n\t\t\t/* Make sure the toggle is always to the right as far as possible. */\n\t\t\tmargin-left: auto;\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\t/* Make sure the toggle is always to the left as far as possible. */\n\t\t\tmargin-right: auto;\n\t\t}\n\n\t\t/* Apply some smooth transition to the box-shadow and border. */\n\t\t/* Gently animate the background color of the toggle switch */\n\t\ttransition: background 400ms ease, box-shadow .2s ease-in-out, outline .2s ease-in-out;\n\t\tborder: 1px solid transparent;\n\t\twidth: var(--ck-switch-button-toggle-width);\n\t\tbackground: var(--ck-color-switch-button-off-background);\n\n\t\t& .ck-button__toggle__inner {\n\t\t\t@mixin ck-rounded-corners {\n\t\t\t\tborder-radius: calc(.5 * var(--ck-border-radius));\n\t\t\t}\n\n\t\t\twidth: var(--ck-switch-button-toggle-inner-size);\n\t\t\theight: var(--ck-switch-button-toggle-inner-size);\n\t\t\tbackground: var(--ck-color-switch-button-inner-background);\n\n\t\t\t/* Gently animate the inner part of the toggle switch */\n\t\t\ttransition: all 300ms ease;\n\t\t}\n\n\t\t&:hover {\n\t\t\tbackground: var(--ck-color-switch-button-off-hover-background);\n\n\t\t\t& .ck-button__toggle__inner {\n\t\t\t\tbox-shadow: var(--ck-switch-button-inner-hover-shadow);\n\t\t\t}\n\t\t}\n\t}\n\n\t&.ck-disabled .ck-button__toggle {\n\t\t@mixin ck-disabled;\n\t}\n\n\t/* Overriding default .ck-button:focus styles + an outline around the toogle */\n\t&:focus {\n\t\tborder-color: transparent;\n\t\toutline: none;\n\t\tbox-shadow: none;\n\n\t\t& .ck-button__toggle {\n\t\t\tbox-shadow: 0 0 0 1px var(--ck-color-base-background), 0 0 0 5px var(--ck-color-focus-outer-shadow);\n\t\t\toutline-offset: 1px;\n\t\t\toutline: var(--ck-focus-ring);\n\t\t}\n\t}\n\n\t/* stylelint-disable-next-line no-descending-specificity */\n\t&.ck-on {\n\t\t& .ck-button__toggle {\n\t\t\tbackground: var(--ck-color-switch-button-on-background);\n\n\t\t\t&:hover {\n\t\t\t\tbackground: var(--ck-color-switch-button-on-hover-background);\n\t\t\t}\n\n\t\t\t& .ck-button__toggle__inner {\n\t\t\t\t/*\n\t\t\t\t* Move the toggle switch to the right. It will be animated.\n\t\t\t\t*/\n\t\t\t\t@mixin ck-dir ltr {\n\t\t\t\t\ttransform: translateX( var( --ck-switch-button-translation ) );\n\t\t\t\t}\n\n\t\t\t\t@mixin ck-dir rtl {\n\t\t\t\t\ttransform: translateX( calc( -1 * var( --ck-switch-button-translation ) ) );\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A class which indicates that an element holding it is disabled.\n */\n@define-mixin ck-disabled {\n\topacity: var(--ck-disabled-opacity);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4923:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-color-grid{display:grid}:root{--ck-color-grid-tile-size:24px;--ck-color-color-grid-check-icon:#166fd4}.ck.ck-color-grid{grid-gap:5px;padding:8px}.ck.ck-color-grid__tile{border:0;height:var(--ck-color-grid-tile-size);min-height:var(--ck-color-grid-tile-size);min-width:var(--ck-color-grid-tile-size);padding:0;transition:box-shadow .2s ease;width:var(--ck-color-grid-tile-size)}.ck.ck-color-grid__tile.ck-disabled{cursor:unset;transition:unset}.ck.ck-color-grid__tile.ck-color-table__color-tile_bordered{box-shadow:0 0 0 1px var(--ck-color-base-border)}.ck.ck-color-grid__tile .ck.ck-icon{color:var(--ck-color-color-grid-check-icon);display:none}.ck.ck-color-grid__tile.ck-on{box-shadow:inset 0 0 0 1px var(--ck-color-base-background),0 0 0 2px var(--ck-color-base-text)}.ck.ck-color-grid__tile.ck-on .ck.ck-icon{display:block}.ck.ck-color-grid__tile.ck-on,.ck.ck-color-grid__tile:focus:not(.ck-disabled),.ck.ck-color-grid__tile:hover:not(.ck-disabled){border:0}.ck.ck-color-grid__tile:focus:not(.ck-disabled),.ck.ck-color-grid__tile:hover:not(.ck-disabled){box-shadow:inset 0 0 0 1px var(--ck-color-base-background),0 0 0 2px var(--ck-color-focus-border)}.ck.ck-color-grid__label{padding:0 var(--ck-spacing-standard)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/colorgrid/colorgrid.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/colorgrid/colorgrid.css"],"names":[],"mappings":"AAKA,kBACC,YACD,CCAA,MACC,8BAA+B,CAK/B,wCACD,CAEA,kBACC,YAAa,CACb,WACD,CAEA,wBAOC,QAAS,CALT,qCAAsC,CAEtC,yCAA0C,CAD1C,wCAAyC,CAEzC,SAAU,CACV,8BAA+B,CAL/B,oCAyCD,CAjCC,oCACC,YAAa,CACb,gBACD,CAEA,4DACC,gDACD,CAEA,oCAEC,2CAA4C,CAD5C,YAED,CAEA,8BACC,8FAKD,CAHC,0CACC,aACD,CAGD,8HAIC,QACD,CAEA,gGAEC,iGACD,CAGD,yBACC,oCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-color-grid {\n\tdisplay: grid;\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n\n:root {\n\t--ck-color-grid-tile-size: 24px;\n\n\t/* Not using global colors here because these may change but some colors in a pallette\n\t * require special treatment. For instance, this ensures no matter what the UI text color is,\n\t * the check icon will look good on the black color tile. */\n\t--ck-color-color-grid-check-icon: hsl(212, 81%, 46%);\n}\n\n.ck.ck-color-grid {\n\tgrid-gap: 5px;\n\tpadding: 8px;\n}\n\n.ck.ck-color-grid__tile {\n\twidth: var(--ck-color-grid-tile-size);\n\theight: var(--ck-color-grid-tile-size);\n\tmin-width: var(--ck-color-grid-tile-size);\n\tmin-height: var(--ck-color-grid-tile-size);\n\tpadding: 0;\n\ttransition: .2s ease box-shadow;\n\tborder: 0;\n\n\t&.ck-disabled {\n\t\tcursor: unset;\n\t\ttransition: unset;\n\t}\n\n\t&.ck-color-table__color-tile_bordered {\n\t\tbox-shadow: 0 0 0 1px var(--ck-color-base-border);\n\t}\n\n\t& .ck.ck-icon {\n\t\tdisplay: none;\n\t\tcolor: var(--ck-color-color-grid-check-icon);\n\t}\n\n\t&.ck-on {\n\t\tbox-shadow: inset 0 0 0 1px var(--ck-color-base-background), 0 0 0 2px var(--ck-color-base-text);\n\n\t\t& .ck.ck-icon {\n\t\t\tdisplay: block;\n\t\t}\n\t}\n\n\t&.ck-on,\n\t&:focus:not( .ck-disabled ),\n\t&:hover:not( .ck-disabled ) {\n\t\t/* Disable the default .ck-button's border ring. */\n\t\tborder: 0;\n\t}\n\n\t&:focus:not( .ck-disabled ),\n\t&:hover:not( .ck-disabled ) {\n\t\tbox-shadow: inset 0 0 0 1px var(--ck-color-base-background), 0 0 0 2px var(--ck-color-focus-border);\n\t}\n}\n\n.ck.ck-color-grid__label {\n\tpadding: 0 var(--ck-spacing-standard);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3488:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-dropdown-max-width:75vw}.ck.ck-dropdown{display:inline-block;position:relative}.ck.ck-dropdown .ck-dropdown__arrow{pointer-events:none;z-index:var(--ck-z-default)}.ck.ck-dropdown .ck-button.ck-dropdown__button{width:100%}.ck.ck-dropdown .ck-dropdown__panel{display:none;max-width:var(--ck-dropdown-max-width);position:absolute;z-index:var(--ck-z-modal)}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel-visible{display:inline-block}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_n,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_ne,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_nme,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_nmw,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_nw{bottom:100%}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_s,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_se,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_sme,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_smw,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_sw{bottom:auto;top:100%}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_ne,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_se{left:0}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_nw,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_sw{right:0}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_n,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_s{left:50%;transform:translateX(-50%)}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_nmw,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_smw{left:75%;transform:translateX(-75%)}.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_nme,.ck.ck-dropdown .ck-dropdown__panel.ck-dropdown__panel_sme{left:25%;transform:translateX(-25%)}.ck.ck-toolbar .ck-dropdown__panel{z-index:calc(var(--ck-z-modal) + 1)}:root{--ck-dropdown-arrow-size:calc(var(--ck-icon-size)*0.5)}.ck.ck-dropdown{font-size:inherit}.ck.ck-dropdown .ck-dropdown__arrow{width:var(--ck-dropdown-arrow-size)}[dir=ltr] .ck.ck-dropdown .ck-dropdown__arrow{margin-left:var(--ck-spacing-standard);right:var(--ck-spacing-standard)}[dir=rtl] .ck.ck-dropdown .ck-dropdown__arrow{left:var(--ck-spacing-standard);margin-right:var(--ck-spacing-small)}.ck.ck-dropdown.ck-disabled .ck-dropdown__arrow{opacity:var(--ck-disabled-opacity)}[dir=ltr] .ck.ck-dropdown .ck-button.ck-dropdown__button:not(.ck-button_with-text){padding-left:var(--ck-spacing-small)}[dir=rtl] .ck.ck-dropdown .ck-button.ck-dropdown__button:not(.ck-button_with-text){padding-right:var(--ck-spacing-small)}.ck.ck-dropdown .ck-button.ck-dropdown__button .ck-button__label{overflow:hidden;text-overflow:ellipsis;width:7em}.ck.ck-dropdown .ck-button.ck-dropdown__button.ck-disabled .ck-button__label{opacity:var(--ck-disabled-opacity)}.ck.ck-dropdown .ck-button.ck-dropdown__button.ck-on{border-bottom-left-radius:0;border-bottom-right-radius:0}.ck.ck-dropdown .ck-button.ck-dropdown__button.ck-dropdown__button_label-width_auto .ck-button__label{width:auto}.ck.ck-dropdown .ck-button.ck-dropdown__button.ck-off:active,.ck.ck-dropdown .ck-button.ck-dropdown__button.ck-on:active{box-shadow:none}.ck.ck-dropdown .ck-button.ck-dropdown__button.ck-off:active:focus,.ck.ck-dropdown .ck-button.ck-dropdown__button.ck-on:active:focus{box-shadow:var(--ck-focus-outer-shadow),0 0}.ck.ck-dropdown__panel{border-radius:0}.ck-rounded-corners .ck.ck-dropdown__panel,.ck.ck-dropdown__panel.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-dropdown__panel{background:var(--ck-color-dropdown-panel-background);border:1px solid var(--ck-color-dropdown-panel-border);bottom:0;box-shadow:var(--ck-drop-shadow),0 0;min-width:100%}.ck.ck-dropdown__panel.ck-dropdown__panel_se{border-top-left-radius:0}.ck.ck-dropdown__panel.ck-dropdown__panel_sw{border-top-right-radius:0}.ck.ck-dropdown__panel.ck-dropdown__panel_ne{border-bottom-left-radius:0}.ck.ck-dropdown__panel.ck-dropdown__panel_nw{border-bottom-right-radius:0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/dropdown.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/dropdown/dropdown.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_disabled.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAKA,MACC,4BACD,CAEA,gBACC,oBAAqB,CACrB,iBA2ED,CAzEC,oCACC,mBAAoB,CACpB,2BACD,CAGA,+CACC,UACD,CAEA,oCACC,YAAa,CAEb,sCAAuC,CAEvC,iBAAkB,CAHlB,yBA4DD,CAvDC,+DACC,oBACD,CAEA,mSAKC,WACD,CAEA,mSAUC,WAAY,CADZ,QAED,CAEA,oHAEC,MACD,CAEA,oHAEC,OACD,CAEA,kHAGC,QAAS,CACT,0BACD,CAEA,sHAGC,QAAS,CACT,0BACD,CAEA,sHAGC,QAAS,CACT,0BACD,CAQF,mCACC,mCACD,CCpFA,MACC,sDACD,CAEA,gBAEC,iBA2ED,CAzEC,oCACC,mCACD,CAGC,8CAIC,sCAAuC,CAHvC,gCAID,CAIA,8CACC,+BAAgC,CAGhC,oCACD,CAGD,gDC/BA,kCDiCA,CAIE,mFAEC,oCACD,CAIA,mFAEC,qCACD,CAID,iEAEC,eAAgB,CAChB,sBAAuB,CAFvB,SAGD,CAGA,6EC1DD,kCD4DC,CAGA,qDACC,2BAA4B,CAC5B,4BACD,CAEA,sGACC,UACD,CAGA,yHAEC,eAKD,CAHC,qIE7EF,2CF+EE,CAKH,uBGlFC,eH8GD,CA5BA,qFG9EE,qCH0GF,CA5BA,uBAIC,oDAAqD,CACrD,sDAAuD,CACvD,QAAS,CE1FT,oCAA8B,CF6F9B,cAmBD,CAfC,6CACC,wBACD,CAEA,6CACC,yBACD,CAEA,6CACC,2BACD,CAEA,6CACC,4BACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-dropdown-max-width: 75vw;\n}\n\n.ck.ck-dropdown {\n\tdisplay: inline-block;\n\tposition: relative;\n\n\t& .ck-dropdown__arrow {\n\t\tpointer-events: none;\n\t\tz-index: var(--ck-z-default);\n\t}\n\n\t/* Dropdown button should span horizontally, e.g. in vertical toolbars */\n\t& .ck-button.ck-dropdown__button {\n\t\twidth: 100%;\n\t}\n\n\t& .ck-dropdown__panel {\n\t\tdisplay: none;\n\t\tz-index: var(--ck-z-modal);\n\t\tmax-width: var(--ck-dropdown-max-width);\n\n\t\tposition: absolute;\n\n\t\t&.ck-dropdown__panel-visible {\n\t\t\tdisplay: inline-block;\n\t\t}\n\n\t\t&.ck-dropdown__panel_ne,\n\t\t&.ck-dropdown__panel_nw,\n\t\t&.ck-dropdown__panel_n,\n\t\t&.ck-dropdown__panel_nmw,\n\t\t&.ck-dropdown__panel_nme {\n\t\t\tbottom: 100%;\n\t\t}\n\n\t\t&.ck-dropdown__panel_se,\n\t\t&.ck-dropdown__panel_sw,\n\t\t&.ck-dropdown__panel_smw,\n\t\t&.ck-dropdown__panel_sme,\n\t\t&.ck-dropdown__panel_s {\n\t\t\t/*\n\t\t\t * Using transform: translate3d( 0, 100%, 0 ) causes blurry dropdown on Chrome 67-78+ on non-retina displays.\n\t\t\t * See https://github.com/ckeditor/ckeditor5/issues/1053.\n\t\t\t */\n\t\t\ttop: 100%;\n\t\t\tbottom: auto;\n\t\t}\n\n\t\t&.ck-dropdown__panel_ne,\n\t\t&.ck-dropdown__panel_se {\n\t\t\tleft: 0px;\n\t\t}\n\n\t\t&.ck-dropdown__panel_nw,\n\t\t&.ck-dropdown__panel_sw {\n\t\t\tright: 0px;\n\t\t}\n\n\t\t&.ck-dropdown__panel_s,\n\t\t&.ck-dropdown__panel_n {\n\t\t\t/* Positioning panels relative to the center of the button */\n\t\t\tleft: 50%;\n\t\t\ttransform: translateX(-50%);\n\t\t}\n\n\t\t&.ck-dropdown__panel_nmw,\n\t\t&.ck-dropdown__panel_smw {\n\t\t\t/* Positioning panels relative to the middle-west of the button */\n\t\t\tleft: 75%;\n\t\t\ttransform: translateX(-75%);\n\t\t}\n\n\t\t&.ck-dropdown__panel_nme,\n\t\t&.ck-dropdown__panel_sme {\n\t\t\t/* Positioning panels relative to the middle-east of the button */\n\t\t\tleft: 25%;\n\t\t\ttransform: translateX(-25%);\n\t\t}\n\t}\n}\n\n/*\n * Toolbar dropdown panels should be always above the UI (eg. other dropdown panels) from the editor's content.\n * See https://github.com/ckeditor/ckeditor5/issues/7874\n */\n.ck.ck-toolbar .ck-dropdown__panel {\n\tz-index: calc( var(--ck-z-modal) + 1 );\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n@import \"../../../mixins/_disabled.css\";\n@import \"../../../mixins/_shadow.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n:root {\n\t--ck-dropdown-arrow-size: calc(0.5 * var(--ck-icon-size));\n}\n\n.ck.ck-dropdown {\n\t/* Enable font size inheritance, which allows fluid UI scaling. */\n\tfont-size: inherit;\n\n\t& .ck-dropdown__arrow {\n\t\twidth: var(--ck-dropdown-arrow-size);\n\t}\n\n\t@mixin ck-dir ltr {\n\t\t& .ck-dropdown__arrow {\n\t\t\tright: var(--ck-spacing-standard);\n\n\t\t\t/* A space to accommodate the triangle. */\n\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t}\n\t}\n\n\t@mixin ck-dir rtl {\n\t\t& .ck-dropdown__arrow {\n\t\t\tleft: var(--ck-spacing-standard);\n\n\t\t\t/* A space to accommodate the triangle. */\n\t\t\tmargin-right: var(--ck-spacing-small);\n\t\t}\n\t}\n\n\t&.ck-disabled .ck-dropdown__arrow {\n\t\t@mixin ck-disabled;\n\t}\n\n\t& .ck-button.ck-dropdown__button {\n\t\t@mixin ck-dir ltr {\n\t\t\t&:not(.ck-button_with-text) {\n\t\t\t\t/* Make sure dropdowns with just an icon have the right inner spacing */\n\t\t\t\tpadding-left: var(--ck-spacing-small);\n\t\t\t}\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\t&:not(.ck-button_with-text) {\n\t\t\t\t/* Make sure dropdowns with just an icon have the right inner spacing */\n\t\t\t\tpadding-right: var(--ck-spacing-small);\n\t\t\t}\n\t\t}\n\n\t\t/* #23 */\n\t\t& .ck-button__label {\n\t\t\twidth: 7em;\n\t\t\toverflow: hidden;\n\t\t\ttext-overflow: ellipsis;\n\t\t}\n\n\t\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/70 */\n\t\t&.ck-disabled .ck-button__label {\n\t\t\t@mixin ck-disabled;\n\t\t}\n\n\t\t/* https://github.com/ckeditor/ckeditor5/issues/816 */\n\t\t&.ck-on {\n\t\t\tborder-bottom-left-radius: 0;\n\t\t\tborder-bottom-right-radius: 0;\n\t\t}\n\n\t\t&.ck-dropdown__button_label-width_auto .ck-button__label {\n\t\t\twidth: auto;\n\t\t}\n\n\t\t/* https://github.com/ckeditor/ckeditor5/issues/8699 */\n\t\t&.ck-off:active,\n\t\t&.ck-on:active {\n\t\t\tbox-shadow: none;\n\t\t\t\n\t\t\t&:focus {\n\t\t\t\t@mixin ck-box-shadow var(--ck-focus-outer-shadow);\n\t\t\t}\n\t\t}\n\t}\n}\n\n.ck.ck-dropdown__panel {\n\t@mixin ck-rounded-corners;\n\t@mixin ck-drop-shadow;\n\n\tbackground: var(--ck-color-dropdown-panel-background);\n\tborder: 1px solid var(--ck-color-dropdown-panel-border);\n\tbottom: 0;\n\n\t/* Make sure the panel is at least as wide as the drop-down's button. */\n\tmin-width: 100%;\n\n\t/* Disabled corner border radius to be consistent with the .dropdown__button\n\thttps://github.com/ckeditor/ckeditor5/issues/816 */\n\t&.ck-dropdown__panel_se {\n\t\tborder-top-left-radius: 0;\n\t}\n\n\t&.ck-dropdown__panel_sw {\n\t\tborder-top-right-radius: 0;\n\t}\n\n\t&.ck-dropdown__panel_ne {\n\t\tborder-bottom-left-radius: 0;\n\t}\n\n\t&.ck-dropdown__panel_nw {\n\t\tborder-bottom-right-radius: 0;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A class which indicates that an element holding it is disabled.\n */\n@define-mixin ck-disabled {\n\topacity: var(--ck-disabled-opacity);\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6875:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-dropdown .ck-dropdown__panel .ck-list{border-radius:0}.ck-rounded-corners .ck.ck-dropdown .ck-dropdown__panel .ck-list,.ck.ck-dropdown .ck-dropdown__panel .ck-list.ck-rounded-corners{border-radius:var(--ck-border-radius);border-top-left-radius:0}.ck.ck-dropdown .ck-dropdown__panel .ck-list .ck-list__item:first-child .ck-button{border-radius:0}.ck-rounded-corners .ck.ck-dropdown .ck-dropdown__panel .ck-list .ck-list__item:first-child .ck-button,.ck.ck-dropdown .ck-dropdown__panel .ck-list .ck-list__item:first-child .ck-button.ck-rounded-corners{border-radius:var(--ck-border-radius);border-bottom-left-radius:0;border-bottom-right-radius:0;border-top-left-radius:0}.ck.ck-dropdown .ck-dropdown__panel .ck-list .ck-list__item:last-child .ck-button{border-radius:0}.ck-rounded-corners .ck.ck-dropdown .ck-dropdown__panel .ck-list .ck-list__item:last-child .ck-button,.ck.ck-dropdown .ck-dropdown__panel .ck-list .ck-list__item:last-child .ck-button.ck-rounded-corners{border-radius:var(--ck-border-radius);border-top-left-radius:0;border-top-right-radius:0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/dropdown/listdropdown.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAOA,6CCIC,eDqBD,CAzBA,iICQE,qCAAsC,CDJtC,wBAqBF,CAfE,mFCND,eDYC,CANA,6MCFA,qCAAsC,CDKpC,2BAA4B,CAC5B,4BAA6B,CAF7B,wBAIF,CAEA,kFCdD,eDmBC,CALA,2MCVA,qCAAsC,CDYpC,wBAAyB,CACzB,yBAEF","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n\n.ck.ck-dropdown .ck-dropdown__panel .ck-list {\n\t/* Disabled radius of top-left border to be consistent with .dropdown__button\n\thttps://github.com/ckeditor/ckeditor5/issues/816 */\n\t@mixin ck-rounded-corners {\n\t\tborder-top-left-radius: 0;\n\t}\n\n\t/* Make sure the button belonging to the first/last child of the list goes well with the\n\tborder radius of the entire panel. */\n\t& .ck-list__item {\n\t\t&:first-child .ck-button {\n\t\t\t@mixin ck-rounded-corners {\n\t\t\t\tborder-top-left-radius: 0;\n\t\t\t\tborder-bottom-left-radius: 0;\n\t\t\t\tborder-bottom-right-radius: 0;\n\t\t\t}\n\t\t}\n\n\t\t&:last-child .ck-button {\n\t\t\t@mixin ck-rounded-corners {\n\t\t\t\tborder-top-left-radius: 0;\n\t\t\t\tborder-top-right-radius: 0;\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 66:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-splitbutton{font-size:inherit}.ck.ck-splitbutton .ck-splitbutton__action:focus{z-index:calc(var(--ck-z-default) + 1)}:root{--ck-color-split-button-hover-background:#ebebeb;--ck-color-split-button-hover-border:#b3b3b3}[dir=ltr] .ck.ck-splitbutton.ck-splitbutton_open>.ck-splitbutton__action,[dir=ltr] .ck.ck-splitbutton:hover>.ck-splitbutton__action{border-bottom-right-radius:unset;border-top-right-radius:unset}[dir=rtl] .ck.ck-splitbutton.ck-splitbutton_open>.ck-splitbutton__action,[dir=rtl] .ck.ck-splitbutton:hover>.ck-splitbutton__action{border-bottom-left-radius:unset;border-top-left-radius:unset}.ck.ck-splitbutton>.ck-splitbutton__arrow{min-width:unset}[dir=ltr] .ck.ck-splitbutton>.ck-splitbutton__arrow{border-bottom-left-radius:unset;border-top-left-radius:unset}[dir=rtl] .ck.ck-splitbutton>.ck-splitbutton__arrow{border-bottom-right-radius:unset;border-top-right-radius:unset}.ck.ck-splitbutton>.ck-splitbutton__arrow svg{width:var(--ck-dropdown-arrow-size)}.ck.ck-splitbutton.ck-splitbutton_open>.ck-button:not(.ck-on):not(.ck-disabled):not(:hover),.ck.ck-splitbutton:hover>.ck-button:not(.ck-on):not(.ck-disabled):not(:hover){background:var(--ck-color-split-button-hover-background)}.ck.ck-splitbutton.ck-splitbutton_open>.ck-splitbutton__arrow:not(.ck-disabled):after,.ck.ck-splitbutton:hover>.ck-splitbutton__arrow:not(.ck-disabled):after{background-color:var(--ck-color-split-button-hover-border);content:\"\";height:100%;position:absolute;width:1px}[dir=ltr] .ck.ck-splitbutton.ck-splitbutton_open>.ck-splitbutton__arrow:not(.ck-disabled):after,[dir=ltr] .ck.ck-splitbutton:hover>.ck-splitbutton__arrow:not(.ck-disabled):after{left:-1px}[dir=rtl] .ck.ck-splitbutton.ck-splitbutton_open>.ck-splitbutton__arrow:not(.ck-disabled):after,[dir=rtl] .ck.ck-splitbutton:hover>.ck-splitbutton__arrow:not(.ck-disabled):after{right:-1px}.ck.ck-splitbutton.ck-splitbutton_open{border-radius:0}.ck-rounded-corners .ck.ck-splitbutton.ck-splitbutton_open,.ck.ck-splitbutton.ck-splitbutton_open.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck-rounded-corners .ck.ck-splitbutton.ck-splitbutton_open>.ck-splitbutton__action,.ck.ck-splitbutton.ck-splitbutton_open.ck-rounded-corners>.ck-splitbutton__action{border-bottom-left-radius:0}.ck-rounded-corners .ck.ck-splitbutton.ck-splitbutton_open>.ck-splitbutton__arrow,.ck.ck-splitbutton.ck-splitbutton_open.ck-rounded-corners>.ck-splitbutton__arrow{border-bottom-right-radius:0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/splitbutton.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/dropdown/splitbutton.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAKA,mBAEC,iBAKD,CAHC,iDACC,qCACD,CCJD,MACC,gDAAyD,CACzD,4CACD,CAMC,oIAKE,gCAAiC,CADjC,6BASF,CAbA,oIAWE,+BAAgC,CADhC,4BAGF,CAEA,0CAGC,eAiBD,CApBA,oDAQE,+BAAgC,CADhC,4BAaF,CApBA,oDAcE,gCAAiC,CADjC,6BAOF,CAHC,8CACC,mCACD,CASA,0KACC,wDACD,CAIA,8JAKC,0DAA2D,CAJ3D,UAAW,CAGX,WAAY,CAFZ,iBAAkB,CAClB,SAGD,CAGC,kLACC,SACD,CAIA,kLACC,UACD,CAMF,uCC7EA,eDuFA,CAVA,qHCzEC,qCDmFD,CARE,qKACC,2BACD,CAEA,mKACC,4BACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-splitbutton {\n\t/* Enable font size inheritance, which allows fluid UI scaling. */\n\tfont-size: inherit;\n\n\t& .ck-splitbutton__action:focus {\n\t\tz-index: calc(var(--ck-z-default) + 1);\n\t}\n}\n\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n\n:root {\n\t--ck-color-split-button-hover-background: hsl(0, 0%, 92%);\n\t--ck-color-split-button-hover-border: hsl(0, 0%, 70%);\n}\n\n.ck.ck-splitbutton {\n\t/*\n\t * Note: ck-rounded and ck-dir mixins don't go together (because they both use @nest).\n\t */\n\t&:hover > .ck-splitbutton__action,\n\t&.ck-splitbutton_open > .ck-splitbutton__action {\n\t\t@nest [dir=\"ltr\"] & {\n\t\t\t/* Don't round the action button on the right side */\n\t\t\tborder-top-right-radius: unset;\n\t\t\tborder-bottom-right-radius: unset;\n\t\t}\n\n\t\t@nest [dir=\"rtl\"] & {\n\t\t\t/* Don't round the action button on the left side */\n\t\t\tborder-top-left-radius: unset;\n\t\t\tborder-bottom-left-radius: unset;\n\t\t}\n\t}\n\n\t& > .ck-splitbutton__arrow {\n\t\t/* It's a text-less button and since the icon is positioned absolutely in such situation,\n\t\tit must get some arbitrary min-width. */\n\t\tmin-width: unset;\n\n\t\t@nest [dir=\"ltr\"] & {\n\t\t\t/* Don't round the arrow button on the left side */\n\t\t\tborder-top-left-radius: unset;\n\t\t\tborder-bottom-left-radius: unset;\n\t\t}\n\n\t\t@nest [dir=\"rtl\"] & {\n\t\t\t/* Don't round the arrow button on the right side */\n\t\t\tborder-top-right-radius: unset;\n\t\t\tborder-bottom-right-radius: unset;\n\t\t}\n\n\t\t& svg {\n\t\t\twidth: var(--ck-dropdown-arrow-size);\n\t\t}\n\t}\n\n\t/* When the split button is \"open\" (the arrow is on) or being hovered, it should get some styling\n\tas a whole. The background of both buttons should stand out and there should be a visual\n\tseparation between both buttons. */\n\t&.ck-splitbutton_open,\n\t&:hover {\n\t\t/* When the split button hovered as a whole, not as individual buttons. */\n\t\t& > .ck-button:not(.ck-on):not(.ck-disabled):not(:hover) {\n\t\t\tbackground: var(--ck-color-split-button-hover-background);\n\t\t}\n\n\t\t/* Splitbutton separator needs to be set with the ::after pseudoselector\n\t\tto display properly the borders on focus */\n\t\t& > .ck-splitbutton__arrow:not(.ck-disabled)::after {\n\t\t\tcontent: '';\n\t\t\tposition: absolute;\n\t\t\twidth: 1px;\n\t\t\theight: 100%;\n\t\t\tbackground-color: var(--ck-color-split-button-hover-border);\n\t\t}\n\n\t\t@nest [dir=\"ltr\"] & {\n\t\t\t& > .ck-splitbutton__arrow:not(.ck-disabled)::after {\n\t\t\t\tleft: -1px;\n\t\t\t}\n\t\t}\n\n\t\t@nest [dir=\"rtl\"] & {\n\t\t\t& > .ck-splitbutton__arrow:not(.ck-disabled)::after {\n\t\t\t\tright: -1px;\n\t\t\t}\n\t\t}\n\t}\n\n\t/* Don't round the bottom left and right corners of the buttons when \"open\"\n\thttps://github.com/ckeditor/ckeditor5/issues/816 */\n\t&.ck-splitbutton_open {\n\t\t@mixin ck-rounded-corners {\n\t\t\t& > .ck-splitbutton__action {\n\t\t\t\tborder-bottom-left-radius: 0;\n\t\t\t}\n\n\t\t\t& > .ck-splitbutton__arrow {\n\t\t\t\tborder-bottom-right-radius: 0;\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5075:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-toolbar-dropdown-max-width:60vw}.ck.ck-toolbar-dropdown>.ck-dropdown__panel{max-width:var(--ck-toolbar-dropdown-max-width);width:max-content}.ck.ck-toolbar-dropdown>.ck-dropdown__panel .ck-button:focus{z-index:calc(var(--ck-z-default) + 1)}.ck.ck-toolbar-dropdown .ck-toolbar{border:0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/toolbardropdown.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/dropdown/toolbardropdown.css"],"names":[],"mappings":"AAKA,MACC,oCACD,CAEA,4CAGC,8CAA+C,CAD/C,iBAQD,CAJE,6DACC,qCACD,CCZF,oCACC,QACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-toolbar-dropdown-max-width: 60vw;\n}\n\n.ck.ck-toolbar-dropdown > .ck-dropdown__panel {\n\t/* https://github.com/ckeditor/ckeditor5/issues/5586 */\n\twidth: max-content;\n\tmax-width: var(--ck-toolbar-dropdown-max-width);\n\n\t& .ck-button {\n\t\t&:focus {\n\t\t\tz-index: calc(var(--ck-z-default) + 1);\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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.ck.ck-toolbar-dropdown .ck-toolbar {\n\tborder: 0;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 4547:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-editable-blur-selection:#d9d9d9}.ck.ck-editor__editable:not(.ck-editor__nested-editable){border-radius:0}.ck-rounded-corners .ck.ck-editor__editable:not(.ck-editor__nested-editable),.ck.ck-editor__editable.ck-rounded-corners:not(.ck-editor__nested-editable){border-radius:var(--ck-border-radius)}.ck.ck-editor__editable.ck-focused:not(.ck-editor__nested-editable){border:var(--ck-focus-ring);box-shadow:var(--ck-inner-shadow),0 0;outline:none}.ck.ck-editor__editable_inline{border:1px solid transparent;overflow:auto;padding:0 var(--ck-spacing-standard)}.ck.ck-editor__editable_inline[dir=ltr]{text-align:left}.ck.ck-editor__editable_inline[dir=rtl]{text-align:right}.ck.ck-editor__editable_inline>:first-child{margin-top:var(--ck-spacing-large)}.ck.ck-editor__editable_inline>:last-child{margin-bottom:var(--ck-spacing-large)}.ck.ck-editor__editable_inline.ck-blurred ::selection{background:var(--ck-color-editable-blur-selection)}.ck.ck-balloon-panel.ck-toolbar-container[class*=arrow_n]:after{border-bottom-color:var(--ck-color-base-foreground)}.ck.ck-balloon-panel.ck-toolbar-container[class*=arrow_s]:after{border-top-color:var(--ck-color-base-foreground)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/editorui/editorui.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_focus.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css"],"names":[],"mappings":"AAWA,MACC,0CACD,CAEA,yDCJC,eDWD,CAPA,yJCAE,qCDOF,CAJC,oEEPA,2BAA2B,CCF3B,qCAA8B,CDC9B,YFWA,CAGD,+BAGC,4BAA6B,CAF7B,aAAc,CACd,oCA6BD,CA1BC,wCACC,eACD,CAEA,wCACC,gBACD,CAGA,4CACC,kCACD,CAGA,2CAKC,qCACD,CAGA,sDACC,kDACD,CAKA,gEACC,mDACD,CAIA,gEACC,gDACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n@import \"../../../mixins/_disabled.css\";\n@import \"../../../mixins/_shadow.css\";\n@import \"../../../mixins/_focus.css\";\n@import \"../../mixins/_button.css\";\n\n:root {\n\t--ck-color-editable-blur-selection: hsl(0, 0%, 85%);\n}\n\n.ck.ck-editor__editable:not(.ck-editor__nested-editable) {\n\t@mixin ck-rounded-corners;\n\n\t&.ck-focused {\n\t\t@mixin ck-focus-ring;\n\t\t@mixin ck-box-shadow var(--ck-inner-shadow);\n\t}\n}\n\n.ck.ck-editor__editable_inline {\n\toverflow: auto;\n\tpadding: 0 var(--ck-spacing-standard);\n\tborder: 1px solid transparent;\n\n\t&[dir=\"ltr\"] {\n\t\ttext-align: left;\n\t}\n\n\t&[dir=\"rtl\"] {\n\t\ttext-align: right;\n\t}\n\n\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/116 */\n\t& > *:first-child {\n\t\tmargin-top: var(--ck-spacing-large);\n\t}\n\n\t/* https://github.com/ckeditor/ckeditor5/issues/847 */\n\t& > *:last-child {\n\t\t/*\n\t\t * This value should match with the default margins of the block elements (like .media or .image)\n\t\t * to avoid a content jumping when the fake selection container shows up (See https://github.com/ckeditor/ckeditor5/issues/9825).\n\t\t */\n\t\tmargin-bottom: var(--ck-spacing-large);\n\t}\n\n\t/* https://github.com/ckeditor/ckeditor5/issues/6517 */\n\t&.ck-blurred ::selection {\n\t\tbackground: var(--ck-color-editable-blur-selection);\n\t}\n}\n\n/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/111 */\n.ck.ck-balloon-panel.ck-toolbar-container[class*=\"arrow_n\"] {\n\t&::after {\n\t\tborder-bottom-color: var(--ck-color-base-foreground);\n\t}\n}\n\n.ck.ck-balloon-panel.ck-toolbar-container[class*=\"arrow_s\"] {\n\t&::after {\n\t\tborder-top-color: var(--ck-color-base-foreground);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A visual style of focused element's border.\n */\n@define-mixin ck-focus-ring {\n\t/* Disable native outline. */\n\toutline: none;\n\tborder: var(--ck-focus-ring)\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5523:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-form__header{align-items:center;display:flex;flex-direction:row;flex-wrap:nowrap;justify-content:space-between}:root{--ck-form-header-height:38px}.ck.ck-form__header{border-bottom:1px solid var(--ck-color-base-border);height:var(--ck-form-header-height);line-height:var(--ck-form-header-height);padding:var(--ck-spacing-small) var(--ck-spacing-large)}.ck.ck-form__header .ck-form__header__label{font-weight:700}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/formheader/formheader.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/formheader/formheader.css"],"names":[],"mappings":"AAKA,oBAIC,kBAAmB,CAHnB,YAAa,CACb,kBAAmB,CACnB,gBAAiB,CAEjB,6BACD,CCNA,MACC,4BACD,CAEA,oBAIC,mDAAoD,CAFpD,mCAAoC,CACpC,wCAAyC,CAFzC,uDAQD,CAHC,4CACC,eACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-form__header {\n\tdisplay: flex;\n\tflex-direction: row;\n\tflex-wrap: nowrap;\n\talign-items: center;\n\tjustify-content: space-between;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-form-header-height: 38px;\n}\n\n.ck.ck-form__header {\n\tpadding: var(--ck-spacing-small) var(--ck-spacing-large);\n\theight: var(--ck-form-header-height);\n\tline-height: var(--ck-form-header-height);\n\tborder-bottom: 1px solid var(--ck-color-base-border);\n\n\t& .ck-form__header__label {\n\t\tfont-weight: bold;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 1174:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-icon{vertical-align:middle}:root{--ck-icon-size:calc(var(--ck-line-height-base)*var(--ck-font-size-normal))}.ck.ck-icon{font-size:.8333350694em;height:var(--ck-icon-size);width:var(--ck-icon-size);will-change:transform}.ck.ck-icon,.ck.ck-icon *{cursor:inherit}.ck.ck-icon.ck-icon_inherit-color,.ck.ck-icon.ck-icon_inherit-color *{color:inherit}.ck.ck-icon.ck-icon_inherit-color :not([fill]){fill:currentColor}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/icon/icon.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/icon/icon.css"],"names":[],"mappings":"AAKA,YACC,qBACD,CCFA,MACC,0EACD,CAEA,YAKC,uBAAwB,CAHxB,0BAA2B,CAD3B,yBAA0B,CAU1B,qBAoBD,CAlBC,0BALA,cAQA,CAMC,sEACC,aAMD,CAJC,+CAEC,iBACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-icon {\n\tvertical-align: middle;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-icon-size: calc(var(--ck-line-height-base) * var(--ck-font-size-normal));\n}\n\n.ck.ck-icon {\n\twidth: var(--ck-icon-size);\n\theight: var(--ck-icon-size);\n\n\t/* Multiplied by the height of the line in \"px\" should give SVG \"viewport\" dimensions */\n\tfont-size: .8333350694em;\n\n\t/* Inherit cursor style (#5). */\n\tcursor: inherit;\n\n\t/* This will prevent blurry icons on Firefox. See #340. */\n\twill-change: transform;\n\n\t& * {\n\t\t/* Inherit cursor style (#5). */\n\t\tcursor: inherit;\n\t}\n\n\t/* Allows dynamic coloring of an icon by inheriting its color from the parent. */\n\t&.ck-icon_inherit-color {\n\t\tcolor: inherit;\n\n\t\t& * {\n\t\t\tcolor: inherit;\n\n\t\t\t&:not([fill]) {\n\t\t\t\t/* Needed by FF. */\n\t\t\t\tfill: currentColor;\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6985:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-input-width:18em;--ck-input-text-width:var(--ck-input-width)}.ck.ck-input{border-radius:0}.ck-rounded-corners .ck.ck-input,.ck.ck-input.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-input{background:var(--ck-color-input-background);border:1px solid var(--ck-color-input-border);min-height:var(--ck-ui-component-min-height);min-width:var(--ck-input-width);padding:var(--ck-spacing-extra-tiny) var(--ck-spacing-medium);transition:box-shadow .1s ease-in-out,border .1s ease-in-out}.ck.ck-input:focus{border:var(--ck-focus-ring);box-shadow:var(--ck-focus-outer-shadow),0 0;outline:none}.ck.ck-input[readonly]{background:var(--ck-color-input-disabled-background);border:1px solid var(--ck-color-input-disabled-border);color:var(--ck-color-input-disabled-text)}.ck.ck-input[readonly]:focus{box-shadow:var(--ck-focus-disabled-outer-shadow),0 0}.ck.ck-input.ck-error{animation:ck-input-shake .3s ease both;border-color:var(--ck-color-input-error-border)}.ck.ck-input.ck-error:focus{box-shadow:var(--ck-focus-error-outer-shadow),0 0}@keyframes ck-input-shake{20%{transform:translateX(-2px)}40%{transform:translateX(2px)}60%{transform:translateX(-1px)}80%{transform:translateX(1px)}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/input/input.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_focus.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css"],"names":[],"mappings":"AASA,MACC,qBAAsB,CAGtB,2CACD,CAEA,aCLC,eD2CD,CAtCA,iECDE,qCDuCF,CAtCA,aAGC,2CAA4C,CAC5C,6CAA8C,CAK9C,4CAA6C,CAH7C,+BAAgC,CADhC,6DAA8D,CAO9D,4DA0BD,CAxBC,mBEnBA,2BAA2B,CCF3B,2CAA8B,CDC9B,YFuBA,CAEA,uBAEC,oDAAqD,CADrD,sDAAuD,CAEvD,yCAMD,CAJC,6BG/BD,oDHkCC,CAGD,sBAEC,sCAAuC,CADvC,+CAMD,CAHC,4BGzCD,iDH2CC,CAIF,0BACC,IACC,0BACD,CAEA,IACC,yBACD,CAEA,IACC,0BACD,CAEA,IACC,yBACD,CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n@import \"../../../mixins/_focus.css\";\n@import \"../../../mixins/_shadow.css\";\n\n:root {\n\t--ck-input-width: 18em;\n\n\t/* Backward compatibility. */\n\t--ck-input-text-width: var(--ck-input-width);\n}\n\n.ck.ck-input {\n\t@mixin ck-rounded-corners;\n\n\tbackground: var(--ck-color-input-background);\n\tborder: 1px solid var(--ck-color-input-border);\n\tpadding: var(--ck-spacing-extra-tiny) var(--ck-spacing-medium);\n\tmin-width: var(--ck-input-width);\n\n\t/* This is important to stay of the same height as surrounding buttons */\n\tmin-height: var(--ck-ui-component-min-height);\n\n\t/* Apply some smooth transition to the box-shadow and border. */\n\ttransition: box-shadow .1s ease-in-out, border .1s ease-in-out;\n\n\t&:focus {\n\t\t@mixin ck-focus-ring;\n\t\t@mixin ck-box-shadow var(--ck-focus-outer-shadow);\n\t}\n\n\t&[readonly] {\n\t\tborder: 1px solid var(--ck-color-input-disabled-border);\n\t\tbackground: var(--ck-color-input-disabled-background);\n\t\tcolor: var(--ck-color-input-disabled-text);\n\n\t\t&:focus {\n\t\t\t/* The read-only input should have a slightly less visible shadow when focused. */\n\t\t\t@mixin ck-box-shadow var(--ck-focus-disabled-outer-shadow);\n\t\t}\n\t}\n\n\t&.ck-error {\n\t\tborder-color: var(--ck-color-input-error-border);\n\t\tanimation: ck-input-shake .3s ease both;\n\n\t\t&:focus {\n\t\t\t@mixin ck-box-shadow var(--ck-focus-error-outer-shadow);\n\t\t}\n\t}\n}\n\n@keyframes ck-input-shake {\n\t20% {\n\t\ttransform: translateX(-2px);\n\t}\n\n\t40% {\n\t\ttransform: translateX(2px);\n\t}\n\n\t60% {\n\t\ttransform: translateX(-1px);\n\t}\n\n\t80% {\n\t\ttransform: translateX(1px);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A visual style of focused element's border.\n */\n@define-mixin ck-focus-ring {\n\t/* Disable native outline. */\n\toutline: none;\n\tborder: var(--ck-focus-ring)\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 2751:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-label{display:block}.ck.ck-voice-label{display:none}.ck.ck-label{font-weight:700}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/label/label.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/label/label.css"],"names":[],"mappings":"AAKA,aACC,aACD,CAEA,mBACC,YACD,CCNA,aACC,eACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-label {\n\tdisplay: block;\n}\n\n.ck.ck-voice-label {\n\tdisplay: none;\n}\n","/*\n * Copyright (c) 2003-2022, 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.ck.ck-label {\n\tfont-weight: bold;\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8111:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-labeled-field-view>.ck.ck-labeled-field-view__input-wrapper{display:flex;position:relative}.ck.ck-labeled-field-view .ck.ck-label{display:block;position:absolute}:root{--ck-labeled-field-view-transition:.1s cubic-bezier(0,0,0.24,0.95);--ck-labeled-field-empty-unfocused-max-width:100% - 2 * var(--ck-spacing-medium);--ck-labeled-field-label-default-position-x:var(--ck-spacing-medium);--ck-labeled-field-label-default-position-y:calc(var(--ck-font-size-base)*0.6);--ck-color-labeled-field-label-background:var(--ck-color-base-background)}.ck.ck-labeled-field-view{border-radius:0}.ck-rounded-corners .ck.ck-labeled-field-view,.ck.ck-labeled-field-view.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-labeled-field-view>.ck.ck-labeled-field-view__input-wrapper{width:100%}.ck.ck-labeled-field-view>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{top:0}[dir=ltr] .ck.ck-labeled-field-view>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{left:0}[dir=rtl] .ck.ck-labeled-field-view>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{right:0}.ck.ck-labeled-field-view>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{background:var(--ck-color-labeled-field-label-background);font-weight:400;line-height:normal;max-width:100%;overflow:hidden;padding:0 calc(var(--ck-font-size-tiny)*.5);pointer-events:none;text-overflow:ellipsis;transform:translate(var(--ck-spacing-medium),-6px) scale(.75);transform-origin:0 0;transition:transform var(--ck-labeled-field-view-transition),padding var(--ck-labeled-field-view-transition),background var(--ck-labeled-field-view-transition)}.ck.ck-labeled-field-view.ck-error .ck-input:not([readonly])+.ck.ck-label,.ck.ck-labeled-field-view.ck-error>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{color:var(--ck-color-base-error)}.ck.ck-labeled-field-view .ck-labeled-field-view__status{font-size:var(--ck-font-size-small);margin-top:var(--ck-spacing-small);white-space:normal}.ck.ck-labeled-field-view .ck-labeled-field-view__status.ck-labeled-field-view__status_error{color:var(--ck-color-base-error)}.ck.ck-labeled-field-view.ck-disabled>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label,.ck.ck-labeled-field-view.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused)>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{color:var(--ck-color-input-disabled-text)}[dir=ltr] .ck.ck-labeled-field-view.ck-disabled.ck-labeled-field-view_empty>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label,[dir=ltr] .ck.ck-labeled-field-view.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused):not(.ck-labeled-field-view_placeholder)>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{transform:translate(var(--ck-labeled-field-label-default-position-x),var(--ck-labeled-field-label-default-position-y)) scale(1)}[dir=rtl] .ck.ck-labeled-field-view.ck-disabled.ck-labeled-field-view_empty>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label,[dir=rtl] .ck.ck-labeled-field-view.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused):not(.ck-labeled-field-view_placeholder)>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{transform:translate(calc(var(--ck-labeled-field-label-default-position-x)*-1),var(--ck-labeled-field-label-default-position-y)) scale(1)}.ck.ck-labeled-field-view.ck-disabled.ck-labeled-field-view_empty>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label,.ck.ck-labeled-field-view.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused):not(.ck-labeled-field-view_placeholder)>.ck.ck-labeled-field-view__input-wrapper>.ck.ck-label{background:transparent;max-width:calc(var(--ck-labeled-field-empty-unfocused-max-width));padding:0}.ck.ck-labeled-field-view>.ck.ck-labeled-field-view__input-wrapper>.ck-dropdown>.ck.ck-button{background:transparent}.ck.ck-labeled-field-view.ck-labeled-field-view_empty>.ck.ck-labeled-field-view__input-wrapper>.ck-dropdown>.ck-button>.ck-button__label{opacity:0}.ck.ck-labeled-field-view.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused):not(.ck-labeled-field-view_placeholder)>.ck.ck-labeled-field-view__input-wrapper>.ck-dropdown+.ck-label{max-width:calc(var(--ck-labeled-field-empty-unfocused-max-width) - var(--ck-dropdown-arrow-size) - var(--ck-spacing-standard))}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/labeledfield/labeledfieldview.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/labeledfield/labeledfieldview.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAMC,mEACC,YAAa,CACb,iBACD,CAEA,uCACC,aAAc,CACd,iBACD,CCND,MACC,kEAAsE,CACtE,gFAAiF,CACjF,oEAAqE,CACrE,8EAAiF,CACjF,yEACD,CAEA,0BCLC,eD8GD,CAzGA,2FCDE,qCD0GF,CAtGC,mEACC,UAmCD,CAjCC,gFACC,KA+BD,CAhCA,0FAIE,MA4BF,CAhCA,0FAQE,OAwBF,CAhCA,gFAiBC,yDAA0D,CAG1D,eAAmB,CADnB,kBAAoB,CAOpB,cAAe,CAFf,eAAgB,CANhB,2CAA8C,CAP9C,mBAAoB,CAYpB,sBAAuB,CARvB,6DAA+D,CAH/D,oBAAqB,CAgBrB,+JAID,CAQA,mKACC,gCACD,CAGD,yDACC,mCAAoC,CACpC,kCAAmC,CAInC,kBAKD,CAHC,6FACC,gCACD,CAID,4OAEC,yCACD,CAIA,oUAGE,+HAYF,CAfA,oUAOE,wIAQF,CAfA,gTAaC,sBAAuB,CAFvB,iEAAkE,CAGlE,SACD,CAKA,8FACC,sBACD,CAGA,yIACC,SACD,CAGA,kMACC,8HACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-labeled-field-view {\n\t& > .ck.ck-labeled-field-view__input-wrapper {\n\t\tdisplay: flex;\n\t\tposition: relative;\n\t}\n\n\t& .ck.ck-label {\n\t\tdisplay: block;\n\t\tposition: absolute;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n@import \"../../../mixins/_rounded.css\";\n\n:root {\n\t--ck-labeled-field-view-transition: .1s cubic-bezier(0, 0, 0.24, 0.95);\n\t--ck-labeled-field-empty-unfocused-max-width: 100% - 2 * var(--ck-spacing-medium);\n\t--ck-labeled-field-label-default-position-x: var(--ck-spacing-medium);\n\t--ck-labeled-field-label-default-position-y: calc(0.6 * var(--ck-font-size-base));\n\t--ck-color-labeled-field-label-background: var(--ck-color-base-background);\n}\n\n.ck.ck-labeled-field-view {\n\t@mixin ck-rounded-corners;\n\n\t& > .ck.ck-labeled-field-view__input-wrapper {\n\t\twidth: 100%;\n\n\t\t& > .ck.ck-label {\n\t\t\ttop: 0px;\n\n\t\t\t@mixin ck-dir ltr {\n\t\t\t\tleft: 0px;\n\t\t\t}\n\n\t\t\t@mixin ck-dir rtl {\n\t\t\t\tright: 0px;\n\t\t\t}\n\n\t\t\tpointer-events: none;\n\t\t\ttransform-origin: 0 0;\n\n\t\t\t/* By default, display the label scaled down above the field. */\n\t\t\ttransform: translate(var(--ck-spacing-medium), -6px) scale(.75);\n\n\t\t\tbackground: var(--ck-color-labeled-field-label-background);\n\t\t\tpadding: 0 calc(.5 * var(--ck-font-size-tiny));\n\t\t\tline-height: initial;\n\t\t\tfont-weight: normal;\n\n\t\t\t/* Prevent overflow when the label is longer than the input */\n\t\t\ttext-overflow: ellipsis;\n\t\t\toverflow: hidden;\n\n\t\t\tmax-width: 100%;\n\n\t\t\ttransition:\n\t\t\t\ttransform var(--ck-labeled-field-view-transition),\n\t\t\t\tpadding var(--ck-labeled-field-view-transition),\n\t\t\t\tbackground var(--ck-labeled-field-view-transition);\n\t\t}\n\t}\n\n\t&.ck-error {\n\t\t& > .ck.ck-labeled-field-view__input-wrapper > .ck.ck-label {\n\t\t\tcolor: var(--ck-color-base-error);\n\t\t}\n\n\t\t& .ck-input:not([readonly]) + .ck.ck-label {\n\t\t\tcolor: var(--ck-color-base-error);\n\t\t}\n\t}\n\n\t& .ck-labeled-field-view__status {\n\t\tfont-size: var(--ck-font-size-small);\n\t\tmargin-top: var(--ck-spacing-small);\n\n\t\t/* Let the info wrap to the next line to avoid stretching the layout horizontally.\n\t\tThe status could be very long. */\n\t\twhite-space: normal;\n\n\t\t&.ck-labeled-field-view__status_error {\n\t\t\tcolor: var(--ck-color-base-error);\n\t\t}\n\t}\n\n\t/* Disabled fields and fields that have no focus should fade out. */\n\t&.ck-disabled > .ck.ck-labeled-field-view__input-wrapper > .ck.ck-label,\n\t&.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused) > .ck.ck-labeled-field-view__input-wrapper > .ck.ck-label {\n\t\tcolor: var(--ck-color-input-disabled-text);\n\t}\n\n\t/* Fields that are disabled or not focused and without a placeholder should have full-sized labels. */\n\t/* stylelint-disable-next-line no-descending-specificity */\n\t&.ck-disabled.ck-labeled-field-view_empty > .ck.ck-labeled-field-view__input-wrapper > .ck.ck-label,\n\t&.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused):not(.ck-labeled-field-view_placeholder) > .ck.ck-labeled-field-view__input-wrapper > .ck.ck-label {\n\t\t@mixin ck-dir ltr {\n\t\t\ttransform: translate(var(--ck-labeled-field-label-default-position-x), var(--ck-labeled-field-label-default-position-y)) scale(1);\n\t\t}\n\n\t\t@mixin ck-dir rtl {\n\t\t\ttransform: translate(calc(-1 * var(--ck-labeled-field-label-default-position-x)), var(--ck-labeled-field-label-default-position-y)) scale(1);\n\t\t}\n\n\t\t/* Compensate for the default translate position. */\n\t\tmax-width: calc(var(--ck-labeled-field-empty-unfocused-max-width));\n\n\t\tbackground: transparent;\n\t\tpadding: 0;\n\t}\n\n\t/*------ DropdownView integration ----------------------------------------------------------------------------------- */\n\n\t/* Make sure dropdown' background color in any of dropdown's state does not collide with labeled field. */\n\t& > .ck.ck-labeled-field-view__input-wrapper > .ck-dropdown > .ck.ck-button {\n\t\tbackground: transparent;\n\t}\n\n\t/* When the dropdown is \"empty\", the labeled field label replaces its label. */\n\t&.ck-labeled-field-view_empty > .ck.ck-labeled-field-view__input-wrapper > .ck-dropdown > .ck-button > .ck-button__label {\n\t\topacity: 0;\n\t}\n\n\t/* Make sure the label of the empty, unfocused input does not cover the dropdown arrow. */\n\t&.ck-labeled-field-view_empty:not(.ck-labeled-field-view_focused):not(.ck-labeled-field-view_placeholder) > .ck.ck-labeled-field-view__input-wrapper > .ck-dropdown + .ck-label {\n\t\tmax-width: calc(var(--ck-labeled-field-empty-unfocused-max-width) - var(--ck-dropdown-arrow-size) - var(--ck-spacing-standard));\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 1162:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-list{display:flex;flex-direction:column;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.ck.ck-list .ck-list__item,.ck.ck-list .ck-list__separator{display:block}.ck.ck-list .ck-list__item>:focus{position:relative;z-index:var(--ck-z-default)}.ck.ck-list{border-radius:0}.ck-rounded-corners .ck.ck-list,.ck.ck-list.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-list{background:var(--ck-color-list-background);list-style-type:none}.ck.ck-list__item{cursor:default;min-width:12em}.ck.ck-list__item .ck-button{border-radius:0;min-height:unset;padding:calc(var(--ck-line-height-base)*.2*var(--ck-font-size-base)) calc(var(--ck-line-height-base)*.4*var(--ck-font-size-base));text-align:left;width:100%}.ck.ck-list__item .ck-button .ck-button__label{line-height:calc(var(--ck-line-height-base)*1.2*var(--ck-font-size-base))}.ck.ck-list__item .ck-button:active{box-shadow:none}.ck.ck-list__item .ck-button.ck-on{background:var(--ck-color-list-button-on-background);color:var(--ck-color-list-button-on-text)}.ck.ck-list__item .ck-button.ck-on:active{box-shadow:none}.ck.ck-list__item .ck-button.ck-on:hover:not(.ck-disabled){background:var(--ck-color-list-button-on-background-focus)}.ck.ck-list__item .ck-button.ck-on:focus:not(.ck-switchbutton):not(.ck-disabled){border-color:var(--ck-color-base-background)}.ck.ck-list__item .ck-button:hover:not(.ck-disabled){background:var(--ck-color-list-button-hover-background)}.ck.ck-list__item .ck-switchbutton.ck-on{background:var(--ck-color-list-background);color:inherit}.ck.ck-list__item .ck-switchbutton.ck-on:hover:not(.ck-disabled){background:var(--ck-color-list-button-hover-background);color:inherit}.ck.ck-list__separator{background:var(--ck-color-base-border);height:1px;width:100%}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/list/list.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_unselectable.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/list/list.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAOA,YAGC,YAAa,CACb,qBAAsB,CCFtB,qBAAsB,CACtB,wBAAyB,CACzB,oBAAqB,CACrB,gBDaD,CAZC,2DAEC,aACD,CAKA,kCACC,iBAAkB,CAClB,2BACD,CEfD,YCEC,eDGD,CALA,+DCME,qCDDF,CALA,YAIC,0CAA2C,CAD3C,oBAED,CAEA,kBACC,cAAe,CACf,cA2DD,CAzDC,6BAIC,eAAgB,CAHhB,gBAAiB,CAQjB,iIAEiE,CARjE,eAAgB,CADhB,UAwCD,CA7BC,+CAEC,yEACD,CAEA,oCACC,eACD,CAEA,mCACC,oDAAqD,CACrD,yCAaD,CAXC,0CACC,eACD,CAEA,2DACC,0DACD,CAEA,iFACC,4CACD,CAGD,qDACC,uDACD,CAMA,yCACC,0CAA2C,CAC3C,aAMD,CAJC,iEACC,uDAAwD,CACxD,aACD,CAKH,uBAGC,sCAAuC,CAFvC,UAAW,CACX,UAED","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"../../mixins/_unselectable.css\";\n\n.ck.ck-list {\n\t@mixin ck-unselectable;\n\n\tdisplay: flex;\n\tflex-direction: column;\n\n\t& .ck-list__item,\n\t& .ck-list__separator {\n\t\tdisplay: block;\n\t}\n\n\t/* Make sure that whatever child of the list item gets focus, it remains on the\n\ttop. Thanks to that, styles like box-shadow, outline, etc. are not masked by\n\tadjacent list items. */\n\t& .ck-list__item > *:focus {\n\t\tposition: relative;\n\t\tz-index: var(--ck-z-default);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Makes element unselectable.\n */\n@define-mixin ck-unselectable {\n\t-moz-user-select: none;\n\t-webkit-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_disabled.css\";\n@import \"../../../mixins/_rounded.css\";\n@import \"../../../mixins/_shadow.css\";\n\n.ck.ck-list {\n\t@mixin ck-rounded-corners;\n\n\tlist-style-type: none;\n\tbackground: var(--ck-color-list-background);\n}\n\n.ck.ck-list__item {\n\tcursor: default;\n\tmin-width: 12em;\n\n\t& .ck-button {\n\t\tmin-height: unset;\n\t\twidth: 100%;\n\t\ttext-align: left;\n\t\tborder-radius: 0;\n\n\t\t/* List items should have the same height. Use absolute units to make sure it is so\n\t\t   because e.g. different heading styles may have different height\n\t\t   https://github.com/ckeditor/ckeditor5-heading/issues/63 */\n\t\tpadding:\n\t\t\tcalc(.2 * var(--ck-line-height-base) * var(--ck-font-size-base))\n\t\t\tcalc(.4 * var(--ck-line-height-base) * var(--ck-font-size-base));\n\n\t\t& .ck-button__label {\n\t\t\t/* https://github.com/ckeditor/ckeditor5-heading/issues/63 */\n\t\t\tline-height: calc(1.2 * var(--ck-line-height-base) * var(--ck-font-size-base));\n\t\t}\n\n\t\t&:active {\n\t\t\tbox-shadow: none;\n\t\t}\n\n\t\t&.ck-on {\n\t\t\tbackground: var(--ck-color-list-button-on-background);\n\t\t\tcolor: var(--ck-color-list-button-on-text);\n\n\t\t\t&:active {\n\t\t\t\tbox-shadow: none;\n\t\t\t}\n\n\t\t\t&:hover:not(.ck-disabled) {\n\t\t\t\tbackground: var(--ck-color-list-button-on-background-focus);\n\t\t\t}\n\n\t\t\t&:focus:not(.ck-switchbutton):not(.ck-disabled) {\n\t\t\t\tborder-color: var(--ck-color-base-background);\n\t\t\t}\n\t\t}\n\n\t\t&:hover:not(.ck-disabled) {\n\t\t\tbackground: var(--ck-color-list-button-hover-background);\n\t\t}\n\t}\n\n\t/* It's unnecessary to change the background/text of a switch toggle; it has different ways\n\tof conveying its state (like the switcher) */\n\t& .ck-switchbutton {\n\t\t&.ck-on {\n\t\t\tbackground: var(--ck-color-list-background);\n\t\t\tcolor: inherit;\n\n\t\t\t&:hover:not(.ck-disabled) {\n\t\t\t\tbackground: var(--ck-color-list-button-hover-background);\n\t\t\t\tcolor: inherit;\n\t\t\t}\n\t\t}\n\t}\n}\n\n.ck.ck-list__separator {\n\theight: 1px;\n\twidth: 100%;\n\tbackground: var(--ck-color-base-border);\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 8245:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-balloon-panel-arrow-z-index:calc(var(--ck-z-default) - 3)}.ck.ck-balloon-panel{display:none;position:absolute;z-index:var(--ck-z-modal)}.ck.ck-balloon-panel.ck-balloon-panel_with-arrow:after,.ck.ck-balloon-panel.ck-balloon-panel_with-arrow:before{content:\"\";position:absolute}.ck.ck-balloon-panel.ck-balloon-panel_with-arrow:before{z-index:var(--ck-balloon-panel-arrow-z-index)}.ck.ck-balloon-panel.ck-balloon-panel_with-arrow:after{z-index:calc(var(--ck-balloon-panel-arrow-z-index) + 1)}.ck.ck-balloon-panel[class*=arrow_n]:before{z-index:var(--ck-balloon-panel-arrow-z-index)}.ck.ck-balloon-panel[class*=arrow_n]:after{z-index:calc(var(--ck-balloon-panel-arrow-z-index) + 1)}.ck.ck-balloon-panel[class*=arrow_s]:before{z-index:var(--ck-balloon-panel-arrow-z-index)}.ck.ck-balloon-panel[class*=arrow_s]:after{z-index:calc(var(--ck-balloon-panel-arrow-z-index) + 1)}.ck.ck-balloon-panel.ck-balloon-panel_visible{display:block}:root{--ck-balloon-border-width:1px;--ck-balloon-arrow-offset:2px;--ck-balloon-arrow-height:10px;--ck-balloon-arrow-half-width:8px;--ck-balloon-arrow-drop-shadow:0 2px 2px var(--ck-color-shadow-drop)}.ck.ck-balloon-panel{border-radius:0}.ck-rounded-corners .ck.ck-balloon-panel,.ck.ck-balloon-panel.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-balloon-panel{background:var(--ck-color-panel-background);border:var(--ck-balloon-border-width) solid var(--ck-color-panel-border);box-shadow:var(--ck-drop-shadow),0 0;min-height:15px}.ck.ck-balloon-panel.ck-balloon-panel_with-arrow:after,.ck.ck-balloon-panel.ck-balloon-panel_with-arrow:before{border-style:solid;height:0;width:0}.ck.ck-balloon-panel[class*=arrow_n]:after,.ck.ck-balloon-panel[class*=arrow_n]:before{border-width:0 var(--ck-balloon-arrow-half-width) var(--ck-balloon-arrow-height) var(--ck-balloon-arrow-half-width)}.ck.ck-balloon-panel[class*=arrow_n]:before{border-color:transparent transparent var(--ck-color-panel-border) transparent;margin-top:calc(var(--ck-balloon-border-width)*-1)}.ck.ck-balloon-panel[class*=arrow_n]:after{border-color:transparent transparent var(--ck-color-panel-background) transparent;margin-top:calc(var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width))}.ck.ck-balloon-panel[class*=arrow_s]:after,.ck.ck-balloon-panel[class*=arrow_s]:before{border-width:var(--ck-balloon-arrow-height) var(--ck-balloon-arrow-half-width) 0 var(--ck-balloon-arrow-half-width)}.ck.ck-balloon-panel[class*=arrow_s]:before{border-color:var(--ck-color-panel-border) transparent transparent;filter:drop-shadow(var(--ck-balloon-arrow-drop-shadow));margin-bottom:calc(var(--ck-balloon-border-width)*-1)}.ck.ck-balloon-panel[class*=arrow_s]:after{border-color:var(--ck-color-panel-background) transparent transparent transparent;margin-bottom:calc(var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width))}.ck.ck-balloon-panel[class*=arrow_e]:after,.ck.ck-balloon-panel[class*=arrow_e]:before{border-width:var(--ck-balloon-arrow-half-width) 0 var(--ck-balloon-arrow-half-width) var(--ck-balloon-arrow-height)}.ck.ck-balloon-panel[class*=arrow_e]:before{border-color:transparent transparent transparent var(--ck-color-panel-border);margin-right:calc(var(--ck-balloon-border-width)*-1)}.ck.ck-balloon-panel[class*=arrow_e]:after{border-color:transparent transparent transparent var(--ck-color-panel-background);margin-right:calc(var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width))}.ck.ck-balloon-panel[class*=arrow_w]:after,.ck.ck-balloon-panel[class*=arrow_w]:before{border-width:var(--ck-balloon-arrow-half-width) var(--ck-balloon-arrow-height) var(--ck-balloon-arrow-half-width) 0}.ck.ck-balloon-panel[class*=arrow_w]:before{border-color:transparent var(--ck-color-panel-border) transparent transparent;margin-left:calc(var(--ck-balloon-border-width)*-1)}.ck.ck-balloon-panel[class*=arrow_w]:after{border-color:transparent var(--ck-color-panel-background) transparent transparent;margin-left:calc(var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width))}.ck.ck-balloon-panel.ck-balloon-panel_arrow_n:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_n:before{left:50%;margin-left:calc(var(--ck-balloon-arrow-half-width)*-1);top:calc(var(--ck-balloon-arrow-height)*-1)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_nw:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_nw:before{left:calc(var(--ck-balloon-arrow-half-width)*2);top:calc(var(--ck-balloon-arrow-height)*-1)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_ne:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_ne:before{right:calc(var(--ck-balloon-arrow-half-width)*2);top:calc(var(--ck-balloon-arrow-height)*-1)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_s:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_s:before{bottom:calc(var(--ck-balloon-arrow-height)*-1);left:50%;margin-left:calc(var(--ck-balloon-arrow-half-width)*-1)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_sw:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_sw:before{bottom:calc(var(--ck-balloon-arrow-height)*-1);left:calc(var(--ck-balloon-arrow-half-width)*2)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_se:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_se:before{bottom:calc(var(--ck-balloon-arrow-height)*-1);right:calc(var(--ck-balloon-arrow-half-width)*2)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_sme:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_sme:before{bottom:calc(var(--ck-balloon-arrow-height)*-1);margin-right:calc(var(--ck-balloon-arrow-half-width)*2);right:25%}.ck.ck-balloon-panel.ck-balloon-panel_arrow_smw:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_smw:before{bottom:calc(var(--ck-balloon-arrow-height)*-1);left:25%;margin-left:calc(var(--ck-balloon-arrow-half-width)*2)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_nme:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_nme:before{margin-right:calc(var(--ck-balloon-arrow-half-width)*2);right:25%;top:calc(var(--ck-balloon-arrow-height)*-1)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_nmw:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_nmw:before{left:25%;margin-left:calc(var(--ck-balloon-arrow-half-width)*2);top:calc(var(--ck-balloon-arrow-height)*-1)}.ck.ck-balloon-panel.ck-balloon-panel_arrow_e:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_e:before{margin-top:calc(var(--ck-balloon-arrow-half-width)*-1);right:calc(var(--ck-balloon-arrow-height)*-1);top:50%}.ck.ck-balloon-panel.ck-balloon-panel_arrow_w:after,.ck.ck-balloon-panel.ck-balloon-panel_arrow_w:before{left:calc(var(--ck-balloon-arrow-height)*-1);margin-top:calc(var(--ck-balloon-arrow-half-width)*-1);top:50%}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/balloonpanel.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/panel/balloonpanel.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css"],"names":[],"mappings":"AAKA,MAEC,8DACD,CAEA,qBACC,YAAa,CACb,iBAAkB,CAElB,yBAyCD,CAtCE,+GAEC,UAAW,CACX,iBACD,CAEA,wDACC,6CACD,CAEA,uDACC,uDACD,CAIA,4CACC,6CACD,CAEA,2CACC,uDACD,CAIA,4CACC,6CACD,CAEA,2CACC,uDACD,CAGD,8CACC,aACD,CC9CD,MACC,6BAA8B,CAC9B,6BAA8B,CAC9B,8BAA+B,CAC/B,iCAAkC,CAClC,oEACD,CAEA,qBCLC,eDmMD,CA9LA,iFCDE,qCD+LF,CA9LA,qBAMC,2CAA4C,CAC5C,wEAAyE,CEdzE,oCAA8B,CFW9B,eA0LD,CApLE,+GAIC,kBAAmB,CADnB,QAAS,CADT,OAGD,CAIA,uFAEC,mHACD,CAEA,4CACC,6EAA8E,CAC9E,kDACD,CAEA,2CACC,iFAAkF,CAClF,gFACD,CAIA,uFAEC,mHACD,CAEA,4CACC,iEAAkE,CAClE,uDAAwD,CACxD,qDACD,CAEA,2CACC,iFAAkF,CAClF,mFACD,CAIA,uFAEC,mHACD,CAEA,4CACC,6EAA8E,CAC9E,oDACD,CAEA,2CACC,iFAAkF,CAClF,kFACD,CAIA,uFAEC,mHACD,CAEA,4CACC,6EAA8E,CAC9E,mDACD,CAEA,2CACC,iFAAkF,CAClF,iFACD,CAIA,yGAEC,QAAS,CACT,uDAA0D,CAC1D,2CACD,CAIA,2GAEC,+CAAkD,CAClD,2CACD,CAIA,2GAEC,gDAAmD,CACnD,2CACD,CAIA,yGAIC,8CAAiD,CAFjD,QAAS,CACT,uDAED,CAIA,2GAGC,8CAAiD,CADjD,+CAED,CAIA,2GAGC,8CAAiD,CADjD,gDAED,CAIA,6GAIC,8CAAiD,CADjD,uDAA0D,CAD1D,SAGD,CAIA,6GAIC,8CAAiD,CAFjD,QAAS,CACT,sDAED,CAIA,6GAGC,uDAA0D,CAD1D,SAAU,CAEV,2CACD,CAIA,6GAEC,QAAS,CACT,sDAAyD,CACzD,2CACD,CAIA,yGAGC,sDAAyD,CADzD,6CAAgD,CAEhD,OACD,CAIA,yGAEC,4CAA+C,CAC/C,sDAAyD,CACzD,OACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t/* Make sure the balloon arrow does not float over its children. */\n\t--ck-balloon-panel-arrow-z-index: calc(var(--ck-z-default) - 3);\n}\n\n.ck.ck-balloon-panel {\n\tdisplay: none;\n\tposition: absolute;\n\n\tz-index: var(--ck-z-modal);\n\n\t&.ck-balloon-panel_with-arrow {\n\t\t&::before,\n\t\t&::after {\n\t\t\tcontent: \"\";\n\t\t\tposition: absolute;\n\t\t}\n\n\t\t&::before {\n\t\t\tz-index: var(--ck-balloon-panel-arrow-z-index);\n\t\t}\n\n\t\t&::after {\n\t\t\tz-index: calc(var(--ck-balloon-panel-arrow-z-index) + 1);\n\t\t}\n\t}\n\n\t&[class*=\"arrow_n\"] {\n\t\t&::before {\n\t\t\tz-index: var(--ck-balloon-panel-arrow-z-index);\n\t\t}\n\n\t\t&::after {\n\t\t\tz-index: calc(var(--ck-balloon-panel-arrow-z-index) + 1);\n\t\t}\n\t}\n\n\t&[class*=\"arrow_s\"] {\n\t\t&::before {\n\t\t\tz-index: var(--ck-balloon-panel-arrow-z-index);\n\t\t}\n\n\t\t&::after {\n\t\t\tz-index: calc(var(--ck-balloon-panel-arrow-z-index) + 1);\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_visible {\n\t\tdisplay: block;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n@import \"../../../mixins/_shadow.css\";\n\n:root {\n\t--ck-balloon-border-width: 1px;\n\t--ck-balloon-arrow-offset: 2px;\n\t--ck-balloon-arrow-height: 10px;\n\t--ck-balloon-arrow-half-width: 8px;\n\t--ck-balloon-arrow-drop-shadow: 0 2px 2px var(--ck-color-shadow-drop);\n}\n\n.ck.ck-balloon-panel {\n\t@mixin ck-rounded-corners;\n\t@mixin ck-drop-shadow;\n\n\tmin-height: 15px;\n\n\tbackground: var(--ck-color-panel-background);\n\tborder: var(--ck-balloon-border-width) solid var(--ck-color-panel-border);\n\n\t&.ck-balloon-panel_with-arrow {\n\t\t&::before,\n\t\t&::after {\n\t\t\twidth: 0;\n\t\t\theight: 0;\n\t\t\tborder-style: solid;\n\t\t}\n\t}\n\n\t&[class*=\"arrow_n\"] {\n\t\t&::before,\n\t\t&::after {\n\t\t\tborder-width: 0 var(--ck-balloon-arrow-half-width) var(--ck-balloon-arrow-height) var(--ck-balloon-arrow-half-width);\n\t\t}\n\n\t\t&::before {\n\t\t\tborder-color: transparent transparent var(--ck-color-panel-border) transparent;\n\t\t\tmargin-top: calc( -1 * var(--ck-balloon-border-width) );\n\t\t}\n\n\t\t&::after {\n\t\t\tborder-color: transparent transparent var(--ck-color-panel-background) transparent;\n\t\t\tmargin-top: calc( var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width) );\n\t\t}\n\t}\n\n\t&[class*=\"arrow_s\"] {\n\t\t&::before,\n\t\t&::after {\n\t\t\tborder-width: var(--ck-balloon-arrow-height) var(--ck-balloon-arrow-half-width) 0 var(--ck-balloon-arrow-half-width);\n\t\t}\n\n\t\t&::before {\n\t\t\tborder-color: var(--ck-color-panel-border) transparent transparent;\n\t\t\tfilter: drop-shadow(var(--ck-balloon-arrow-drop-shadow));\n\t\t\tmargin-bottom: calc( -1 * var(--ck-balloon-border-width) );\n\t\t}\n\n\t\t&::after {\n\t\t\tborder-color: var(--ck-color-panel-background) transparent transparent transparent;\n\t\t\tmargin-bottom: calc( var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width) );\n\t\t}\n\t}\n\n\t&[class*=\"arrow_e\"] {\n\t\t&::before,\n\t\t&::after {\n\t\t\tborder-width: var(--ck-balloon-arrow-half-width) 0 var(--ck-balloon-arrow-half-width) var(--ck-balloon-arrow-height);\n\t\t}\n\n\t\t&::before {\n\t\t\tborder-color: transparent transparent transparent var(--ck-color-panel-border);\n\t\t\tmargin-right: calc( -1 * var(--ck-balloon-border-width) );\n\t\t}\n\n\t\t&::after {\n\t\t\tborder-color: transparent transparent transparent var(--ck-color-panel-background);\n\t\t\tmargin-right: calc( var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width) );\n\t\t}\n\t}\n\n\t&[class*=\"arrow_w\"] {\n\t\t&::before,\n\t\t&::after {\n\t\t\tborder-width: var(--ck-balloon-arrow-half-width) var(--ck-balloon-arrow-height) var(--ck-balloon-arrow-half-width) 0;\n\t\t}\n\n\t\t&::before {\n\t\t\tborder-color: transparent var(--ck-color-panel-border) transparent transparent;\n\t\t\tmargin-left: calc( -1 * var(--ck-balloon-border-width) );\n\t\t}\n\n\t\t&::after {\n\t\t\tborder-color: transparent var(--ck-color-panel-background) transparent transparent;\n\t\t\tmargin-left: calc( var(--ck-balloon-arrow-offset) - var(--ck-balloon-border-width) );\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_n {\n\t\t&::before,\n\t\t&::after {\n\t\t\tleft: 50%;\n\t\t\tmargin-left: calc(-1 * var(--ck-balloon-arrow-half-width));\n\t\t\ttop: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_nw {\n\t\t&::before,\n\t\t&::after {\n\t\t\tleft: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\ttop: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_ne {\n\t\t&::before,\n\t\t&::after {\n\t\t\tright: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\ttop: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_s {\n\t\t&::before,\n\t\t&::after {\n\t\t\tleft: 50%;\n\t\t\tmargin-left: calc(-1 * var(--ck-balloon-arrow-half-width));\n\t\t\tbottom: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_sw {\n\t\t&::before,\n\t\t&::after {\n\t\t\tleft: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\tbottom: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_se {\n\t\t&::before,\n\t\t&::after {\n\t\t\tright: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\tbottom: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_sme {\n\t\t&::before,\n\t\t&::after {\n\t\t\tright: 25%;\n\t\t\tmargin-right: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\tbottom: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_smw {\n\t\t&::before,\n\t\t&::after {\n\t\t\tleft: 25%;\n\t\t\tmargin-left: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\tbottom: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_nme {\n\t\t&::before,\n\t\t&::after {\n\t\t\tright: 25%;\n\t\t\tmargin-right: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\ttop: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_nmw {\n\t\t&::before,\n\t\t&::after {\n\t\t\tleft: 25%;\n\t\t\tmargin-left: calc(2 * var(--ck-balloon-arrow-half-width));\n\t\t\ttop: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_e {\n\t\t&::before,\n\t\t&::after {\n\t\t\tright: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t\tmargin-top: calc(-1 * var(--ck-balloon-arrow-half-width));\n\t\t\ttop: 50%;\n\t\t}\n\t}\n\n\t&.ck-balloon-panel_arrow_w {\n\t\t&::before,\n\t\t&::after {\n\t\t\tleft: calc(-1 * var(--ck-balloon-arrow-height));\n\t\t\tmargin-top: calc(-1 * var(--ck-balloon-arrow-half-width));\n\t\t\ttop: 50%;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 1757:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-balloon-rotator__navigation{align-items:center;display:flex;justify-content:center}.ck .ck-balloon-rotator__content .ck-toolbar{justify-content:center}.ck .ck-balloon-rotator__navigation{background:var(--ck-color-toolbar-background);border-bottom:1px solid var(--ck-color-toolbar-border);padding:0 var(--ck-spacing-small)}.ck .ck-balloon-rotator__navigation>*{margin-bottom:var(--ck-spacing-small);margin-right:var(--ck-spacing-small);margin-top:var(--ck-spacing-small)}.ck .ck-balloon-rotator__navigation .ck-balloon-rotator__counter{margin-left:var(--ck-spacing-small);margin-right:var(--ck-spacing-standard)}.ck .ck-balloon-rotator__content .ck.ck-annotation-wrapper{box-shadow:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/balloonrotator.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/panel/balloonrotator.css"],"names":[],"mappings":"AAKA,oCAEC,kBAAmB,CADnB,YAAa,CAEb,sBACD,CAKA,6CACC,sBACD,CCXA,oCACC,6CAA8C,CAC9C,sDAAuD,CACvD,iCAgBD,CAbC,sCAGC,qCAAsC,CAFtC,oCAAqC,CACrC,kCAED,CAGA,iEAIC,mCAAoC,CAHpC,uCAID,CAMA,2DACC,eACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck .ck-balloon-rotator__navigation {\n\tdisplay: flex;\n\talign-items: center;\n\tjustify-content: center;\n}\n\n/* Buttons inside a toolbar should be centered when rotator bar is wider.\n * See: https://github.com/ckeditor/ckeditor5-ui/issues/495\n */\n.ck .ck-balloon-rotator__content .ck-toolbar {\n\tjustify-content: center;\n}\n","/*\n * Copyright (c) 2003-2022, 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.ck .ck-balloon-rotator__navigation {\n\tbackground: var(--ck-color-toolbar-background);\n\tborder-bottom: 1px solid var(--ck-color-toolbar-border);\n\tpadding: 0 var(--ck-spacing-small);\n\n\t/* Let's keep similar appearance to `ck-toolbar`. */\n\t& > * {\n\t\tmargin-right: var(--ck-spacing-small);\n\t\tmargin-top: var(--ck-spacing-small);\n\t\tmargin-bottom: var(--ck-spacing-small);\n\t}\n\n\t/* Gives counter more breath than buttons. */\n\t& .ck-balloon-rotator__counter {\n\t\tmargin-right: var(--ck-spacing-standard);\n\n\t\t/* We need to use smaller margin because of previous button's right margin. */\n\t\tmargin-left: var(--ck-spacing-small);\n\t}\n}\n\n.ck .ck-balloon-rotator__content {\n\n\t/* Disable default annotation shadow inside rotator with fake panels. */\n\t& .ck.ck-annotation-wrapper {\n\t\tbox-shadow: none;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3553:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-fake-panel{position:absolute;z-index:calc(var(--ck-z-modal) - 1)}.ck .ck-fake-panel div{position:absolute}.ck .ck-fake-panel div:first-child{z-index:2}.ck .ck-fake-panel div:nth-child(2){z-index:1}:root{--ck-balloon-fake-panel-offset-horizontal:6px;--ck-balloon-fake-panel-offset-vertical:6px}.ck .ck-fake-panel div{background:var(--ck-color-panel-background);border:1px solid var(--ck-color-panel-border);border-radius:var(--ck-border-radius);box-shadow:var(--ck-drop-shadow),0 0;height:100%;min-height:15px;width:100%}.ck .ck-fake-panel div:first-child{margin-left:var(--ck-balloon-fake-panel-offset-horizontal);margin-top:var(--ck-balloon-fake-panel-offset-vertical)}.ck .ck-fake-panel div:nth-child(2){margin-left:calc(var(--ck-balloon-fake-panel-offset-horizontal)*2);margin-top:calc(var(--ck-balloon-fake-panel-offset-vertical)*2)}.ck .ck-fake-panel div:nth-child(3){margin-left:calc(var(--ck-balloon-fake-panel-offset-horizontal)*3);margin-top:calc(var(--ck-balloon-fake-panel-offset-vertical)*3)}.ck .ck-balloon-panel_arrow_s+.ck-fake-panel,.ck .ck-balloon-panel_arrow_se+.ck-fake-panel,.ck .ck-balloon-panel_arrow_sw+.ck-fake-panel{--ck-balloon-fake-panel-offset-vertical:-6px}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/fakepanel.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/panel/fakepanel.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css"],"names":[],"mappings":"AAKA,mBACC,iBAAkB,CAGlB,mCACD,CAEA,uBACC,iBACD,CAEA,mCACC,SACD,CAEA,oCACC,SACD,CCfA,MACC,6CAA8C,CAC9C,2CACD,CAGA,uBAKC,2CAA4C,CAC5C,6CAA8C,CAC9C,qCAAsC,CCXtC,oCAA8B,CDc9B,WAAY,CAPZ,eAAgB,CAMhB,UAED,CAEA,mCACC,0DAA2D,CAC3D,uDACD,CAEA,oCACC,kEAAqE,CACrE,+DACD,CACA,oCACC,kEAAqE,CACrE,+DACD,CAGA,yIAGC,4CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck .ck-fake-panel {\n\tposition: absolute;\n\n\t/* Fake panels should be placed under main balloon content. */\n\tz-index: calc(var(--ck-z-modal) - 1);\n}\n\n.ck .ck-fake-panel div {\n\tposition: absolute;\n}\n\n.ck .ck-fake-panel div:nth-child( 1 ) {\n\tz-index: 2;\n}\n\n.ck .ck-fake-panel div:nth-child( 2 ) {\n\tz-index: 1;\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_shadow.css\";\n\n:root {\n\t--ck-balloon-fake-panel-offset-horizontal: 6px;\n\t--ck-balloon-fake-panel-offset-vertical: 6px;\n}\n\n/* Let's use `.ck-balloon-panel` appearance. See: balloonpanel.css. */\n.ck .ck-fake-panel div {\n\t@mixin ck-drop-shadow;\n\n\tmin-height: 15px;\n\n\tbackground: var(--ck-color-panel-background);\n\tborder: 1px solid var(--ck-color-panel-border);\n\tborder-radius: var(--ck-border-radius);\n\n\twidth: 100%;\n\theight: 100%;\n}\n\n.ck .ck-fake-panel div:nth-child( 1 ) {\n\tmargin-left: var(--ck-balloon-fake-panel-offset-horizontal);\n\tmargin-top: var(--ck-balloon-fake-panel-offset-vertical);\n}\n\n.ck .ck-fake-panel div:nth-child( 2 ) {\n\tmargin-left: calc(var(--ck-balloon-fake-panel-offset-horizontal) * 2);\n\tmargin-top: calc(var(--ck-balloon-fake-panel-offset-vertical) * 2);\n}\n.ck .ck-fake-panel div:nth-child( 3 ) {\n\tmargin-left: calc(var(--ck-balloon-fake-panel-offset-horizontal) * 3);\n\tmargin-top: calc(var(--ck-balloon-fake-panel-offset-vertical) * 3);\n}\n\n/* If balloon is positioned above element, we need to move fake panel to the top. */\n.ck .ck-balloon-panel_arrow_s + .ck-fake-panel,\n.ck .ck-balloon-panel_arrow_se + .ck-fake-panel,\n.ck .ck-balloon-panel_arrow_sw + .ck-fake-panel {\n\t--ck-balloon-fake-panel-offset-vertical: -6px;\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3609:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-sticky-panel .ck-sticky-panel__content_sticky{position:fixed;top:0;z-index:var(--ck-z-modal)}.ck.ck-sticky-panel .ck-sticky-panel__content_sticky_bottom-limit{position:absolute;top:auto}.ck.ck-sticky-panel .ck-sticky-panel__content_sticky{border-top-left-radius:0;border-top-right-radius:0;border-width:0 1px 1px;box-shadow:var(--ck-drop-shadow),0 0}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/stickypanel.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/panel/stickypanel.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css"],"names":[],"mappings":"AAMC,qDAEC,cAAe,CACf,KAAM,CAFN,yBAGD,CAEA,kEAEC,iBAAkB,CADlB,QAED,CCPA,qDAIC,wBAAyB,CACzB,yBAA0B,CAF1B,sBAAuB,CCFxB,oCDKA","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-sticky-panel {\n\t& .ck-sticky-panel__content_sticky {\n\t\tz-index: var(--ck-z-modal); /* #315 */\n\t\tposition: fixed;\n\t\ttop: 0;\n\t}\n\n\t& .ck-sticky-panel__content_sticky_bottom-limit {\n\t\ttop: auto;\n\t\tposition: absolute;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_shadow.css\";\n\n.ck.ck-sticky-panel {\n\t& .ck-sticky-panel__content_sticky {\n\t\t@mixin ck-drop-shadow;\n\n\t\tborder-width: 0 1px 1px;\n\t\tborder-top-left-radius: 0;\n\t\tborder-top-right-radius: 0;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 1590:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-vertical-form .ck-button:after{bottom:-1px;content:\"\";position:absolute;right:-1px;top:-1px;width:0;z-index:1}.ck-vertical-form .ck-button:focus:after{display:none}@media screen and (max-width:600px){.ck.ck-responsive-form .ck-button:after{bottom:-1px;content:\"\";position:absolute;right:-1px;top:-1px;width:0;z-index:1}.ck.ck-responsive-form .ck-button:focus:after{display:none}}.ck-vertical-form>.ck-button:nth-last-child(2):after{border-right:1px solid var(--ck-color-base-border)}.ck.ck-responsive-form{padding:var(--ck-spacing-large)}.ck.ck-responsive-form:focus{outline:none}[dir=ltr] .ck.ck-responsive-form>:not(:first-child),[dir=rtl] .ck.ck-responsive-form>:not(:last-child){margin-left:var(--ck-spacing-standard)}@media screen and (max-width:600px){.ck.ck-responsive-form{padding:0;width:calc(var(--ck-input-width)*.8)}.ck.ck-responsive-form .ck-labeled-field-view{margin:var(--ck-spacing-large) var(--ck-spacing-large) 0}.ck.ck-responsive-form .ck-labeled-field-view .ck-input-text{min-width:0;width:100%}.ck.ck-responsive-form .ck-labeled-field-view .ck-labeled-field-view__error{white-space:normal}.ck.ck-responsive-form>.ck-button:nth-last-child(2):after{border-right:1px solid var(--ck-color-base-border)}.ck.ck-responsive-form>.ck-button:last-child,.ck.ck-responsive-form>.ck-button:nth-last-child(2){border-radius:0;margin-top:var(--ck-spacing-large);padding:var(--ck-spacing-standard)}.ck.ck-responsive-form>.ck-button:last-child:not(:focus),.ck.ck-responsive-form>.ck-button:nth-last-child(2):not(:focus){border-top:1px solid var(--ck-color-base-border)}[dir=ltr] .ck.ck-responsive-form>.ck-button:last-child,[dir=ltr] .ck.ck-responsive-form>.ck-button:nth-last-child(2),[dir=rtl] .ck.ck-responsive-form>.ck-button:last-child,[dir=rtl] .ck.ck-responsive-form>.ck-button:nth-last-child(2){margin-left:0}[dir=rtl] .ck.ck-responsive-form>.ck-button:last-child:last-of-type,[dir=rtl] .ck.ck-responsive-form>.ck-button:nth-last-child(2):last-of-type{border-right:1px solid var(--ck-color-base-border)}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/responsive-form/responsiveform.css"],"names":[],"mappings":"AAQC,mCAMC,WAAY,CALZ,UAAW,CAEX,iBAAkB,CAClB,UAAW,CACX,QAAS,CAHT,OAAQ,CAKR,SACD,CAEA,yCACC,YACD,CCdA,oCDoBE,wCAMC,WAAY,CALZ,UAAW,CAEX,iBAAkB,CAClB,UAAW,CACX,QAAS,CAHT,OAAQ,CAKR,SACD,CAEA,8CACC,YACD,CC9BF,CCAD,qDACC,kDACD,CAEA,uBACC,+BAmED,CAjEC,6BAEC,YACD,CASC,uGACC,sCACD,CDvBD,oCCMD,uBAqBE,SAAU,CACV,oCA8CF,CA5CE,8CACC,wDAWD,CATC,6DACC,WAAY,CACZ,UACD,CAGA,4EACC,kBACD,CAKA,0DACC,kDACD,CAGD,iGAIC,eAAgB,CADhB,kCAAmC,CADnC,kCAmBD,CAfC,yHACC,gDACD,CARD,0OAeE,aAMF,CAJE,+IACC,kDACD,CDpEH","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n\n.ck-vertical-form .ck-button {\n\t&::after {\n\t\tcontent: \"\";\n\t\twidth: 0;\n\t\tposition: absolute;\n\t\tright: -1px;\n\t\ttop: -1px;\n\t\tbottom: -1px;\n\t\tz-index: 1;\n\t}\n\n\t&:focus::after {\n\t\tdisplay: none;\n\t}\n}\n\n.ck.ck-responsive-form {\n\t@mixin ck-media-phone {\n\t\t& .ck-button {\n\t\t\t&::after {\n\t\t\t\tcontent: \"\";\n\t\t\t\twidth: 0;\n\t\t\t\tposition: absolute;\n\t\t\t\tright: -1px;\n\t\t\t\ttop: -1px;\n\t\t\t\tbottom: -1px;\n\t\t\t\tz-index: 1;\n\t\t\t}\n\n\t\t\t&:focus::after {\n\t\t\t\tdisplay: none;\n\t\t\t}\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@define-mixin ck-media-phone {\n\t@media screen and (max-width: 600px) {\n\t\t@mixin-content;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"@ckeditor/ckeditor5-ui/theme/mixins/_rwd.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n.ck-vertical-form > .ck-button:nth-last-child(2)::after {\n\tborder-right: 1px solid var(--ck-color-base-border);\n}\n\n.ck.ck-responsive-form {\n\tpadding: var(--ck-spacing-large);\n\n\t&:focus {\n\t\t/* See: https://github.com/ckeditor/ckeditor5/issues/4773 */\n\t\toutline: none;\n\t}\n\n\t@mixin ck-dir ltr {\n\t\t& > :not(:first-child) {\n\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t}\n\t}\n\n\t@mixin ck-dir rtl {\n\t\t& > :not(:last-child) {\n\t\t\tmargin-left: var(--ck-spacing-standard);\n\t\t}\n\t}\n\n\t@mixin ck-media-phone {\n\t\tpadding: 0;\n\t\twidth: calc(.8 * var(--ck-input-width));\n\n\t\t& .ck-labeled-field-view {\n\t\t\tmargin: var(--ck-spacing-large) var(--ck-spacing-large) 0;\n\n\t\t\t& .ck-input-text {\n\t\t\t\tmin-width: 0;\n\t\t\t\twidth: 100%;\n\t\t\t}\n\n\t\t\t/* Let the long error messages wrap in the narrow form. */\n\t\t\t& .ck-labeled-field-view__error {\n\t\t\t\twhite-space: normal;\n\t\t\t}\n\t\t}\n\n\t\t/* Styles for two last buttons in the form (save&cancel, edit&unlink, etc.). */\n\t\t& > .ck-button:nth-last-child(2) {\n\t\t\t&::after {\n\t\t\t\tborder-right: 1px solid var(--ck-color-base-border);\n\t\t\t}\n\t\t}\n\n\t\t& > .ck-button:nth-last-child(1),\n\t\t& > .ck-button:nth-last-child(2) {\n\t\t\tpadding: var(--ck-spacing-standard);\n\t\t\tmargin-top: var(--ck-spacing-large);\n\t\t\tborder-radius: 0;\n\n\t\t\t&:not(:focus) {\n\t\t\t\tborder-top: 1px solid var(--ck-color-base-border);\n\t\t\t}\n\n\t\t\t@mixin ck-dir ltr {\n\t\t\t\tmargin-left: 0;\n\t\t\t}\n\n\t\t\t@mixin ck-dir rtl {\n\t\t\t\tmargin-left: 0;\n\n\t\t\t\t&:last-of-type {\n\t\t\t\t\tborder-right: 1px solid var(--ck-color-base-border);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6706:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-block-toolbar-button{position:absolute;z-index:var(--ck-z-default)}:root{--ck-color-block-toolbar-button:var(--ck-color-text);--ck-block-toolbar-button-size:var(--ck-font-size-normal)}.ck.ck-block-toolbar-button{color:var(--ck-color-block-toolbar-button);font-size:var(--ck-block-toolbar-size)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/toolbar/blocktoolbar.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/toolbar/blocktoolbar.css"],"names":[],"mappings":"AAKA,4BACC,iBAAkB,CAClB,2BACD,CCHA,MACC,oDAAqD,CACrD,yDACD,CAEA,4BACC,0CAA2C,CAC3C,sCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-block-toolbar-button {\n\tposition: absolute;\n\tz-index: var(--ck-z-default);\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-block-toolbar-button: var(--ck-color-text);\n\t--ck-block-toolbar-button-size: var(--ck-font-size-normal);\n}\n\n.ck.ck-block-toolbar-button {\n\tcolor: var(--ck-color-block-toolbar-button);\n\tfont-size: var(--ck-block-toolbar-size);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5571:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-toolbar{align-items:center;display:flex;flex-flow:row nowrap;-moz-user-select:none;-webkit-user-select:none;-ms-user-select:none;user-select:none}.ck.ck-toolbar>.ck-toolbar__items{align-items:center;display:flex;flex-flow:row wrap;flex-grow:1}.ck.ck-toolbar .ck.ck-toolbar__separator{display:inline-block}.ck.ck-toolbar .ck.ck-toolbar__separator:first-child,.ck.ck-toolbar .ck.ck-toolbar__separator:last-child{display:none}.ck.ck-toolbar .ck-toolbar__line-break{flex-basis:100%}.ck.ck-toolbar.ck-toolbar_grouping>.ck-toolbar__items{flex-wrap:nowrap}.ck.ck-toolbar.ck-toolbar_vertical>.ck-toolbar__items{flex-direction:column}.ck.ck-toolbar.ck-toolbar_floating>.ck-toolbar__items{flex-wrap:nowrap}.ck.ck-toolbar>.ck.ck-toolbar__grouped-dropdown>.ck-dropdown__button .ck-dropdown__arrow{display:none}.ck.ck-toolbar{border-radius:0}.ck-rounded-corners .ck.ck-toolbar,.ck.ck-toolbar.ck-rounded-corners{border-radius:var(--ck-border-radius)}.ck.ck-toolbar{background:var(--ck-color-toolbar-background);border:1px solid var(--ck-color-toolbar-border);padding:0 var(--ck-spacing-small)}.ck.ck-toolbar .ck.ck-toolbar__separator{align-self:stretch;background:var(--ck-color-toolbar-border);margin-bottom:var(--ck-spacing-small);margin-top:var(--ck-spacing-small);min-width:1px;width:1px}.ck.ck-toolbar .ck-toolbar__line-break{height:0}.ck.ck-toolbar>.ck-toolbar__items>:not(.ck-toolbar__line-break){margin-right:var(--ck-spacing-small)}.ck.ck-toolbar>.ck-toolbar__items:empty+.ck.ck-toolbar__separator{display:none}.ck.ck-toolbar>.ck-toolbar__items>:not(.ck-toolbar__line-break),.ck.ck-toolbar>.ck.ck-toolbar__grouped-dropdown{margin-bottom:var(--ck-spacing-small);margin-top:var(--ck-spacing-small)}.ck.ck-toolbar.ck-toolbar_vertical{padding:0}.ck.ck-toolbar.ck-toolbar_vertical>.ck-toolbar__items>.ck{border-radius:0;margin:0;width:100%}.ck.ck-toolbar.ck-toolbar_compact{padding:0}.ck.ck-toolbar.ck-toolbar_compact>.ck-toolbar__items>*{margin:0}.ck.ck-toolbar.ck-toolbar_compact>.ck-toolbar__items>:not(:first-child):not(:last-child){border-radius:0}.ck.ck-toolbar>.ck.ck-toolbar__grouped-dropdown>.ck.ck-button.ck-dropdown__button{padding-left:var(--ck-spacing-tiny)}.ck.ck-toolbar .ck-toolbar__nested-toolbar-dropdown>.ck-dropdown__panel{min-width:auto}.ck.ck-toolbar .ck-toolbar__nested-toolbar-dropdown>.ck-button>.ck-button__label{max-width:7em;width:auto}.ck-toolbar-container .ck.ck-toolbar{border:0}.ck.ck-toolbar[dir=rtl]>.ck-toolbar__items>.ck,[dir=rtl] .ck.ck-toolbar>.ck-toolbar__items>.ck{margin-right:0}.ck.ck-toolbar[dir=rtl]:not(.ck-toolbar_compact)>.ck-toolbar__items>.ck,[dir=rtl] .ck.ck-toolbar:not(.ck-toolbar_compact)>.ck-toolbar__items>.ck{margin-left:var(--ck-spacing-small)}.ck.ck-toolbar[dir=rtl]>.ck-toolbar__items>.ck:last-child,[dir=rtl] .ck.ck-toolbar>.ck-toolbar__items>.ck:last-child{margin-left:0}.ck.ck-toolbar.ck-toolbar_compact[dir=rtl]>.ck-toolbar__items>.ck:first-child,[dir=rtl] .ck.ck-toolbar.ck-toolbar_compact>.ck-toolbar__items>.ck:first-child{border-bottom-left-radius:0;border-top-left-radius:0}.ck.ck-toolbar.ck-toolbar_compact[dir=rtl]>.ck-toolbar__items>.ck:last-child,[dir=rtl] .ck.ck-toolbar.ck-toolbar_compact>.ck-toolbar__items>.ck:last-child{border-bottom-right-radius:0;border-top-right-radius:0}.ck.ck-toolbar.ck-toolbar_grouping[dir=rtl]>.ck-toolbar__items:not(:empty):not(:only-child),.ck.ck-toolbar[dir=rtl]>.ck.ck-toolbar__separator,[dir=rtl] .ck.ck-toolbar.ck-toolbar_grouping>.ck-toolbar__items:not(:empty):not(:only-child),[dir=rtl] .ck.ck-toolbar>.ck.ck-toolbar__separator{margin-left:var(--ck-spacing-small)}.ck.ck-toolbar[dir=ltr]>.ck-toolbar__items>.ck:last-child,[dir=ltr] .ck.ck-toolbar>.ck-toolbar__items>.ck:last-child{margin-right:0}.ck.ck-toolbar.ck-toolbar_compact[dir=ltr]>.ck-toolbar__items>.ck:first-child,[dir=ltr] .ck.ck-toolbar.ck-toolbar_compact>.ck-toolbar__items>.ck:first-child{border-bottom-right-radius:0;border-top-right-radius:0}.ck.ck-toolbar.ck-toolbar_compact[dir=ltr]>.ck-toolbar__items>.ck:last-child,[dir=ltr] .ck.ck-toolbar.ck-toolbar_compact>.ck-toolbar__items>.ck:last-child{border-bottom-left-radius:0;border-top-left-radius:0}.ck.ck-toolbar.ck-toolbar_grouping[dir=ltr]>.ck-toolbar__items:not(:empty):not(:only-child),.ck.ck-toolbar[dir=ltr]>.ck.ck-toolbar__separator,[dir=ltr] .ck.ck-toolbar.ck-toolbar_grouping>.ck-toolbar__items:not(:empty):not(:only-child),[dir=ltr] .ck.ck-toolbar>.ck.ck-toolbar__separator{margin-right:var(--ck-spacing-small)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/toolbar/toolbar.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/mixins/_unselectable.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/toolbar/toolbar.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_rounded.css"],"names":[],"mappings":"AAOA,eAKC,kBAAmB,CAFnB,YAAa,CACb,oBAAqB,CCFrB,qBAAsB,CACtB,wBAAyB,CACzB,oBAAqB,CACrB,gBD6CD,CA3CC,kCAGC,kBAAmB,CAFnB,YAAa,CACb,kBAAmB,CAEnB,WAED,CAEA,yCACC,oBAWD,CAJC,yGAEC,YACD,CAGD,uCACC,eACD,CAEA,sDACC,gBACD,CAEA,sDACC,qBACD,CAEA,sDACC,gBACD,CAGC,yFACC,YACD,CE/CF,eCGC,eDoGD,CAvGA,qECOE,qCDgGF,CAvGA,eAGC,6CAA8C,CAE9C,+CAAgD,CADhD,iCAmGD,CAhGC,yCACC,kBAAmB,CAGnB,yCAA0C,CAO1C,qCAAsC,CADtC,kCAAmC,CAPnC,aAAc,CADd,SAUD,CAEA,uCACC,QACD,CAGC,gEAEC,oCACD,CAIA,kEACC,YACD,CAGD,gHAIC,qCAAsC,CADtC,kCAED,CAEA,mCAEC,SAaD,CAVC,0DAQC,eAAgB,CAHhB,QAAS,CAHT,UAOD,CAGD,kCAEC,SAWD,CATC,uDAEC,QAMD,CAHC,yFACC,eACD,CASD,kFACC,mCACD,CAMA,wEACC,cACD,CAEA,iFACC,aAAc,CACd,UACD,CAjGF,qCAqGE,QAEF,CAYC,+FACC,cACD,CAEA,iJAEC,mCACD,CAEA,qHACC,aACD,CAIC,6JAEC,2BAA4B,CAD5B,wBAED,CAGA,2JAEC,4BAA6B,CAD7B,yBAED,CASD,8RACC,mCACD,CAWA,qHACC,cACD,CAIC,6JAEC,4BAA6B,CAD7B,yBAED,CAGA,2JAEC,2BAA4B,CAD5B,wBAED,CASD,8RACC,oCACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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@import \"../../mixins/_unselectable.css\";\n\n.ck.ck-toolbar {\n\t@mixin ck-unselectable;\n\n\tdisplay: flex;\n\tflex-flow: row nowrap;\n\talign-items: center;\n\n\t& > .ck-toolbar__items {\n\t\tdisplay: flex;\n\t\tflex-flow: row wrap;\n\t\talign-items: center;\n\t\tflex-grow: 1;\n\n\t}\n\n\t& .ck.ck-toolbar__separator {\n\t\tdisplay: inline-block;\n\n\t\t/*\n\t\t * A leading or trailing separator makes no sense (separates from nothing on one side).\n\t\t * For instance, it can happen when toolbar items (also separators) are getting grouped one by one and\n\t\t * moved to another toolbar in the dropdown.\n\t\t */\n\t\t&:first-child,\n\t\t&:last-child {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t& .ck-toolbar__line-break {\n\t\tflex-basis: 100%;\n\t}\n\n\t&.ck-toolbar_grouping > .ck-toolbar__items {\n\t\tflex-wrap: nowrap;\n\t}\n\n\t&.ck-toolbar_vertical > .ck-toolbar__items {\n\t\tflex-direction: column;\n\t}\n\n\t&.ck-toolbar_floating > .ck-toolbar__items {\n\t\tflex-wrap: nowrap;\n\t}\n\n\t& > .ck.ck-toolbar__grouped-dropdown {\n\t\t& > .ck-dropdown__button .ck-dropdown__arrow {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Makes element unselectable.\n */\n@define-mixin ck-unselectable {\n\t-moz-user-select: none;\n\t-webkit-user-select: none;\n\t-ms-user-select: none;\n\tuser-select: none\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n@import \"@ckeditor/ckeditor5-ui/theme/mixins/_dir.css\";\n\n.ck.ck-toolbar {\n\t@mixin ck-rounded-corners;\n\n\tbackground: var(--ck-color-toolbar-background);\n\tpadding: 0 var(--ck-spacing-small);\n\tborder: 1px solid var(--ck-color-toolbar-border);\n\n\t& .ck.ck-toolbar__separator {\n\t\talign-self: stretch;\n\t\twidth: 1px;\n\t\tmin-width: 1px;\n\t\tbackground: var(--ck-color-toolbar-border);\n\n\t\t/*\n\t\t * These margins make the separators look better in balloon toolbars (when aligned with the \"tip\").\n\t\t * See https://github.com/ckeditor/ckeditor5/issues/7493.\n\t\t */\n\t\tmargin-top: var(--ck-spacing-small);\n\t\tmargin-bottom: var(--ck-spacing-small);\n\t}\n\n\t& .ck-toolbar__line-break {\n\t\theight: 0;\n\t}\n\n\t& > .ck-toolbar__items {\n\t\t& > *:not(.ck-toolbar__line-break) {\n\t\t\t/* (#11) Separate toolbar items. */\n\t\t\tmargin-right: var(--ck-spacing-small);\n\t\t}\n\n\t\t/* Don't display a separator after an empty items container, for instance,\n\t\twhen all items were grouped */\n\t\t&:empty + .ck.ck-toolbar__separator {\n\t\t\tdisplay: none;\n\t\t}\n\t}\n\n\t& > .ck-toolbar__items > *:not(.ck-toolbar__line-break),\n\t& > .ck.ck-toolbar__grouped-dropdown {\n\t\t/* Make sure items wrapped to the next line have v-spacing */\n\t\tmargin-top: var(--ck-spacing-small);\n\t\tmargin-bottom: var(--ck-spacing-small);\n\t}\n\n\t&.ck-toolbar_vertical {\n\t\t/* Items in a vertical toolbar span the entire width. */\n\t\tpadding: 0;\n\n\t\t/* Specificity matters here. See https://github.com/ckeditor/ckeditor5-theme-lark/issues/168. */\n\t\t& > .ck-toolbar__items > .ck {\n\t\t\t/* Items in a vertical toolbar should span the horizontal space. */\n\t\t\twidth: 100%;\n\n\t\t\t/* Items in a vertical toolbar should have no margin. */\n\t\t\tmargin: 0;\n\n\t\t\t/* Items in a vertical toolbar span the entire width so rounded corners are pointless. */\n\t\t\tborder-radius: 0;\n\t\t}\n\t}\n\n\t&.ck-toolbar_compact {\n\t\t/* No spacing around items. */\n\t\tpadding: 0;\n\n\t\t& > .ck-toolbar__items > * {\n\t\t\t/* Compact toolbar items have no spacing between them. */\n\t\t\tmargin: 0;\n\n\t\t\t/* \"Middle\" children should have no rounded corners. */\n\t\t\t&:not(:first-child):not(:last-child) {\n\t\t\t\tborder-radius: 0;\n\t\t\t}\n\t\t}\n\t}\n\n\t& > .ck.ck-toolbar__grouped-dropdown {\n\t\t/*\n\t\t * Dropdown button has asymmetric padding to fit the arrow.\n\t\t * This button has no arrow so let's revert that padding back to normal.\n\t\t */\n\t\t& > .ck.ck-button.ck-dropdown__button {\n\t\t\tpadding-left: var(--ck-spacing-tiny);\n\t\t}\n\t}\n\n\t/* A drop-down containing the nested toolbar with configured items. */\n\t& .ck-toolbar__nested-toolbar-dropdown {\n\t\t/* Prevent empty space in the panel when the dropdown label is visible and long but the toolbar has few items. */\n\t\t& > .ck-dropdown__panel {\n\t\t\tmin-width: auto;\n\t\t}\n\n\t\t& > .ck-button > .ck-button__label {\n\t\t\tmax-width: 7em;\n\t\t\twidth: auto;\n\t\t}\n\t}\n\n\t@nest .ck-toolbar-container & {\n\t\tborder: 0;\n\t}\n}\n\n/* stylelint-disable */\n\n/*\n * Styles for RTL toolbars.\n *\n * Note: In some cases (e.g. a decoupled editor), the toolbar has its own \"dir\"\n * because its parent is not controlled by the editor framework.\n */\n[dir=\"rtl\"] .ck.ck-toolbar,\n.ck.ck-toolbar[dir=\"rtl\"] {\n\t& > .ck-toolbar__items > .ck {\n\t\tmargin-right: 0;\n\t}\n\n\t&:not(.ck-toolbar_compact) > .ck-toolbar__items > .ck {\n\t\t/* (#11) Separate toolbar items. */\n\t\tmargin-left: var(--ck-spacing-small);\n\t}\n\n\t& > .ck-toolbar__items > .ck:last-child {\n\t\tmargin-left: 0;\n\t}\n\n\t&.ck-toolbar_compact > .ck-toolbar__items > .ck {\n\t\t/* No rounded corners on the right side of the first child. */\n\t\t&:first-child {\n\t\t\tborder-top-left-radius: 0;\n\t\t\tborder-bottom-left-radius: 0;\n\t\t}\n\n\t\t/* No rounded corners on the left side of the last child. */\n\t\t&:last-child {\n\t\t\tborder-top-right-radius: 0;\n\t\t\tborder-bottom-right-radius: 0;\n\t\t}\n\t}\n\n\t/* Separate the the separator form the grouping dropdown when some items are grouped. */\n\t& > .ck.ck-toolbar__separator {\n\t\tmargin-left: var(--ck-spacing-small);\n\t}\n\n\t/* Some spacing between the items and the separator before the grouped items dropdown. */\n\t&.ck-toolbar_grouping > .ck-toolbar__items:not(:empty):not(:only-child) {\n\t\tmargin-left: var(--ck-spacing-small);\n\t}\n}\n\n/*\n * Styles for LTR toolbars.\n *\n * Note: In some cases (e.g. a decoupled editor), the toolbar has its own \"dir\"\n * because its parent is not controlled by the editor framework.\n */\n[dir=\"ltr\"] .ck.ck-toolbar,\n.ck.ck-toolbar[dir=\"ltr\"] {\n\t& > .ck-toolbar__items > .ck:last-child {\n\t\tmargin-right: 0;\n\t}\n\n\t&.ck-toolbar_compact > .ck-toolbar__items > .ck {\n\t\t/* No rounded corners on the right side of the first child. */\n\t\t&:first-child {\n\t\t\tborder-top-right-radius: 0;\n\t\t\tborder-bottom-right-radius: 0;\n\t\t}\n\n\t\t/* No rounded corners on the left side of the last child. */\n\t\t&:last-child {\n\t\t\tborder-top-left-radius: 0;\n\t\t\tborder-bottom-left-radius: 0;\n\t\t}\n\t}\n\n\t/* Separate the the separator form the grouping dropdown when some items are grouped. */\n\t& > .ck.ck-toolbar__separator {\n\t\tmargin-right: var(--ck-spacing-small);\n\t}\n\n\t/* Some spacing between the items and the separator before the grouped items dropdown. */\n\t&.ck-toolbar_grouping > .ck-toolbar__items:not(:empty):not(:only-child) {\n\t\tmargin-right: var(--ck-spacing-small);\n\t}\n}\n\n/* stylelint-enable */\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Implements rounded corner interface for .ck-rounded-corners class.\n *\n * @see $ck-border-radius\n */\n@define-mixin ck-rounded-corners {\n\tborder-radius: 0;\n\n\t@nest .ck-rounded-corners &,\n\t&.ck-rounded-corners {\n\t\tborder-radius: var(--ck-border-radius);\n\t\t@mixin-content;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 9948:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck.ck-balloon-panel.ck-tooltip{--ck-balloon-border-width:0px;--ck-balloon-arrow-offset:0px;--ck-balloon-arrow-half-width:4px;--ck-balloon-arrow-height:4px;--ck-color-panel-background:var(--ck-color-tooltip-background);padding:0 var(--ck-spacing-medium);pointer-events:none;z-index:calc(var(--ck-z-modal) + 100)}.ck.ck-balloon-panel.ck-tooltip .ck-tooltip__text{color:var(--ck-color-tooltip-text);font-size:.9em;line-height:1.5}.ck.ck-balloon-panel.ck-tooltip{box-shadow:none}.ck.ck-balloon-panel.ck-tooltip:before{display:none}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/components/tooltip/tooltip.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/components/tooltip/tooltip.css"],"names":[],"mappings":"AAKA,gCCGC,6BAA8B,CAC9B,6BAA8B,CAC9B,iCAAkC,CAClC,6BAA8B,CAC9B,8DAA+D,CAE/D,kCAAmC,CDPnC,mBAAoB,CAEpB,qCACD,CCMC,kDAGC,kCAAmC,CAFnC,cAAe,CACf,eAED,CAbD,gCAgBC,eAMD,CAHC,uCACC,YACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck.ck-balloon-panel.ck-tooltip {\n\t/* Keep tooltips transparent for any interactions. */\n\tpointer-events: none;\n\n\tz-index: calc( var(--ck-z-modal) + 100 );\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../../../mixins/_rounded.css\";\n\n.ck.ck-balloon-panel.ck-tooltip {\n\t--ck-balloon-border-width: 0px;\n\t--ck-balloon-arrow-offset: 0px;\n\t--ck-balloon-arrow-half-width: 4px;\n\t--ck-balloon-arrow-height: 4px;\n\t--ck-color-panel-background: var(--ck-color-tooltip-background);\n\n\tpadding: 0 var(--ck-spacing-medium);\n\n\t& .ck-tooltip__text {\n\t\tfont-size: .9em;\n\t\tline-height: 1.5;\n\t\tcolor: var(--ck-color-tooltip-text);\n\t}\n\n\t/* Reset balloon panel styles */\n\tbox-shadow: none;\n\n\t/* Hide the default shadow of the .ck-balloon-panel tip */\n\t&::before {\n\t\tdisplay: none;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6150:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck-hidden{display:none!important}.ck-reset_all :not(.ck-reset_all-excluded *),.ck.ck-reset,.ck.ck-reset_all{box-sizing:border-box;height:auto;position:static;width:auto}:root{--ck-z-default:1;--ck-z-modal:calc(var(--ck-z-default) + 999)}.ck-transitions-disabled,.ck-transitions-disabled *{transition:none!important}:root{--ck-color-base-foreground:#fafafa;--ck-color-base-background:#fff;--ck-color-base-border:#ccced1;--ck-color-base-action:#53a336;--ck-color-base-focus:#6cb5f9;--ck-color-base-text:#333;--ck-color-base-active:#2977ff;--ck-color-base-active-focus:#0d65ff;--ck-color-base-error:#db3700;--ck-color-focus-border-coordinates:218,81.8%,56.9%;--ck-color-focus-border:hsl(var(--ck-color-focus-border-coordinates));--ck-color-focus-outer-shadow:#cae1fc;--ck-color-focus-disabled-shadow:rgba(119,186,248,.3);--ck-color-focus-error-shadow:rgba(255,64,31,.3);--ck-color-text:var(--ck-color-base-text);--ck-color-shadow-drop:rgba(0,0,0,.15);--ck-color-shadow-drop-active:rgba(0,0,0,.2);--ck-color-shadow-inner:rgba(0,0,0,.1);--ck-color-button-default-background:transparent;--ck-color-button-default-hover-background:#f0f0f0;--ck-color-button-default-active-background:#f0f0f0;--ck-color-button-default-disabled-background:transparent;--ck-color-button-on-background:#f0f7ff;--ck-color-button-on-hover-background:#dbecff;--ck-color-button-on-active-background:#dbecff;--ck-color-button-on-disabled-background:#f0f2f4;--ck-color-button-on-color:#2977ff;--ck-color-button-action-background:var(--ck-color-base-action);--ck-color-button-action-hover-background:#4d9d30;--ck-color-button-action-active-background:#4d9d30;--ck-color-button-action-disabled-background:#7ec365;--ck-color-button-action-text:var(--ck-color-base-background);--ck-color-button-save:#008a00;--ck-color-button-cancel:#db3700;--ck-color-switch-button-off-background:#939393;--ck-color-switch-button-off-hover-background:#7d7d7d;--ck-color-switch-button-on-background:var(--ck-color-button-action-background);--ck-color-switch-button-on-hover-background:#4d9d30;--ck-color-switch-button-inner-background:var(--ck-color-base-background);--ck-color-switch-button-inner-shadow:rgba(0,0,0,.1);--ck-color-dropdown-panel-background:var(--ck-color-base-background);--ck-color-dropdown-panel-border:var(--ck-color-base-border);--ck-color-input-background:var(--ck-color-base-background);--ck-color-input-border:var(--ck-color-base-border);--ck-color-input-error-border:var(--ck-color-base-error);--ck-color-input-text:var(--ck-color-base-text);--ck-color-input-disabled-background:#f2f2f2;--ck-color-input-disabled-border:var(--ck-color-base-border);--ck-color-input-disabled-text:#757575;--ck-color-list-background:var(--ck-color-base-background);--ck-color-list-button-hover-background:var(--ck-color-button-default-hover-background);--ck-color-list-button-on-background:var(--ck-color-button-on-color);--ck-color-list-button-on-background-focus:var(--ck-color-button-on-color);--ck-color-list-button-on-text:var(--ck-color-base-background);--ck-color-panel-background:var(--ck-color-base-background);--ck-color-panel-border:var(--ck-color-base-border);--ck-color-toolbar-background:var(--ck-color-base-background);--ck-color-toolbar-border:var(--ck-color-base-border);--ck-color-tooltip-background:var(--ck-color-base-text);--ck-color-tooltip-text:var(--ck-color-base-background);--ck-color-engine-placeholder-text:#707070;--ck-color-upload-bar-background:#6cb5f9;--ck-color-link-default:#0000f0;--ck-color-link-selected-background:rgba(31,176,255,.1);--ck-color-link-fake-selection:rgba(31,176,255,.3);--ck-color-highlight-background:#ff0;--ck-disabled-opacity:.5;--ck-focus-outer-shadow-geometry:0 0 0 3px;--ck-focus-outer-shadow:var(--ck-focus-outer-shadow-geometry) var(--ck-color-focus-outer-shadow);--ck-focus-disabled-outer-shadow:var(--ck-focus-outer-shadow-geometry) var(--ck-color-focus-disabled-shadow);--ck-focus-error-outer-shadow:var(--ck-focus-outer-shadow-geometry) var(--ck-color-focus-error-shadow);--ck-focus-ring:1px solid var(--ck-color-focus-border);--ck-font-size-base:13px;--ck-line-height-base:1.84615;--ck-font-face:Helvetica,Arial,Tahoma,Verdana,Sans-Serif;--ck-font-size-tiny:0.7em;--ck-font-size-small:0.75em;--ck-font-size-normal:1em;--ck-font-size-big:1.4em;--ck-font-size-large:1.8em;--ck-ui-component-min-height:2.3em}.ck-reset_all :not(.ck-reset_all-excluded *),.ck.ck-reset,.ck.ck-reset_all{word-wrap:break-word;background:transparent;border:0;margin:0;padding:0;text-decoration:none;transition:none;vertical-align:middle}.ck-reset_all :not(.ck-reset_all-excluded *),.ck.ck-reset_all{border-collapse:collapse;color:var(--ck-color-text);cursor:auto;float:none;font:normal normal normal var(--ck-font-size-base)/var(--ck-line-height-base) var(--ck-font-face);text-align:left;white-space:nowrap}.ck-reset_all .ck-rtl :not(.ck-reset_all-excluded *){text-align:right}.ck-reset_all iframe:not(.ck-reset_all-excluded *){vertical-align:inherit}.ck-reset_all textarea:not(.ck-reset_all-excluded *){white-space:pre-wrap}.ck-reset_all input[type=password]:not(.ck-reset_all-excluded *),.ck-reset_all input[type=text]:not(.ck-reset_all-excluded *),.ck-reset_all textarea:not(.ck-reset_all-excluded *){cursor:text}.ck-reset_all input[type=password][disabled]:not(.ck-reset_all-excluded *),.ck-reset_all input[type=text][disabled]:not(.ck-reset_all-excluded *),.ck-reset_all textarea[disabled]:not(.ck-reset_all-excluded *){cursor:default}.ck-reset_all fieldset:not(.ck-reset_all-excluded *){border:2px groove #dfdee3;padding:10px}.ck-reset_all button:not(.ck-reset_all-excluded *)::-moz-focus-inner{border:0;padding:0}.ck[dir=rtl],.ck[dir=rtl] .ck{text-align:right}:root{--ck-border-radius:2px;--ck-inner-shadow:2px 2px 3px var(--ck-color-shadow-inner) inset;--ck-drop-shadow:0 1px 2px 1px var(--ck-color-shadow-drop);--ck-drop-shadow-active:0 3px 6px 1px var(--ck-color-shadow-drop-active);--ck-spacing-unit:0.6em;--ck-spacing-large:calc(var(--ck-spacing-unit)*1.5);--ck-spacing-standard:var(--ck-spacing-unit);--ck-spacing-medium:calc(var(--ck-spacing-unit)*0.8);--ck-spacing-small:calc(var(--ck-spacing-unit)*0.5);--ck-spacing-tiny:calc(var(--ck-spacing-unit)*0.3);--ck-spacing-extra-tiny:calc(var(--ck-spacing-unit)*0.16)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/globals/_hidden.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/globals/_reset.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/globals/_zindex.css","webpack://./node_modules/@ckeditor/ckeditor5-ui/theme/globals/_transition.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_colors.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_disabled.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_focus.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_fonts.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_reset.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_rounded.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_shadow.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-ui/globals/_spacing.css"],"names":[],"mappings":"AAQA,WAGC,sBACD,CCPA,2EAGC,qBAAsB,CAEtB,WAAY,CACZ,eAAgB,CAFhB,UAGD,CCPA,MACC,gBAAiB,CACjB,4CACD,CCAA,oDAEC,yBACD,CCNA,MACC,kCAAmD,CACnD,+BAAoD,CACpD,8BAAkD,CAClD,8BAAuD,CACvD,6BAAmD,CACnD,yBAA+C,CAC/C,8BAAsD,CACtD,oCAA4D,CAC5D,6BAAkD,CAIlD,mDAA4D,CAC5D,qEAA+E,CAC/E,qCAA4D,CAC5D,qDAA8D,CAC9D,gDAAyD,CACzD,yCAAqD,CACrD,sCAAsD,CACtD,4CAA0D,CAC1D,sCAAsD,CAItD,gDAAuD,CACvD,kDAAiE,CACjE,mDAAkE,CAClE,yDAA8D,CAE9D,uCAA6D,CAC7D,6CAAoE,CACpE,8CAAoE,CACpE,gDAAiE,CACjE,kCAAyD,CAGzD,+DAAsE,CACtE,iDAAsE,CACtE,kDAAsE,CACtE,oDAAoE,CACpE,6DAAsE,CAEtE,8BAAoD,CACpD,gCAAqD,CAErD,+CAA8D,CAC9D,qDAAiE,CACjE,+EAAqF,CACrF,oDAAuE,CACvE,yEAA8E,CAC9E,oDAAgE,CAIhE,oEAA2E,CAC3E,4DAAoE,CAIpE,2DAAoE,CACpE,mDAA6D,CAC7D,wDAAgE,CAChE,+CAA0D,CAC1D,4CAA2D,CAC3D,4DAAoE,CACpE,sCAAsD,CAItD,0DAAmE,CACnE,uFAA6F,CAC7F,oEAA2E,CAC3E,0EAA+E,CAC/E,8DAAsE,CAItE,2DAAoE,CACpE,mDAA6D,CAI7D,6DAAsE,CACtE,qDAA+D,CAI/D,uDAAgE,CAChE,uDAAiE,CAIjE,0CAAyD,CAIzD,wCAA2D,CAI3D,+BAAoD,CACpD,uDAAmE,CACnE,kDAAgE,CAIhE,oCAAwD,CCvGxD,wBAAyB,CCAzB,0CAA2C,CAK3C,gGAAiG,CAKjG,4GAA6G,CAK7G,sGAAuG,CAKvG,sDAAuD,CCvBvD,wBAAyB,CACzB,6BAA8B,CAC9B,wDAA6D,CAE7D,yBAA0B,CAC1B,2BAA4B,CAC5B,yBAA0B,CAC1B,wBAAyB,CACzB,0BAA2B,CCJ3B,kCJuGD,CIjGA,2EAaC,oBAAqB,CANrB,sBAAuB,CADvB,QAAS,CAFT,QAAS,CACT,SAAU,CAGV,oBAAqB,CAErB,eAAgB,CADhB,qBAKD,CAKA,8DAGC,wBAAyB,CAEzB,0BAA2B,CAG3B,WAAY,CACZ,UAAW,CALX,iGAAkG,CAElG,eAAgB,CAChB,kBAGD,CAGC,qDACC,gBACD,CAEA,mDAEC,sBACD,CAEA,qDACC,oBACD,CAEA,mLAGC,WACD,CAEA,iNAGC,cACD,CAEA,qDAEC,yBAAoC,CADpC,YAED,CAEA,qEAGC,QAAQ,CADR,SAED,CAMD,8BAEC,gBACD,CCnFA,MACC,sBAAuB,CCAvB,gEAAiE,CAKjE,0DAA2D,CAK3D,wEAAyE,CCbzE,uBAA8B,CAC9B,mDAA2D,CAC3D,4CAAkD,CAClD,oDAA4D,CAC5D,mDAA2D,CAC3D,kDAA2D,CAC3D,yDFFD","sourcesContent":["/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A class which hides an element in DOM.\n */\n.ck-hidden {\n\t/* Override selector specificity. Otherwise, all elements with some display\n\tstyle defined will override this one, which is not a desired result. */\n\tdisplay: none !important;\n}\n","/*\n * Copyright (c) 2003-2022, 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.ck.ck-reset,\n.ck.ck-reset_all,\n.ck-reset_all *:not(.ck-reset_all-excluded *) {\n\tbox-sizing: border-box;\n\twidth: auto;\n\theight: auto;\n\tposition: static;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-z-default: 1;\n\t--ck-z-modal: calc( var(--ck-z-default) + 999 );\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A class that disables all transitions of the element and its children.\n */\n.ck-transitions-disabled,\n.ck-transitions-disabled * {\n\ttransition: none !important;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-base-foreground: \t\t\t\t\t\t\t\thsl(0, 0%, 98%);\n\t--ck-color-base-background: \t\t\t\t\t\t\t\thsl(0, 0%, 100%);\n\t--ck-color-base-border: \t\t\t\t\t\t\t\t\thsl(220, 6%, 81%);\n\t--ck-color-base-action: \t\t\t\t\t\t\t\t\thsl(104, 50.2%, 42.5%);\n\t--ck-color-base-focus: \t\t\t\t\t\t\t\t\t\thsl(209, 92%, 70%);\n\t--ck-color-base-text: \t\t\t\t\t\t\t\t\t\thsl(0, 0%, 20%);\n\t--ck-color-base-active: \t\t\t\t\t\t\t\t\thsl(218.1, 100%, 58%);\n\t--ck-color-base-active-focus:\t\t\t\t\t\t\t\thsl(218.2, 100%, 52.5%);\n\t--ck-color-base-error:\t\t\t\t\t\t\t\t\t\thsl(15, 100%, 43%);\n\n\t/* -- Generic colors ------------------------------------------------------------------------ */\n\n\t--ck-color-focus-border-coordinates: \t\t\t\t\t\t218, 81.8%, 56.9%;\n\t--ck-color-focus-border: \t\t\t\t\t\t\t\t\thsl(var(--ck-color-focus-border-coordinates));\n\t--ck-color-focus-outer-shadow:\t\t\t\t\t\t\t\thsl(212.4, 89.3%, 89%);\n\t--ck-color-focus-disabled-shadow:\t\t\t\t\t\t\thsla(209, 90%, 72%,.3);\n\t--ck-color-focus-error-shadow:\t\t\t\t\t\t\t\thsla(9,100%,56%,.3);\n\t--ck-color-text: \t\t\t\t\t\t\t\t\t\t\tvar(--ck-color-base-text);\n\t--ck-color-shadow-drop: \t\t\t\t\t\t\t\t\thsla(0, 0%, 0%, 0.15);\n\t--ck-color-shadow-drop-active:\t\t\t\t\t\t\t\thsla(0, 0%, 0%, 0.2);\n\t--ck-color-shadow-inner: \t\t\t\t\t\t\t\t\thsla(0, 0%, 0%, 0.1);\n\n\t/* -- Buttons ------------------------------------------------------------------------------- */\n\n\t--ck-color-button-default-background: \t\t\t\t\t\ttransparent;\n\t--ck-color-button-default-hover-background: \t\t\t\thsl(0, 0%, 94.1%);\n\t--ck-color-button-default-active-background: \t\t\t\thsl(0, 0%, 94.1%);\n\t--ck-color-button-default-disabled-background: \t\t\t\ttransparent;\n\n\t--ck-color-button-on-background: \t\t\t\t\t\t\thsl(212, 100%, 97.1%);\n\t--ck-color-button-on-hover-background: \t\t\t\t\t\thsl(211.7, 100%, 92.9%);\n\t--ck-color-button-on-active-background: \t\t\t\t\thsl(211.7, 100%, 92.9%);\n\t--ck-color-button-on-disabled-background: \t\t\t\t\thsl(211, 15%, 95%);\n\t--ck-color-button-on-color:\t\t\t\t\t\t\t\t\thsl(218.1, 100%, 58%);\n\n\n\t--ck-color-button-action-background: \t\t\t\t\t\tvar(--ck-color-base-action);\n\t--ck-color-button-action-hover-background: \t\t\t\t\thsl(104, 53.2%, 40.2%);\n\t--ck-color-button-action-active-background: \t\t\t\thsl(104, 53.2%, 40.2%);\n\t--ck-color-button-action-disabled-background: \t\t\t\thsl(104, 44%, 58%);\n\t--ck-color-button-action-text: \t\t\t\t\t\t\t\tvar(--ck-color-base-background);\n\n\t--ck-color-button-save: \t\t\t\t\t\t\t\t\thsl(120, 100%, 27%);\n\t--ck-color-button-cancel: \t\t\t\t\t\t\t\t\thsl(15, 100%, 43%);\n\n\t--ck-color-switch-button-off-background:\t\t\t\t\thsl(0, 0%, 57.6%);\n\t--ck-color-switch-button-off-hover-background:\t\t\t\thsl(0, 0%, 49%);\n\t--ck-color-switch-button-on-background:\t\t\t\t\t\tvar(--ck-color-button-action-background);\n\t--ck-color-switch-button-on-hover-background:\t\t\t\thsl(104, 53.2%, 40.2%);\n\t--ck-color-switch-button-inner-background:\t\t\t\t\tvar(--ck-color-base-background);\n\t--ck-color-switch-button-inner-shadow:\t\t\t\t\t\thsla(0, 0%, 0%, 0.1);\n\n\t/* -- Dropdown ------------------------------------------------------------------------------ */\n\n\t--ck-color-dropdown-panel-background: \t\t\t\t\t\tvar(--ck-color-base-background);\n\t--ck-color-dropdown-panel-border: \t\t\t\t\t\t\tvar(--ck-color-base-border);\n\n\t/* -- Input --------------------------------------------------------------------------------- */\n\n\t--ck-color-input-background: \t\t\t\t\t\t\t\tvar(--ck-color-base-background);\n\t--ck-color-input-border: \t\t\t\t\t\t\t\t\tvar(--ck-color-base-border);\n\t--ck-color-input-error-border:\t\t\t\t\t\t\t\tvar(--ck-color-base-error);\n\t--ck-color-input-text: \t\t\t\t\t\t\t\t\t\tvar(--ck-color-base-text);\n\t--ck-color-input-disabled-background: \t\t\t\t\t\thsl(0, 0%, 95%);\n\t--ck-color-input-disabled-border: \t\t\t\t\t\t\tvar(--ck-color-base-border);\n\t--ck-color-input-disabled-text: \t\t\t\t\t\t\thsl(0, 0%, 46%);\n\n\t/* -- List ---------------------------------------------------------------------------------- */\n\n\t--ck-color-list-background: \t\t\t\t\t\t\t\tvar(--ck-color-base-background);\n\t--ck-color-list-button-hover-background: \t\t\t\t\tvar(--ck-color-button-default-hover-background);\n\t--ck-color-list-button-on-background: \t\t\t\t\t\tvar(--ck-color-button-on-color);\n\t--ck-color-list-button-on-background-focus: \t\t\t\tvar(--ck-color-button-on-color);\n\t--ck-color-list-button-on-text:\t\t\t\t\t\t\t\tvar(--ck-color-base-background);\n\n\t/* -- Panel --------------------------------------------------------------------------------- */\n\n\t--ck-color-panel-background: \t\t\t\t\t\t\t\tvar(--ck-color-base-background);\n\t--ck-color-panel-border: \t\t\t\t\t\t\t\t\tvar(--ck-color-base-border);\n\n\t/* -- Toolbar ------------------------------------------------------------------------------- */\n\n\t--ck-color-toolbar-background: \t\t\t\t\t\t\t\tvar(--ck-color-base-background);\n\t--ck-color-toolbar-border: \t\t\t\t\t\t\t\t\tvar(--ck-color-base-border);\n\n\t/* -- Tooltip ------------------------------------------------------------------------------- */\n\n\t--ck-color-tooltip-background: \t\t\t\t\t\t\t\tvar(--ck-color-base-text);\n\t--ck-color-tooltip-text: \t\t\t\t\t\t\t\t\tvar(--ck-color-base-background);\n\n\t/* -- Engine -------------------------------------------------------------------------------- */\n\n\t--ck-color-engine-placeholder-text: \t\t\t\t\t\thsl(0, 0%, 44%);\n\n\t/* -- Upload -------------------------------------------------------------------------------- */\n\n\t--ck-color-upload-bar-background:\t\t \t\t\t\t\thsl(209, 92%, 70%);\n\n\t/* -- Link -------------------------------------------------------------------------------- */\n\n\t--ck-color-link-default:\t\t\t\t\t\t\t\t\thsl(240, 100%, 47%);\n\t--ck-color-link-selected-background:\t\t\t\t\t\thsla(201, 100%, 56%, 0.1);\n\t--ck-color-link-fake-selection:\t\t\t\t\t\t\t\thsla(201, 100%, 56%, 0.3);\n\n\t/* -- Search result highlight ---------------------------------------------------------------- */\n\n\t--ck-color-highlight-background:\t\t\t\t\t\t\thsl(60, 100%, 50%)\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t/**\n\t * An opacity value of disabled UI item.\n\t */\n\t--ck-disabled-opacity: .5;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t/**\n\t * The geometry of the of focused element's outer shadow.\n\t */\n\t--ck-focus-outer-shadow-geometry: 0 0 0 3px;\n\n\t/**\n\t * A visual style of focused element's outer shadow.\n\t */\n\t--ck-focus-outer-shadow: var(--ck-focus-outer-shadow-geometry) var(--ck-color-focus-outer-shadow);\n\n\t/**\n\t * A visual style of focused element's outer shadow (when disabled).\n\t */\n\t--ck-focus-disabled-outer-shadow: var(--ck-focus-outer-shadow-geometry) var(--ck-color-focus-disabled-shadow);\n\n\t/**\n\t * A visual style of focused element's outer shadow (when has errors).\n\t */\n\t--ck-focus-error-outer-shadow: var(--ck-focus-outer-shadow-geometry) var(--ck-color-focus-error-shadow);\n\n\t/**\n\t * A visual style of focused element's border or outline.\n\t */\n\t--ck-focus-ring: 1px solid var(--ck-color-focus-border);\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-font-size-base: 13px;\n\t--ck-line-height-base: 1.84615;\n\t--ck-font-face: Helvetica, Arial, Tahoma, Verdana, Sans-Serif;\n\n\t--ck-font-size-tiny: 0.7em;\n\t--ck-font-size-small: 0.75em;\n\t--ck-font-size-normal: 1em;\n\t--ck-font-size-big: 1.4em;\n\t--ck-font-size-large: 1.8em;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t/* This is super-important. This is **manually** adjusted so a button without an icon\n\tis never smaller than a button with icon, additionally making sure that text-less buttons\n\tare perfect squares. The value is also shared by other components which should stay \"in-line\"\n\twith buttons. */\n\t--ck-ui-component-min-height: 2.3em;\n}\n\n/**\n * Resets an element, ignoring its children.\n */\n.ck.ck-reset,\n.ck.ck-reset_all,\n.ck-reset_all *:not(.ck-reset_all-excluded *) {\n\t/* Do not include inheritable rules here. */\n\tmargin: 0;\n\tpadding: 0;\n\tborder: 0;\n\tbackground: transparent;\n\ttext-decoration: none;\n\tvertical-align: middle;\n\ttransition: none;\n\n\t/* https://github.com/ckeditor/ckeditor5-theme-lark/issues/105 */\n\tword-wrap: break-word;\n}\n\n/**\n * Resets an element AND its children.\n */\n.ck.ck-reset_all,\n.ck-reset_all *:not(.ck-reset_all-excluded *) {\n\t/* These are rule inherited by all children elements. */\n\tborder-collapse: collapse;\n\tfont: normal normal normal var(--ck-font-size-base)/var(--ck-line-height-base) var(--ck-font-face);\n\tcolor: var(--ck-color-text);\n\ttext-align: left;\n\twhite-space: nowrap;\n\tcursor: auto;\n\tfloat: none;\n}\n\n.ck-reset_all {\n\t& .ck-rtl *:not(.ck-reset_all-excluded *) {\n\t\ttext-align: right;\n\t}\n\n\t& iframe:not(.ck-reset_all-excluded *) {\n\t\t/* For IE */\n\t\tvertical-align: inherit;\n\t}\n\n\t& textarea:not(.ck-reset_all-excluded *) {\n\t\twhite-space: pre-wrap;\n\t}\n\n\t& textarea:not(.ck-reset_all-excluded *),\n\t& input[type=\"text\"]:not(.ck-reset_all-excluded *),\n\t& input[type=\"password\"]:not(.ck-reset_all-excluded *) {\n\t\tcursor: text;\n\t}\n\n\t& textarea[disabled]:not(.ck-reset_all-excluded *),\n\t& input[type=\"text\"][disabled]:not(.ck-reset_all-excluded *),\n\t& input[type=\"password\"][disabled]:not(.ck-reset_all-excluded *) {\n\t\tcursor: default;\n\t}\n\n\t& fieldset:not(.ck-reset_all-excluded *) {\n\t\tpadding: 10px;\n\t\tborder: 2px groove hsl(255, 7%, 88%);\n\t}\n\n\t& button:not(.ck-reset_all-excluded *)::-moz-focus-inner {\n\t\t/* See http://stackoverflow.com/questions/5517744/remove-extra-button-spacing-padding-in-firefox */\n\t\tpadding: 0;\n\t\tborder: 0\n\t}\n}\n\n/**\n * Default UI rules for RTL languages.\n */\n.ck[dir=\"rtl\"],\n.ck[dir=\"rtl\"] .ck {\n\ttext-align: right;\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * Default border-radius value.\n */\n:root{\n\t--ck-border-radius: 2px;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t/**\n\t * A visual style of element's inner shadow (i.e. input).\n\t */\n\t--ck-inner-shadow: 2px 2px 3px var(--ck-color-shadow-inner) inset;\n\n\t/**\n\t * A visual style of element's drop shadow (i.e. panel).\n\t */\n\t--ck-drop-shadow: 0 1px 2px 1px var(--ck-color-shadow-drop);\n\n\t/**\n\t * A visual style of element's active shadow (i.e. comment or suggestion).\n\t */\n\t--ck-drop-shadow-active: 0 3px 6px 1px var(--ck-color-shadow-drop-active);\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-spacing-unit: \t\t\t\t\t\t0.6em;\n\t--ck-spacing-large: \t\t\t\t\tcalc(var(--ck-spacing-unit) * 1.5);\n\t--ck-spacing-standard: \t\t\t\t\tvar(--ck-spacing-unit);\n\t--ck-spacing-medium: \t\t\t\t\tcalc(var(--ck-spacing-unit) * 0.8);\n\t--ck-spacing-small: \t\t\t\t\tcalc(var(--ck-spacing-unit) * 0.5);\n\t--ck-spacing-tiny: \t\t\t\t\t\tcalc(var(--ck-spacing-unit) * 0.3);\n\t--ck-spacing-extra-tiny: \t\t\t\tcalc(var(--ck-spacing-unit) * 0.16);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 6507:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ":root{--ck-color-resizer:var(--ck-color-focus-border);--ck-color-resizer-tooltip-background:#262626;--ck-color-resizer-tooltip-text:#f2f2f2;--ck-resizer-border-radius:var(--ck-border-radius);--ck-resizer-tooltip-offset:10px;--ck-resizer-tooltip-height:calc(var(--ck-spacing-small)*2 + 10px)}.ck .ck-widget,.ck .ck-widget.ck-widget_with-selection-handle{position:relative}.ck .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle{position:absolute}.ck .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle .ck-icon{display:block}.ck .ck-widget.ck-widget_with-selection-handle.ck-widget_selected>.ck-widget__selection-handle,.ck .ck-widget.ck-widget_with-selection-handle:hover>.ck-widget__selection-handle{visibility:visible}.ck .ck-size-view{background:var(--ck-color-resizer-tooltip-background);border:1px solid var(--ck-color-resizer-tooltip-text);border-radius:var(--ck-resizer-border-radius);color:var(--ck-color-resizer-tooltip-text);display:block;font-size:var(--ck-font-size-tiny);height:var(--ck-resizer-tooltip-height);line-height:var(--ck-resizer-tooltip-height);padding:0 var(--ck-spacing-small)}.ck .ck-size-view.ck-orientation-above-center,.ck .ck-size-view.ck-orientation-bottom-left,.ck .ck-size-view.ck-orientation-bottom-right,.ck .ck-size-view.ck-orientation-top-left,.ck .ck-size-view.ck-orientation-top-right{position:absolute}.ck .ck-size-view.ck-orientation-top-left{left:var(--ck-resizer-tooltip-offset);top:var(--ck-resizer-tooltip-offset)}.ck .ck-size-view.ck-orientation-top-right{right:var(--ck-resizer-tooltip-offset);top:var(--ck-resizer-tooltip-offset)}.ck .ck-size-view.ck-orientation-bottom-right{bottom:var(--ck-resizer-tooltip-offset);right:var(--ck-resizer-tooltip-offset)}.ck .ck-size-view.ck-orientation-bottom-left{bottom:var(--ck-resizer-tooltip-offset);left:var(--ck-resizer-tooltip-offset)}.ck .ck-size-view.ck-orientation-above-center{left:50%;top:calc(var(--ck-resizer-tooltip-height)*-1);transform:translate(-50%)}:root{--ck-widget-outline-thickness:3px;--ck-widget-handler-icon-size:16px;--ck-widget-handler-animation-duration:200ms;--ck-widget-handler-animation-curve:ease;--ck-color-widget-blurred-border:#dedede;--ck-color-widget-hover-border:#ffc83d;--ck-color-widget-editable-focus-background:var(--ck-color-base-background);--ck-color-widget-drag-handler-icon-color:var(--ck-color-base-background)}.ck .ck-widget{outline-color:transparent;outline-style:solid;outline-width:var(--ck-widget-outline-thickness);transition:outline-color var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve)}.ck .ck-widget.ck-widget_selected,.ck .ck-widget.ck-widget_selected:hover{outline:var(--ck-widget-outline-thickness) solid var(--ck-color-focus-border)}.ck .ck-widget:hover{outline-color:var(--ck-color-widget-hover-border)}.ck .ck-editor__nested-editable{border:1px solid transparent}.ck .ck-editor__nested-editable.ck-editor__nested-editable_focused,.ck .ck-editor__nested-editable:focus{background-color:var(--ck-color-widget-editable-focus-background);border:var(--ck-focus-ring);box-shadow:var(--ck-inner-shadow),0 0;outline:none}.ck .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle{background-color:transparent;border-radius:var(--ck-border-radius) var(--ck-border-radius) 0 0;box-sizing:border-box;left:calc(0px - var(--ck-widget-outline-thickness));opacity:0;padding:4px;top:0;transform:translateY(-100%);transition:background-color var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve),visibility var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve),opacity var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve)}.ck .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle .ck-icon{color:var(--ck-color-widget-drag-handler-icon-color);height:var(--ck-widget-handler-icon-size);width:var(--ck-widget-handler-icon-size)}.ck .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle .ck-icon .ck-icon__selected-indicator{opacity:0;transition:opacity .3s var(--ck-widget-handler-animation-curve)}.ck .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle:hover .ck-icon .ck-icon__selected-indicator{opacity:1}.ck .ck-widget.ck-widget_with-selection-handle:hover>.ck-widget__selection-handle{background-color:var(--ck-color-widget-hover-border);opacity:1}.ck .ck-widget.ck-widget_with-selection-handle.ck-widget_selected:hover>.ck-widget__selection-handle,.ck .ck-widget.ck-widget_with-selection-handle.ck-widget_selected>.ck-widget__selection-handle{background-color:var(--ck-color-focus-border);opacity:1}.ck .ck-widget.ck-widget_with-selection-handle.ck-widget_selected:hover>.ck-widget__selection-handle .ck-icon .ck-icon__selected-indicator,.ck .ck-widget.ck-widget_with-selection-handle.ck-widget_selected>.ck-widget__selection-handle .ck-icon .ck-icon__selected-indicator{opacity:1}.ck[dir=rtl] .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle{left:auto;right:calc(0px - var(--ck-widget-outline-thickness))}.ck.ck-editor__editable.ck-read-only .ck-widget{transition:none}.ck.ck-editor__editable.ck-read-only .ck-widget:not(.ck-widget_selected){--ck-widget-outline-thickness:0px}.ck.ck-editor__editable.ck-read-only .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle,.ck.ck-editor__editable.ck-read-only .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle:hover{background:var(--ck-color-widget-blurred-border)}.ck.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected,.ck.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected:hover{outline-color:var(--ck-color-widget-blurred-border)}.ck.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected.ck-widget_with-selection-handle:hover>.ck-widget__selection-handle,.ck.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected.ck-widget_with-selection-handle:hover>.ck-widget__selection-handle:hover,.ck.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected.ck-widget_with-selection-handle>.ck-widget__selection-handle,.ck.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected.ck-widget_with-selection-handle>.ck-widget__selection-handle:hover{background:var(--ck-color-widget-blurred-border)}.ck.ck-editor__editable blockquote>.ck-widget.ck-widget_with-selection-handle:first-child,.ck.ck-editor__editable>.ck-widget.ck-widget_with-selection-handle:first-child{margin-top:calc(1em + var(--ck-widget-handler-icon-size))}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-widget/theme/widget.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-widget/widget.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_focus.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/mixins/_shadow.css"],"names":[],"mappings":"AAKA,MACC,+CAAgD,CAChD,6CAAsD,CACtD,uCAAgD,CAEhD,kDAAmD,CACnD,gCAAiC,CACjC,kEACD,CAOA,8DAEC,iBAqBD,CAnBC,4EACC,iBAOD,CALC,qFAGC,aACD,CASD,iLACC,kBACD,CAGD,kBACC,qDAAsD,CAEtD,qDAAsD,CACtD,6CAA8C,CAF9C,0CAA2C,CAI3C,aAAc,CADd,kCAAmC,CAGnC,uCAAwC,CACxC,4CAA6C,CAF7C,iCAsCD,CAlCC,8NAKC,iBACD,CAEA,0CAEC,qCAAsC,CADtC,oCAED,CAEA,2CAEC,sCAAuC,CADvC,oCAED,CAEA,8CACC,uCAAwC,CACxC,sCACD,CAEA,6CACC,uCAAwC,CACxC,qCACD,CAGA,8CAEC,QAAS,CADT,6CAAgD,CAEhD,yBACD,CCjFD,MACC,iCAAkC,CAClC,kCAAmC,CACnC,4CAA6C,CAC7C,wCAAyC,CAEzC,wCAAiD,CACjD,sCAAkD,CAClD,2EAA4E,CAC5E,yEACD,CAEA,eAGC,yBAA0B,CAD1B,mBAAoB,CADpB,gDAAiD,CAGjD,6GAUD,CARC,0EAEC,6EACD,CAEA,qBACC,iDACD,CAGD,gCACC,4BAWD,CAPC,yGAKC,iEAAkE,CCnCnE,2BAA2B,CCF3B,qCAA8B,CDC9B,YDqCA,CAIA,4EAKC,4BAA6B,CAa7B,iEAAkE,CAhBlE,qBAAsB,CAoBtB,mDAAoD,CAhBpD,SAAU,CALV,WAAY,CAsBZ,KAAM,CAFN,2BAA4B,CAT5B,6SAgCD,CAnBC,qFAIC,oDAAqD,CADrD,yCAA0C,CAD1C,wCAWD,CANC,kHACC,SAAU,CAGV,+DACD,CAID,wHACC,SACD,CAID,kFAEC,oDAAqD,CADrD,SAED,CAKC,oMAEC,6CAA8C,CAD9C,SAOD,CAHC,gRACC,SACD,CAOH,qFACC,SAAU,CACV,oDACD,CAGA,gDAEC,eAkBD,CAhBC,yEAOC,iCACD,CAGC,gOAEC,gDACD,CAOD,wIAEC,mDAQD,CALE,ghBAEC,gDACD,CAKH,yKAOC,yDACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-color-resizer: var(--ck-color-focus-border);\n\t--ck-color-resizer-tooltip-background: hsl(0, 0%, 15%);\n\t--ck-color-resizer-tooltip-text: hsl(0, 0%, 95%);\n\n\t--ck-resizer-border-radius: var(--ck-border-radius);\n\t--ck-resizer-tooltip-offset: 10px;\n\t--ck-resizer-tooltip-height: calc(var(--ck-spacing-small) * 2 + 10px);\n}\n\n.ck .ck-widget {\n\t/* This is neccessary for type around UI to be positioned properly. */\n\tposition: relative;\n}\n\n.ck .ck-widget.ck-widget_with-selection-handle {\n\t/* Make the widget wrapper a relative positioning container for the drag handle. */\n\tposition: relative;\n\n\t& .ck-widget__selection-handle {\n\t\tposition: absolute;\n\n\t\t& .ck-icon {\n\t\t\t/* Make sure the icon in not a subject to font-size or line-height to avoid\n\t\t\tunnecessary spacing around it. */\n\t\t\tdisplay: block;\n\t\t}\n\t}\n\n\t/* Show the selection handle on mouse hover over the widget, but not for nested widgets. */\n\t&:hover > .ck-widget__selection-handle {\n\t\tvisibility: visible;\n\t}\n\n\t/* Show the selection handle when the widget is selected, but not for nested widgets. */\n\t&.ck-widget_selected > .ck-widget__selection-handle {\n\t\tvisibility: visible;\n\t}\n}\n\n.ck .ck-size-view {\n\tbackground: var(--ck-color-resizer-tooltip-background);\n\tcolor: var(--ck-color-resizer-tooltip-text);\n\tborder: 1px solid var(--ck-color-resizer-tooltip-text);\n\tborder-radius: var(--ck-resizer-border-radius);\n\tfont-size: var(--ck-font-size-tiny);\n\tdisplay: block;\n\tpadding: 0 var(--ck-spacing-small);\n\theight: var(--ck-resizer-tooltip-height);\n\tline-height: var(--ck-resizer-tooltip-height);\n\n\t&.ck-orientation-top-left,\n\t&.ck-orientation-top-right,\n\t&.ck-orientation-bottom-right,\n\t&.ck-orientation-bottom-left,\n\t&.ck-orientation-above-center {\n\t\tposition: absolute;\n\t}\n\n\t&.ck-orientation-top-left {\n\t\ttop: var(--ck-resizer-tooltip-offset);\n\t\tleft: var(--ck-resizer-tooltip-offset);\n\t}\n\n\t&.ck-orientation-top-right {\n\t\ttop: var(--ck-resizer-tooltip-offset);\n\t\tright: var(--ck-resizer-tooltip-offset);\n\t}\n\n\t&.ck-orientation-bottom-right {\n\t\tbottom: var(--ck-resizer-tooltip-offset);\n\t\tright: var(--ck-resizer-tooltip-offset);\n\t}\n\n\t&.ck-orientation-bottom-left {\n\t\tbottom: var(--ck-resizer-tooltip-offset);\n\t\tleft: var(--ck-resizer-tooltip-offset);\n\t}\n\n\t/* Class applied if the widget is too small to contain the size label */\n\t&.ck-orientation-above-center {\n\t\ttop: calc(var(--ck-resizer-tooltip-height) * -1);\n\t\tleft: 50%;\n\t\ttransform: translate(-50%);\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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@import \"../mixins/_focus.css\";\n@import \"../mixins/_shadow.css\";\n\n:root {\n\t--ck-widget-outline-thickness: 3px;\n\t--ck-widget-handler-icon-size: 16px;\n\t--ck-widget-handler-animation-duration: 200ms;\n\t--ck-widget-handler-animation-curve: ease;\n\n\t--ck-color-widget-blurred-border: hsl(0, 0%, 87%);\n\t--ck-color-widget-hover-border: hsl(43, 100%, 62%);\n\t--ck-color-widget-editable-focus-background: var(--ck-color-base-background);\n\t--ck-color-widget-drag-handler-icon-color: var(--ck-color-base-background);\n}\n\n.ck .ck-widget {\n\toutline-width: var(--ck-widget-outline-thickness);\n\toutline-style: solid;\n\toutline-color: transparent;\n\ttransition: outline-color var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve);\n\n\t&.ck-widget_selected,\n\t&.ck-widget_selected:hover {\n\t\toutline: var(--ck-widget-outline-thickness) solid var(--ck-color-focus-border);\n\t}\n\n\t&:hover {\n\t\toutline-color: var(--ck-color-widget-hover-border);\n\t}\n}\n\n.ck .ck-editor__nested-editable {\n\tborder: 1px solid transparent;\n\n\t/* The :focus style is applied before .ck-editor__nested-editable_focused class is rendered in the view.\n\tThese styles show a different border for a blink of an eye, so `:focus` need to have same styles applied. */\n\t&.ck-editor__nested-editable_focused,\n\t&:focus {\n\t\t@mixin ck-focus-ring;\n\t\t@mixin ck-box-shadow var(--ck-inner-shadow);\n\n\t\tbackground-color: var(--ck-color-widget-editable-focus-background);\n\t}\n}\n\n.ck .ck-widget.ck-widget_with-selection-handle {\n\t& .ck-widget__selection-handle {\n\t\tpadding: 4px;\n\t\tbox-sizing: border-box;\n\n\t\t/* Background and opacity will be animated as the handler shows up or the widget gets selected. */\n\t\tbackground-color: transparent;\n\t\topacity: 0;\n\n\t\t/* Transition:\n\t\t   * background-color for the .ck-widget_selected state change,\n\t\t   * visibility for hiding the handler,\n\t\t   * opacity for the proper look of the icon when the handler disappears. */\n\t\ttransition:\n\t\t\tbackground-color var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve),\n\t\t\tvisibility var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve),\n\t\t\topacity var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve);\n\n\t\t/* Make only top corners round. */\n\t\tborder-radius: var(--ck-border-radius) var(--ck-border-radius) 0 0;\n\n\t\t/* Place the drag handler outside the widget wrapper. */\n\t\ttransform: translateY(-100%);\n\t\tleft: calc(0px - var(--ck-widget-outline-thickness));\n\t\ttop: 0;\n\n\t\t& .ck-icon {\n\t\t\t/* Make sure the dimensions of the icon are independent of the fon-size of the content. */\n\t\t\twidth: var(--ck-widget-handler-icon-size);\n\t\t\theight: var(--ck-widget-handler-icon-size);\n\t\t\tcolor: var(--ck-color-widget-drag-handler-icon-color);\n\n\t\t\t/* The \"selected\" part of the icon is invisible by default */\n\t\t\t& .ck-icon__selected-indicator {\n\t\t\t\topacity: 0;\n\n\t\t\t\t/* Note: The animation is longer on purpose. Simply feels better. */\n\t\t\t\ttransition: opacity 300ms var(--ck-widget-handler-animation-curve);\n\t\t\t}\n\t\t}\n\n\t\t/* Advertise using the look of the icon that once clicked the handler, the widget will be selected. */\n\t\t&:hover .ck-icon .ck-icon__selected-indicator {\n\t\t\topacity: 1;\n\t\t}\n\t}\n\n\t/* Show the selection handler on mouse hover over the widget, but not for nested widgets. */\n\t&:hover > .ck-widget__selection-handle {\n\t\topacity: 1;\n\t\tbackground-color: var(--ck-color-widget-hover-border);\n\t}\n\n\t/* Show the selection handler when the widget is selected, but not for nested widgets. */\n\t&.ck-widget_selected,\n\t&.ck-widget_selected:hover {\n\t\t& > .ck-widget__selection-handle {\n\t\t\topacity: 1;\n\t\t\tbackground-color: var(--ck-color-focus-border);\n\n\t\t\t/* When the widget is selected, notify the user using the proper look of the icon. */\n\t\t\t& .ck-icon .ck-icon__selected-indicator {\n\t\t\t\topacity: 1;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/* In a RTL environment, align the selection handler to the right side of the widget */\n/* stylelint-disable-next-line no-descending-specificity */\n.ck[dir=\"rtl\"] .ck-widget.ck-widget_with-selection-handle .ck-widget__selection-handle {\n\tleft: auto;\n\tright: calc(0px - var(--ck-widget-outline-thickness));\n}\n\n/* https://github.com/ckeditor/ckeditor5/issues/6415 */\n.ck.ck-editor__editable.ck-read-only .ck-widget {\n\t/* Prevent the :hover outline from showing up because of the used outline-color transition. */\n\ttransition: none;\n\n\t&:not(.ck-widget_selected) {\n\t\t/* Disable visual effects of hover/active widget when CKEditor is in readOnly mode.\n\t\t * See: https://github.com/ckeditor/ckeditor5/issues/1261\n\t\t *\n\t\t * Leave the unit because this custom property is used in calc() by other features.\n\t\t * See: https://github.com/ckeditor/ckeditor5/issues/6775\n\t\t */\n\t\t--ck-widget-outline-thickness: 0px;\n\t}\n\n\t&.ck-widget_with-selection-handle {\n\t\t& .ck-widget__selection-handle,\n\t\t& .ck-widget__selection-handle:hover {\n\t\t\tbackground: var(--ck-color-widget-blurred-border);\n\t\t}\n\t}\n}\n\n/* Style the widget when it's selected but the editable it belongs to lost focus. */\n/* stylelint-disable-next-line no-descending-specificity */\n.ck.ck-editor__editable.ck-blurred .ck-widget {\n\t&.ck-widget_selected,\n\t&.ck-widget_selected:hover {\n\t\toutline-color: var(--ck-color-widget-blurred-border);\n\n\t\t&.ck-widget_with-selection-handle {\n\t\t\t& > .ck-widget__selection-handle,\n\t\t\t& > .ck-widget__selection-handle:hover {\n\t\t\t\tbackground: var(--ck-color-widget-blurred-border);\n\t\t\t}\n\t\t}\n\t}\n}\n\n.ck.ck-editor__editable > .ck-widget.ck-widget_with-selection-handle:first-child,\n.ck.ck-editor__editable blockquote > .ck-widget.ck-widget_with-selection-handle:first-child {\n\t/* Do not crop selection handler if a widget is a first-child in the blockquote or in the root editable.\n\tIn fact, anything with overflow: hidden.\n\thttps://github.com/ckeditor/ckeditor5-block-quote/issues/28\n\thttps://github.com/ckeditor/ckeditor5-widget/issues/44\n\thttps://github.com/ckeditor/ckeditor5-widget/issues/66 */\n\tmargin-top: calc(1em + var(--ck-widget-handler-icon-size));\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A visual style of focused element's border.\n */\n@define-mixin ck-focus-ring {\n\t/* Disable native outline. */\n\toutline: none;\n\tborder: var(--ck-focus-ring)\n}\n","/*\n * Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.\n * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license\n */\n\n/**\n * A helper to combine multiple shadows.\n */\n@define-mixin ck-box-shadow $shadowA, $shadowB: 0 0 {\n\tbox-shadow: $shadowA, $shadowB;\n}\n\n/**\n * Gives an element a drop shadow so it looks like a floating panel.\n */\n@define-mixin ck-drop-shadow {\n\t@mixin ck-box-shadow var(--ck-drop-shadow);\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 2263:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-widget_with-resizer{position:relative}.ck .ck-widget__resizer{display:none;left:0;pointer-events:none;position:absolute;top:0}.ck-focused .ck-widget_with-resizer.ck-widget_selected>.ck-widget__resizer{display:block}.ck .ck-widget__resizer__handle{pointer-events:all;position:absolute}.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-bottom-right,.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-top-left{cursor:nwse-resize}.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-bottom-left,.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-top-right{cursor:nesw-resize}:root{--ck-resizer-size:10px;--ck-resizer-offset:calc(var(--ck-resizer-size)/-2 - 2px);--ck-resizer-border-width:1px}.ck .ck-widget__resizer{outline:1px solid var(--ck-color-resizer)}.ck .ck-widget__resizer__handle{background:var(--ck-color-focus-border);border:var(--ck-resizer-border-width) solid #fff;border-radius:var(--ck-resizer-border-radius);height:var(--ck-resizer-size);width:var(--ck-resizer-size)}.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-top-left{left:var(--ck-resizer-offset);top:var(--ck-resizer-offset)}.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-top-right{right:var(--ck-resizer-offset);top:var(--ck-resizer-offset)}.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-bottom-right{bottom:var(--ck-resizer-offset);right:var(--ck-resizer-offset)}.ck .ck-widget__resizer__handle.ck-widget__resizer__handle-bottom-left{bottom:var(--ck-resizer-offset);left:var(--ck-resizer-offset)}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-widget/theme/widgetresize.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-widget/widgetresize.css"],"names":[],"mappings":"AAKA,4BAEC,iBACD,CAEA,wBACC,YAAa,CAMb,MAAO,CAFP,mBAAoB,CAHpB,iBAAkB,CAMlB,KACD,CAGC,2EACC,aACD,CAGD,gCAIC,kBAAmB,CAHnB,iBAcD,CATC,4IAEC,kBACD,CAEA,4IAEC,kBACD,CCpCD,MACC,sBAAuB,CAGvB,yDAAiE,CACjE,6BACD,CAEA,wBACC,yCACD,CAEA,gCAGC,uCAAwC,CACxC,gDAA6D,CAC7D,6CAA8C,CAH9C,6BAA8B,CAD9B,4BAyBD,CAnBC,oEAEC,6BAA8B,CAD9B,4BAED,CAEA,qEAEC,8BAA+B,CAD/B,4BAED,CAEA,wEACC,+BAAgC,CAChC,8BACD,CAEA,uEACC,+BAAgC,CAChC,6BACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck .ck-widget_with-resizer {\n\t/* Make the widget wrapper a relative positioning container for the drag handle. */\n\tposition: relative;\n}\n\n.ck .ck-widget__resizer {\n\tdisplay: none;\n\tposition: absolute;\n\n\t/* The wrapper itself should not interfere with the pointer device, only the handles should. */\n\tpointer-events: none;\n\n\tleft: 0;\n\ttop: 0;\n}\n\n.ck-focused .ck-widget_with-resizer.ck-widget_selected {\n\t& > .ck-widget__resizer {\n\t\tdisplay: block;\n\t}\n}\n\n.ck .ck-widget__resizer__handle {\n\tposition: absolute;\n\n\t/* Resizers are the only UI elements that should interfere with a pointer device. */\n\tpointer-events: all;\n\n\t&.ck-widget__resizer__handle-top-left,\n\t&.ck-widget__resizer__handle-bottom-right {\n\t\tcursor: nwse-resize;\n\t}\n\n\t&.ck-widget__resizer__handle-top-right,\n\t&.ck-widget__resizer__handle-bottom-left {\n\t\tcursor: nesw-resize;\n\t}\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-resizer-size: 10px;\n\n\t/* Set the resizer with a 50% offset. */\n\t--ck-resizer-offset: calc( ( var(--ck-resizer-size) / -2 ) - 2px);\n\t--ck-resizer-border-width: 1px;\n}\n\n.ck .ck-widget__resizer {\n\toutline: 1px solid var(--ck-color-resizer);\n}\n\n.ck .ck-widget__resizer__handle {\n\twidth: var(--ck-resizer-size);\n\theight: var(--ck-resizer-size);\n\tbackground: var(--ck-color-focus-border);\n\tborder: var(--ck-resizer-border-width) solid hsl(0, 0%, 100%);\n\tborder-radius: var(--ck-resizer-border-radius);\n\n\t&.ck-widget__resizer__handle-top-left {\n\t\ttop: var(--ck-resizer-offset);\n\t\tleft: var(--ck-resizer-offset);\n\t}\n\n\t&.ck-widget__resizer__handle-top-right {\n\t\ttop: var(--ck-resizer-offset);\n\t\tright: var(--ck-resizer-offset);\n\t}\n\n\t&.ck-widget__resizer__handle-bottom-right {\n\t\tbottom: var(--ck-resizer-offset);\n\t\tright: var(--ck-resizer-offset);\n\t}\n\n\t&.ck-widget__resizer__handle-bottom-left {\n\t\tbottom: var(--ck-resizer-offset);\n\t\tleft: var(--ck-resizer-offset);\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 5137:
/***/ ((module, __webpack_exports__, __webpack_require__) => {

/* harmony export */ __webpack_require__.d(__webpack_exports__, {
/* harmony export */   "Z": () => (__WEBPACK_DEFAULT_EXPORT__)
/* harmony export */ });
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(4015);
/* harmony import */ var _css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0__);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__ = __webpack_require__(3645);
/* harmony import */ var _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__webpack_require__.n(_css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1__);
// Imports


var ___CSS_LOADER_EXPORT___ = _css_loader_dist_runtime_api_js__WEBPACK_IMPORTED_MODULE_1___default()((_css_loader_dist_runtime_cssWithMappingToString_js__WEBPACK_IMPORTED_MODULE_0___default()));
// Module
___CSS_LOADER_EXPORT___.push([module.id, ".ck .ck-widget .ck-widget__type-around__button{display:block;overflow:hidden;position:absolute;z-index:var(--ck-z-default)}.ck .ck-widget .ck-widget__type-around__button svg{left:50%;position:absolute;top:50%;z-index:calc(var(--ck-z-default) + 2)}.ck .ck-widget .ck-widget__type-around__button.ck-widget__type-around__button_before{left:min(10%,30px);top:calc(var(--ck-widget-outline-thickness)*-.5);transform:translateY(-50%)}.ck .ck-widget .ck-widget__type-around__button.ck-widget__type-around__button_after{bottom:calc(var(--ck-widget-outline-thickness)*-.5);right:min(10%,30px);transform:translateY(50%)}.ck .ck-widget.ck-widget_selected>.ck-widget__type-around>.ck-widget__type-around__button:after,.ck .ck-widget>.ck-widget__type-around>.ck-widget__type-around__button:hover:after{content:\"\";display:block;left:1px;position:absolute;top:1px;z-index:calc(var(--ck-z-default) + 1)}.ck .ck-widget>.ck-widget__type-around>.ck-widget__type-around__fake-caret{display:none;left:0;position:absolute;right:0}.ck .ck-widget:hover>.ck-widget__type-around>.ck-widget__type-around__fake-caret{left:calc(var(--ck-widget-outline-thickness)*-1);right:calc(var(--ck-widget-outline-thickness)*-1)}.ck .ck-widget.ck-widget_type-around_show-fake-caret_before>.ck-widget__type-around>.ck-widget__type-around__fake-caret{display:block;top:calc(var(--ck-widget-outline-thickness)*-1 - 1px)}.ck .ck-widget.ck-widget_type-around_show-fake-caret_after>.ck-widget__type-around>.ck-widget__type-around__fake-caret{bottom:calc(var(--ck-widget-outline-thickness)*-1 - 1px);display:block}.ck.ck-editor__editable.ck-read-only .ck-widget__type-around,.ck.ck-editor__editable.ck-restricted-editing_mode_restricted .ck-widget__type-around,.ck.ck-editor__editable.ck-widget__type-around_disabled .ck-widget__type-around{display:none}:root{--ck-widget-type-around-button-size:20px;--ck-color-widget-type-around-button-active:var(--ck-color-focus-border);--ck-color-widget-type-around-button-hover:var(--ck-color-widget-hover-border);--ck-color-widget-type-around-button-blurred-editable:var(--ck-color-widget-blurred-border);--ck-color-widget-type-around-button-radar-start-alpha:0;--ck-color-widget-type-around-button-radar-end-alpha:.3;--ck-color-widget-type-around-button-icon:var(--ck-color-base-background)}.ck .ck-widget .ck-widget__type-around__button{background:var(--ck-color-widget-type-around-button);border-radius:100px;height:var(--ck-widget-type-around-button-size);opacity:0;pointer-events:none;transition:opacity var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve),background var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve);width:var(--ck-widget-type-around-button-size)}.ck .ck-widget .ck-widget__type-around__button svg{height:8px;margin-top:1px;transform:translate(-50%,-50%);transition:transform .5s ease;width:10px}.ck .ck-widget .ck-widget__type-around__button svg *{stroke-dasharray:10;stroke-dashoffset:0;fill:none;stroke:var(--ck-color-widget-type-around-button-icon);stroke-width:1.5px;stroke-linecap:round;stroke-linejoin:round}.ck .ck-widget .ck-widget__type-around__button svg line{stroke-dasharray:7}.ck .ck-widget .ck-widget__type-around__button:hover{animation:ck-widget-type-around-button-sonar 1s ease infinite}.ck .ck-widget .ck-widget__type-around__button:hover svg polyline{animation:ck-widget-type-around-arrow-dash 2s linear}.ck .ck-widget .ck-widget__type-around__button:hover svg line{animation:ck-widget-type-around-arrow-tip-dash 2s linear}.ck .ck-widget.ck-widget_selected>.ck-widget__type-around>.ck-widget__type-around__button,.ck .ck-widget:hover>.ck-widget__type-around>.ck-widget__type-around__button{opacity:1;pointer-events:auto}.ck .ck-widget:not(.ck-widget_selected)>.ck-widget__type-around>.ck-widget__type-around__button{background:var(--ck-color-widget-type-around-button-hover)}.ck .ck-widget.ck-widget_selected>.ck-widget__type-around>.ck-widget__type-around__button,.ck .ck-widget>.ck-widget__type-around>.ck-widget__type-around__button:hover{background:var(--ck-color-widget-type-around-button-active)}.ck .ck-widget.ck-widget_selected>.ck-widget__type-around>.ck-widget__type-around__button:after,.ck .ck-widget>.ck-widget__type-around>.ck-widget__type-around__button:hover:after{background:linear-gradient(135deg,hsla(0,0%,100%,0),hsla(0,0%,100%,.3));border-radius:100px;height:calc(var(--ck-widget-type-around-button-size) - 2px);width:calc(var(--ck-widget-type-around-button-size) - 2px)}.ck .ck-widget.ck-widget_with-selection-handle>.ck-widget__type-around>.ck-widget__type-around__button_before{margin-left:20px}.ck .ck-widget .ck-widget__type-around__fake-caret{animation:ck-widget-type-around-fake-caret-pulse 1s linear infinite normal forwards;background:var(--ck-color-base-text);height:1px;outline:1px solid hsla(0,0%,100%,.5);pointer-events:none}.ck .ck-widget.ck-widget_selected.ck-widget_type-around_show-fake-caret_after,.ck .ck-widget.ck-widget_selected.ck-widget_type-around_show-fake-caret_before{outline-color:transparent}.ck .ck-widget.ck-widget_type-around_show-fake-caret_after.ck-widget_selected:hover,.ck .ck-widget.ck-widget_type-around_show-fake-caret_before.ck-widget_selected:hover{outline-color:var(--ck-color-widget-hover-border)}.ck .ck-widget.ck-widget_type-around_show-fake-caret_after>.ck-widget__type-around>.ck-widget__type-around__button,.ck .ck-widget.ck-widget_type-around_show-fake-caret_before>.ck-widget__type-around>.ck-widget__type-around__button{opacity:0;pointer-events:none}.ck .ck-widget.ck-widget_type-around_show-fake-caret_after.ck-widget_selected.ck-widget_with-resizer>.ck-widget__resizer,.ck .ck-widget.ck-widget_type-around_show-fake-caret_after.ck-widget_with-selection-handle.ck-widget_selected:hover>.ck-widget__selection-handle,.ck .ck-widget.ck-widget_type-around_show-fake-caret_after.ck-widget_with-selection-handle.ck-widget_selected>.ck-widget__selection-handle,.ck .ck-widget.ck-widget_type-around_show-fake-caret_before.ck-widget_selected.ck-widget_with-resizer>.ck-widget__resizer,.ck .ck-widget.ck-widget_type-around_show-fake-caret_before.ck-widget_with-selection-handle.ck-widget_selected:hover>.ck-widget__selection-handle,.ck .ck-widget.ck-widget_type-around_show-fake-caret_before.ck-widget_with-selection-handle.ck-widget_selected>.ck-widget__selection-handle{opacity:0}.ck[dir=rtl] .ck-widget.ck-widget_with-selection-handle .ck-widget__type-around>.ck-widget__type-around__button_before{margin-left:0;margin-right:20px}.ck-editor__nested-editable.ck-editor__editable_selected .ck-widget.ck-widget_selected>.ck-widget__type-around>.ck-widget__type-around__button,.ck-editor__nested-editable.ck-editor__editable_selected .ck-widget:hover>.ck-widget__type-around>.ck-widget__type-around__button{opacity:0;pointer-events:none}.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected>.ck-widget__type-around>.ck-widget__type-around__button:not(:hover){background:var(--ck-color-widget-type-around-button-blurred-editable)}.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected>.ck-widget__type-around>.ck-widget__type-around__button:not(:hover) svg *{stroke:#999}@keyframes ck-widget-type-around-arrow-dash{0%{stroke-dashoffset:10}20%,to{stroke-dashoffset:0}}@keyframes ck-widget-type-around-arrow-tip-dash{0%,20%{stroke-dashoffset:7}40%,to{stroke-dashoffset:0}}@keyframes ck-widget-type-around-button-sonar{0%{box-shadow:0 0 0 0 hsla(var(--ck-color-focus-border-coordinates),var(--ck-color-widget-type-around-button-radar-start-alpha))}50%{box-shadow:0 0 0 5px hsla(var(--ck-color-focus-border-coordinates),var(--ck-color-widget-type-around-button-radar-end-alpha))}to{box-shadow:0 0 0 5px hsla(var(--ck-color-focus-border-coordinates),var(--ck-color-widget-type-around-button-radar-start-alpha))}}@keyframes ck-widget-type-around-fake-caret-pulse{0%{opacity:1}49%{opacity:1}50%{opacity:0}99%{opacity:0}to{opacity:1}}", "",{"version":3,"sources":["webpack://./node_modules/@ckeditor/ckeditor5-widget/theme/widgettypearound.css","webpack://./node_modules/@ckeditor/ckeditor5-theme-lark/theme/ckeditor5-widget/widgettypearound.css"],"names":[],"mappings":"AASC,+CACC,aAAc,CAEd,eAAgB,CADhB,iBAAkB,CAElB,2BAwBD,CAtBC,mDAGC,QAAS,CAFT,iBAAkB,CAClB,OAAQ,CAER,qCACD,CAEA,qFAGC,kBAAoB,CADpB,gDAAoD,CAGpD,0BACD,CAEA,oFAEC,mDAAuD,CACvD,mBAAqB,CAErB,yBACD,CAUA,mLACC,UAAW,CACX,aAAc,CAGd,QAAS,CAFT,iBAAkB,CAClB,OAAQ,CAER,qCACD,CAMD,2EACC,YAAa,CAEb,MAAO,CADP,iBAAkB,CAElB,OACD,CAOA,iFACC,gDAAqD,CACrD,iDACD,CAKA,wHAEC,aAAc,CADd,qDAED,CAKA,uHACC,wDAA6D,CAC7D,aACD,CAoBD,mOACC,YACD,CC3GA,MACC,wCAAyC,CACzC,wEAAyE,CACzE,8EAA+E,CAC/E,2FAA4F,CAC5F,wDAAyD,CACzD,uDAAwD,CACxD,yEACD,CAgBC,+CAGC,oDAAqD,CACrD,mBAAoB,CAFpB,+CAAgD,CAVjD,SAAU,CACV,mBAAoB,CAYnB,uMAAyM,CAJzM,8CAkDD,CA1CC,mDAEC,UAAW,CAGX,cAAe,CAFf,8BAA+B,CAC/B,6BAA8B,CAH9B,UAoBD,CAdC,qDACC,mBAAoB,CACpB,mBAAoB,CAEpB,SAAU,CACV,qDAAsD,CACtD,kBAAmB,CACnB,oBAAqB,CACrB,qBACD,CAEA,wDACC,kBACD,CAGD,qDAIC,6DAcD,CARE,kEACC,oDACD,CAEA,8DACC,wDACD,CAUF,uKAvED,SAAU,CACV,mBAwEC,CAOD,gGACC,0DACD,CAOA,uKAEC,2DAQD,CANC,mLAIC,uEAAkF,CADlF,mBAAoB,CADpB,2DAA4D,CAD5D,0DAID,CAOD,8GACC,gBACD,CAKA,mDAGC,mFAAoF,CAOpF,oCAAqC,CARrC,UAAW,CAOX,oCAAwC,CARxC,mBAUD,CAOC,6JAEC,yBACD,CAUA,yKACC,iDACD,CAMA,uOAlJD,SAAU,CACV,mBAmJC,CAoBA,6yBACC,SACD,CASF,uHACC,aAAc,CACd,iBACD,CAYG,iRAlMF,SAAU,CACV,mBAmME,CAQH,kIACC,qEAKD,CAHC,wIACC,WACD,CAGD,4CACC,GACC,oBACD,CACA,OACC,mBACD,CACD,CAEA,gDACC,OACC,mBACD,CACA,OACC,mBACD,CACD,CAEA,8CACC,GACC,6HACD,CACA,IACC,6HACD,CACA,GACC,+HACD,CACD,CAEA,kDACC,GACC,SACD,CACA,IACC,SACD,CACA,IACC,SACD,CACA,IACC,SACD,CACA,GACC,SACD,CACD","sourcesContent":["/*\n * Copyright (c) 2003-2022, 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.ck .ck-widget {\n\t/*\n\t * Styles of the type around buttons\n\t */\n\t& .ck-widget__type-around__button {\n\t\tdisplay: block;\n\t\tposition: absolute;\n\t\toverflow: hidden;\n\t\tz-index: var(--ck-z-default);\n\n\t\t& svg {\n\t\t\tposition: absolute;\n\t\t\ttop: 50%;\n\t\t\tleft: 50%;\n\t\t\tz-index: calc(var(--ck-z-default) + 2);\n\t\t}\n\n\t\t&.ck-widget__type-around__button_before {\n\t\t\t/* Place it in the middle of the outline */\n\t\t\ttop: calc(-0.5 * var(--ck-widget-outline-thickness));\n\t\t\tleft: min(10%, 30px);\n\n\t\t\ttransform: translateY(-50%);\n\t\t}\n\n\t\t&.ck-widget__type-around__button_after {\n\t\t\t/* Place it in the middle of the outline */\n\t\t\tbottom: calc(-0.5 * var(--ck-widget-outline-thickness));\n\t\t\tright: min(10%, 30px);\n\n\t\t\ttransform: translateY(50%);\n\t\t}\n\t}\n\n\t/*\n\t * Styles for the buttons when:\n\t * - the widget is selected,\n\t * - or the button is being hovered (regardless of the widget state).\n\t */\n\t&.ck-widget_selected > .ck-widget__type-around > .ck-widget__type-around__button,\n\t& > .ck-widget__type-around > .ck-widget__type-around__button:hover {\n\t\t&::after {\n\t\t\tcontent: \"\";\n\t\t\tdisplay: block;\n\t\t\tposition: absolute;\n\t\t\ttop: 1px;\n\t\t\tleft: 1px;\n\t\t\tz-index: calc(var(--ck-z-default) + 1);\n\t\t}\n\t}\n\n\t/*\n\t * Styles for the horizontal \"fake caret\" which is displayed when the user navigates using the keyboard.\n\t */\n\t& > .ck-widget__type-around > .ck-widget__type-around__fake-caret {\n\t\tdisplay: none;\n\t\tposition: absolute;\n\t\tleft: 0;\n\t\tright: 0;\n\t}\n\n\t/*\n\t * When the widget is hovered the \"fake caret\" would normally be narrower than the\n\t * extra outline displayed around the widget. Let's extend the \"fake caret\" to match\n\t * the full width of the widget.\n\t */\n\t&:hover > .ck-widget__type-around > .ck-widget__type-around__fake-caret {\n\t\tleft: calc( -1 * var(--ck-widget-outline-thickness) );\n\t\tright: calc( -1 * var(--ck-widget-outline-thickness) );\n\t}\n\n\t/*\n\t * Styles for the horizontal \"fake caret\" when it should be displayed before the widget (backward keyboard navigation).\n\t */\n\t&.ck-widget_type-around_show-fake-caret_before > .ck-widget__type-around > .ck-widget__type-around__fake-caret {\n\t\ttop: calc( -1 * var(--ck-widget-outline-thickness) - 1px );\n\t\tdisplay: block;\n\t}\n\n\t/*\n\t * Styles for the horizontal \"fake caret\" when it should be displayed after the widget (forward keyboard navigation).\n\t */\n\t&.ck-widget_type-around_show-fake-caret_after > .ck-widget__type-around > .ck-widget__type-around__fake-caret {\n\t\tbottom: calc( -1 * var(--ck-widget-outline-thickness) - 1px );\n\t\tdisplay: block;\n\t}\n}\n\n/*\n * Integration with the read-only mode of the editor.\n */\n.ck.ck-editor__editable.ck-read-only .ck-widget__type-around {\n\tdisplay: none;\n}\n\n/*\n * Integration with the restricted editing mode (feature) of the editor.\n */\n.ck.ck-editor__editable.ck-restricted-editing_mode_restricted .ck-widget__type-around {\n\tdisplay: none;\n}\n\n/*\n * Integration with the #isEnabled property of the WidgetTypeAround plugin.\n */\n.ck.ck-editor__editable.ck-widget__type-around_disabled .ck-widget__type-around {\n\tdisplay: none;\n}\n","/*\n * Copyright (c) 2003-2022, 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:root {\n\t--ck-widget-type-around-button-size: 20px;\n\t--ck-color-widget-type-around-button-active: var(--ck-color-focus-border);\n\t--ck-color-widget-type-around-button-hover: var(--ck-color-widget-hover-border);\n\t--ck-color-widget-type-around-button-blurred-editable: var(--ck-color-widget-blurred-border);\n\t--ck-color-widget-type-around-button-radar-start-alpha: 0;\n\t--ck-color-widget-type-around-button-radar-end-alpha: .3;\n\t--ck-color-widget-type-around-button-icon: var(--ck-color-base-background);\n}\n\n@define-mixin ck-widget-type-around-button-visible {\n\topacity: 1;\n\tpointer-events: auto;\n}\n\n@define-mixin ck-widget-type-around-button-hidden {\n\topacity: 0;\n\tpointer-events: none;\n}\n\n.ck .ck-widget {\n\t/*\n\t * Styles of the type around buttons\n\t */\n\t& .ck-widget__type-around__button {\n\t\twidth: var(--ck-widget-type-around-button-size);\n\t\theight: var(--ck-widget-type-around-button-size);\n\t\tbackground: var(--ck-color-widget-type-around-button);\n\t\tborder-radius: 100px;\n\t\ttransition: opacity var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve), background var(--ck-widget-handler-animation-duration) var(--ck-widget-handler-animation-curve);\n\n\t\t@mixin ck-widget-type-around-button-hidden;\n\n\t\t& svg {\n\t\t\twidth: 10px;\n\t\t\theight: 8px;\n\t\t\ttransform: translate(-50%,-50%);\n\t\t\ttransition: transform .5s ease;\n\t\t\tmargin-top: 1px;\n\n\t\t\t& * {\n\t\t\t\tstroke-dasharray: 10;\n\t\t\t\tstroke-dashoffset: 0;\n\n\t\t\t\tfill: none;\n\t\t\t\tstroke: var(--ck-color-widget-type-around-button-icon);\n\t\t\t\tstroke-width: 1.5px;\n\t\t\t\tstroke-linecap: round;\n\t\t\t\tstroke-linejoin: round;\n\t\t\t}\n\n\t\t\t& line {\n\t\t\t\tstroke-dasharray: 7;\n\t\t\t}\n\t\t}\n\n\t\t&:hover {\n\t\t\t/*\n\t\t\t * Display the \"sonar\" around the button when hovered.\n\t\t\t */\n\t\t\tanimation: ck-widget-type-around-button-sonar 1s ease infinite;\n\n\t\t\t/*\n\t\t\t * Animate active button's icon.\n\t\t\t */\n\t\t\t& svg {\n\t\t\t\t& polyline {\n\t\t\t\t\tanimation: ck-widget-type-around-arrow-dash 2s linear;\n\t\t\t\t}\n\n\t\t\t\t& line {\n\t\t\t\t\tanimation: ck-widget-type-around-arrow-tip-dash 2s linear;\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/*\n\t * Show type around buttons when the widget gets selected or being hovered.\n\t */\n\t&.ck-widget_selected,\n\t&:hover {\n\t\t& > .ck-widget__type-around > .ck-widget__type-around__button {\n\t\t\t@mixin ck-widget-type-around-button-visible;\n\t\t}\n\t}\n\n\t/*\n\t * Styles for the buttons when the widget is NOT selected (but the buttons are visible\n\t * and still can be hovered).\n\t */\n\t&:not(.ck-widget_selected) > .ck-widget__type-around > .ck-widget__type-around__button {\n\t\tbackground: var(--ck-color-widget-type-around-button-hover);\n\t}\n\n\t/*\n\t * Styles for the buttons when:\n\t * - the widget is selected,\n\t * - or the button is being hovered (regardless of the widget state).\n\t */\n\t&.ck-widget_selected > .ck-widget__type-around > .ck-widget__type-around__button,\n\t& > .ck-widget__type-around > .ck-widget__type-around__button:hover {\n\t\tbackground: var(--ck-color-widget-type-around-button-active);\n\n\t\t&::after {\n\t\t\twidth: calc(var(--ck-widget-type-around-button-size) - 2px);\n\t\t\theight: calc(var(--ck-widget-type-around-button-size) - 2px);\n\t\t\tborder-radius: 100px;\n\t\t\tbackground: linear-gradient(135deg, hsla(0,0%,100%,0) 0%, hsla(0,0%,100%,.3) 100%);\n\t\t}\n\t}\n\n\t/*\n\t * Styles for the \"before\" button when the widget has a selection handle. Because some space\n\t * is consumed by the handle, the button must be moved slightly to the right to let it breathe.\n\t */\n\t&.ck-widget_with-selection-handle > .ck-widget__type-around > .ck-widget__type-around__button_before {\n\t\tmargin-left: 20px;\n\t}\n\n\t/*\n\t * Styles for the horizontal \"fake caret\" which is displayed when the user navigates using the keyboard.\n\t */\n\t& .ck-widget__type-around__fake-caret {\n\t\tpointer-events: none;\n\t\theight: 1px;\n\t\tanimation: ck-widget-type-around-fake-caret-pulse linear 1s infinite normal forwards;\n\n\t\t/*\n\t\t * The semi-transparent-outline+background combo improves the contrast\n\t\t * when the background underneath the fake caret is dark.\n\t\t */\n\t\toutline: solid 1px hsla(0, 0%, 100%, .5);\n\t\tbackground: var(--ck-color-base-text);\n\t}\n\n\t/*\n\t * Styles of the widget when the \"fake caret\" is blinking (e.g. upon keyboard navigation).\n\t * Despite the widget being physically selected in the model, its outline should disappear.\n\t */\n\t&.ck-widget_selected {\n\t\t&.ck-widget_type-around_show-fake-caret_before,\n\t\t&.ck-widget_type-around_show-fake-caret_after {\n\t\t\toutline-color: transparent;\n\t\t}\n\t}\n\n\t&.ck-widget_type-around_show-fake-caret_before,\n\t&.ck-widget_type-around_show-fake-caret_after {\n\t\t/*\n\t\t * When the \"fake caret\" is visible we simulate that the widget is not selected\n\t\t * (despite being physically selected), so the outline color should be for the\n\t\t * unselected widget.\n\t\t */\n\t\t&.ck-widget_selected:hover {\n\t\t\toutline-color: var(--ck-color-widget-hover-border);\n\t\t}\n\n\t\t/*\n\t\t * Styles of the type around buttons when the \"fake caret\" is blinking (e.g. upon keyboard navigation).\n\t\t * In this state, the type around buttons would collide with the fake carets so they should disappear.\n\t\t */\n\t\t& > .ck-widget__type-around > .ck-widget__type-around__button {\n\t\t\t@mixin ck-widget-type-around-button-hidden;\n\t\t}\n\n\t\t/*\n\t\t * Fake horizontal caret integration with the selection handle. When the caret is visible, simply\n\t\t * hide the handle because it intersects with the caret (and does not make much sense anyway).\n\t\t */\n\t\t&.ck-widget_with-selection-handle {\n\t\t\t&.ck-widget_selected,\n\t\t\t&.ck-widget_selected:hover {\n\t\t\t\t& > .ck-widget__selection-handle {\n\t\t\t\t\topacity: 0\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\t/*\n\t\t * Fake horizontal caret integration with the resize UI. When the caret is visible, simply\n\t\t * hide the resize UI because it creates too much noise. It can be visible when the user\n\t\t * hovers the widget, though.\n\t\t */\n\t\t&.ck-widget_selected.ck-widget_with-resizer > .ck-widget__resizer {\n\t\t\topacity: 0\n\t\t}\n\t}\n}\n\n/*\n * Styles for the \"before\" button when the widget has a selection handle in an RTL environment.\n * The selection handler is aligned to the right side of the widget so there is no need to create\n * additional space for it next to the \"before\" button.\n */\n.ck[dir=\"rtl\"] .ck-widget.ck-widget_with-selection-handle .ck-widget__type-around > .ck-widget__type-around__button_before {\n\tmargin-left: 0;\n\tmargin-right: 20px;\n}\n\n/*\n * Hide type around buttons when the widget is selected as a child of a selected\n * nested editable (e.g. mulit-cell table selection).\n *\n * See https://github.com/ckeditor/ckeditor5/issues/7263.\n */\n.ck-editor__nested-editable.ck-editor__editable_selected {\n\t& .ck-widget {\n\t\t&.ck-widget_selected,\n\t\t&:hover {\n\t\t\t& > .ck-widget__type-around > .ck-widget__type-around__button {\n\t\t\t\t@mixin ck-widget-type-around-button-hidden;\n\t\t\t}\n\t\t}\n\t}\n}\n\n/*\n * Styles for the buttons when the widget is selected but the user clicked outside of the editor (blurred the editor).\n */\n.ck-editor__editable.ck-blurred .ck-widget.ck-widget_selected > .ck-widget__type-around > .ck-widget__type-around__button:not(:hover) {\n\tbackground: var(--ck-color-widget-type-around-button-blurred-editable);\n\n\t& svg * {\n\t\tstroke: hsl(0,0%,60%);\n\t}\n}\n\n@keyframes ck-widget-type-around-arrow-dash {\n\t0% {\n\t\tstroke-dashoffset: 10;\n\t}\n\t20%, 100% {\n\t\tstroke-dashoffset: 0;\n\t}\n}\n\n@keyframes ck-widget-type-around-arrow-tip-dash {\n\t0%, 20% {\n\t\tstroke-dashoffset: 7;\n\t}\n\t40%, 100% {\n\t\tstroke-dashoffset: 0;\n\t}\n}\n\n@keyframes ck-widget-type-around-button-sonar {\n\t0% {\n\t\tbox-shadow: 0 0 0 0 hsla(var(--ck-color-focus-border-coordinates), var(--ck-color-widget-type-around-button-radar-start-alpha));\n\t}\n\t50% {\n\t\tbox-shadow: 0 0 0 5px hsla(var(--ck-color-focus-border-coordinates), var(--ck-color-widget-type-around-button-radar-end-alpha));\n\t}\n\t100% {\n\t\tbox-shadow: 0 0 0 5px hsla(var(--ck-color-focus-border-coordinates), var(--ck-color-widget-type-around-button-radar-start-alpha));\n\t}\n}\n\n@keyframes ck-widget-type-around-fake-caret-pulse {\n\t0% {\n\t\topacity: 1;\n\t}\n\t49% {\n\t\topacity: 1;\n\t}\n\t50% {\n\t\topacity: 0;\n\t}\n\t99% {\n\t\topacity: 0;\n\t}\n\t100% {\n\t\topacity: 1;\n\t}\n}\n"],"sourceRoot":""}]);
// Exports
/* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = (___CSS_LOADER_EXPORT___);


/***/ }),

/***/ 3645:
/***/ ((module) => {



/*
  MIT License http://www.opensource.org/licenses/mit-license.php
  Author Tobias Koppers @sokra
*/
// css base code, injected by the css-loader
// eslint-disable-next-line func-names
module.exports = function (cssWithMappingToString) {
  var list = []; // return the list of modules as css string

  list.toString = function toString() {
    return this.map(function (item) {
      var content = cssWithMappingToString(item);

      if (item[2]) {
        return "@media ".concat(item[2], " {").concat(content, "}");
      }

      return content;
    }).join("");
  }; // import a list of modules into the list
  // eslint-disable-next-line func-names


  list.i = function (modules, mediaQuery, dedupe) {
    if (typeof modules === "string") {
      // eslint-disable-next-line no-param-reassign
      modules = [[null, modules, ""]];
    }

    var alreadyImportedModules = {};

    if (dedupe) {
      for (var i = 0; i < this.length; i++) {
        // eslint-disable-next-line prefer-destructuring
        var id = this[i][0];

        if (id != null) {
          alreadyImportedModules[id] = true;
        }
      }
    }

    for (var _i = 0; _i < modules.length; _i++) {
      var item = [].concat(modules[_i]);

      if (dedupe && alreadyImportedModules[item[0]]) {
        // eslint-disable-next-line no-continue
        continue;
      }

      if (mediaQuery) {
        if (!item[2]) {
          item[2] = mediaQuery;
        } else {
          item[2] = "".concat(mediaQuery, " and ").concat(item[2]);
        }
      }

      list.push(item);
    }
  };

  return list;
};

/***/ }),

/***/ 4015:
/***/ ((module) => {



function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _unsupportedIterableToArray(arr, i) || _nonIterableRest(); }

function _nonIterableRest() { throw new TypeError("Invalid attempt to destructure non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

function _iterableToArrayLimit(arr, i) { var _i = arr && (typeof Symbol !== "undefined" && arr[Symbol.iterator] || arr["@@iterator"]); if (_i == null) return; var _arr = []; var _n = true; var _d = false; var _s, _e; try { for (_i = _i.call(arr); !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"] != null) _i["return"](); } finally { if (_d) throw _e; } } return _arr; }

function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }

module.exports = function cssWithMappingToString(item) {
  var _item = _slicedToArray(item, 4),
      content = _item[1],
      cssMapping = _item[3];

  if (!cssMapping) {
    return content;
  }

  if (typeof btoa === "function") {
    // eslint-disable-next-line no-undef
    var base64 = btoa(unescape(encodeURIComponent(JSON.stringify(cssMapping))));
    var data = "sourceMappingURL=data:application/json;charset=utf-8;base64,".concat(base64);
    var sourceMapping = "/*# ".concat(data, " */");
    var sourceURLs = cssMapping.sources.map(function (source) {
      return "/*# sourceURL=".concat(cssMapping.sourceRoot || "").concat(source, " */");
    });
    return [content].concat(sourceURLs).concat([sourceMapping]).join("\n");
  }

  return [content].join("\n");
};

/***/ }),

/***/ 3379:
/***/ ((module, __unused_webpack_exports, __webpack_require__) => {



var isOldIE = function isOldIE() {
  var memo;
  return function memorize() {
    if (typeof memo === 'undefined') {
      // Test for IE <= 9 as proposed by Browserhacks
      // @see http://browserhacks.com/#hack-e71d8692f65334173fee715c222cb805
      // Tests for existence of standard globals is to allow style-loader
      // to operate correctly into non-standard environments
      // @see https://github.com/webpack-contrib/style-loader/issues/177
      memo = Boolean(window && document && document.all && !window.atob);
    }

    return memo;
  };
}();

var getTarget = function getTarget() {
  var memo = {};
  return function memorize(target) {
    if (typeof memo[target] === 'undefined') {
      var styleTarget = document.querySelector(target); // Special case to return head of iframe instead of iframe itself

      if (window.HTMLIFrameElement && styleTarget instanceof window.HTMLIFrameElement) {
        try {
          // This will throw an exception if access to iframe is blocked
          // due to cross-origin restrictions
          styleTarget = styleTarget.contentDocument.head;
        } catch (e) {
          // istanbul ignore next
          styleTarget = null;
        }
      }

      memo[target] = styleTarget;
    }

    return memo[target];
  };
}();

var stylesInDom = [];

function getIndexByIdentifier(identifier) {
  var result = -1;

  for (var i = 0; i < stylesInDom.length; i++) {
    if (stylesInDom[i].identifier === identifier) {
      result = i;
      break;
    }
  }

  return result;
}

function modulesToDom(list, options) {
  var idCountMap = {};
  var identifiers = [];

  for (var i = 0; i < list.length; i++) {
    var item = list[i];
    var id = options.base ? item[0] + options.base : item[0];
    var count = idCountMap[id] || 0;
    var identifier = "".concat(id, " ").concat(count);
    idCountMap[id] = count + 1;
    var index = getIndexByIdentifier(identifier);
    var obj = {
      css: item[1],
      media: item[2],
      sourceMap: item[3]
    };

    if (index !== -1) {
      stylesInDom[index].references++;
      stylesInDom[index].updater(obj);
    } else {
      stylesInDom.push({
        identifier: identifier,
        updater: addStyle(obj, options),
        references: 1
      });
    }

    identifiers.push(identifier);
  }

  return identifiers;
}

function insertStyleElement(options) {
  var style = document.createElement('style');
  var attributes = options.attributes || {};

  if (typeof attributes.nonce === 'undefined') {
    var nonce =  true ? __webpack_require__.nc : 0;

    if (nonce) {
      attributes.nonce = nonce;
    }
  }

  Object.keys(attributes).forEach(function (key) {
    style.setAttribute(key, attributes[key]);
  });

  if (typeof options.insert === 'function') {
    options.insert(style);
  } else {
    var target = getTarget(options.insert || 'head');

    if (!target) {
      throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");
    }

    target.appendChild(style);
  }

  return style;
}

function removeStyleElement(style) {
  // istanbul ignore if
  if (style.parentNode === null) {
    return false;
  }

  style.parentNode.removeChild(style);
}
/* istanbul ignore next  */


var replaceText = function replaceText() {
  var textStore = [];
  return function replace(index, replacement) {
    textStore[index] = replacement;
    return textStore.filter(Boolean).join('\n');
  };
}();

function applyToSingletonTag(style, index, remove, obj) {
  var css = remove ? '' : obj.media ? "@media ".concat(obj.media, " {").concat(obj.css, "}") : obj.css; // For old IE

  /* istanbul ignore if  */

  if (style.styleSheet) {
    style.styleSheet.cssText = replaceText(index, css);
  } else {
    var cssNode = document.createTextNode(css);
    var childNodes = style.childNodes;

    if (childNodes[index]) {
      style.removeChild(childNodes[index]);
    }

    if (childNodes.length) {
      style.insertBefore(cssNode, childNodes[index]);
    } else {
      style.appendChild(cssNode);
    }
  }
}

function applyToTag(style, options, obj) {
  var css = obj.css;
  var media = obj.media;
  var sourceMap = obj.sourceMap;

  if (media) {
    style.setAttribute('media', media);
  } else {
    style.removeAttribute('media');
  }

  if (sourceMap && typeof btoa !== 'undefined') {
    css += "\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))), " */");
  } // For old IE

  /* istanbul ignore if  */


  if (style.styleSheet) {
    style.styleSheet.cssText = css;
  } else {
    while (style.firstChild) {
      style.removeChild(style.firstChild);
    }

    style.appendChild(document.createTextNode(css));
  }
}

var singleton = null;
var singletonCounter = 0;

function addStyle(obj, options) {
  var style;
  var update;
  var remove;

  if (options.singleton) {
    var styleIndex = singletonCounter++;
    style = singleton || (singleton = insertStyleElement(options));
    update = applyToSingletonTag.bind(null, style, styleIndex, false);
    remove = applyToSingletonTag.bind(null, style, styleIndex, true);
  } else {
    style = insertStyleElement(options);
    update = applyToTag.bind(null, style, options);

    remove = function remove() {
      removeStyleElement(style);
    };
  }

  update(obj);
  return function updateStyle(newObj) {
    if (newObj) {
      if (newObj.css === obj.css && newObj.media === obj.media && newObj.sourceMap === obj.sourceMap) {
        return;
      }

      update(obj = newObj);
    } else {
      remove();
    }
  };
}

module.exports = function (list, options) {
  options = options || {}; // Force single-tag solution on IE6-9, which has a hard limit on the # of <style>
  // tags it will allow on a page

  if (!options.singleton && typeof options.singleton !== 'boolean') {
    options.singleton = isOldIE();
  }

  list = list || [];
  var lastIdentifiers = modulesToDom(list, options);
  return function update(newList) {
    newList = newList || [];

    if (Object.prototype.toString.call(newList) !== '[object Array]') {
      return;
    }

    for (var i = 0; i < lastIdentifiers.length; i++) {
      var identifier = lastIdentifiers[i];
      var index = getIndexByIdentifier(identifier);
      stylesInDom[index].references--;
    }

    var newLastIdentifiers = modulesToDom(newList, options);

    for (var _i = 0; _i < lastIdentifiers.length; _i++) {
      var _identifier = lastIdentifiers[_i];

      var _index = getIndexByIdentifier(_identifier);

      if (stylesInDom[_index].references === 0) {
        stylesInDom[_index].updater();

        stylesInDom.splice(_index, 1);
      }
    }

    lastIdentifiers = newLastIdentifiers;
  };
};

/***/ })

/******/ 	});
/************************************************************************/
/******/ 	// The module cache
/******/ 	var __webpack_module_cache__ = {};
/******/ 	
/******/ 	// The require function
/******/ 	function __webpack_require__(moduleId) {
/******/ 		// Check if module is in cache
/******/ 		var cachedModule = __webpack_module_cache__[moduleId];
/******/ 		if (cachedModule !== undefined) {
/******/ 			return cachedModule.exports;
/******/ 		}
/******/ 		// Create a new module (and put it into the cache)
/******/ 		var module = __webpack_module_cache__[moduleId] = {
/******/ 			id: moduleId,
/******/ 			// no module.loaded needed
/******/ 			exports: {}
/******/ 		};
/******/ 	
/******/ 		// Execute the module function
/******/ 		__webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/ 	
/******/ 		// Return the exports of the module
/******/ 		return module.exports;
/******/ 	}
/******/ 	
/************************************************************************/
/******/ 	/* webpack/runtime/compat get default export */
/******/ 	(() => {
/******/ 		// getDefaultExport function for compatibility with non-harmony modules
/******/ 		__webpack_require__.n = (module) => {
/******/ 			var getter = module && module.__esModule ?
/******/ 				() => (module['default']) :
/******/ 				() => (module);
/******/ 			__webpack_require__.d(getter, { a: getter });
/******/ 			return getter;
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/define property getters */
/******/ 	(() => {
/******/ 		// define getter functions for harmony exports
/******/ 		__webpack_require__.d = (exports, definition) => {
/******/ 			for(var key in definition) {
/******/ 				if(__webpack_require__.o(definition, key) && !__webpack_require__.o(exports, key)) {
/******/ 					Object.defineProperty(exports, key, { enumerable: true, get: definition[key] });
/******/ 				}
/******/ 			}
/******/ 		};
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/global */
/******/ 	(() => {
/******/ 		__webpack_require__.g = (function() {
/******/ 			if (typeof globalThis === 'object') return globalThis;
/******/ 			try {
/******/ 				return this || new Function('return this')();
/******/ 			} catch (e) {
/******/ 				if (typeof window === 'object') return window;
/******/ 			}
/******/ 		})();
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/hasOwnProperty shorthand */
/******/ 	(() => {
/******/ 		__webpack_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop))
/******/ 	})();
/******/ 	
/******/ 	/* webpack/runtime/nonce */
/******/ 	(() => {
/******/ 		__webpack_require__.nc = undefined;
/******/ 	})();
/******/ 	
/************************************************************************/
var __webpack_exports__ = {};
// This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
(() => {

// EXPORTS
__webpack_require__.d(__webpack_exports__, {
  "default": () => (/* binding */ ckeditor)
});

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/spy.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/spy
 */
/**
 * Creates a spy function (ala Sinon.js) that can be used to inspect call to it.
 *
 * The following are the present features:
 *
 * * spy.called: property set to `true` if the function has been called at least once.
 *
 * @returns {Function} The spy function.
 */
function spy() {
    return function spy() {
        spy.called = true;
    };
}
/* harmony default export */ const src_spy = (spy);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/eventinfo.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/eventinfo
 */

/**
 * The event object passed to event callbacks. It is used to provide information about the event as well as a tool to
 * manipulate it.
 */
class EventInfo {
    /**
     * @param {Object} source The emitter.
     * @param {String} name The event name.
     */
    constructor(source, name) {
        this.source = source;
        this.name = name;
        this.path = [];
        // The following methods are defined in the constructor because they must be re-created per instance.
        this.stop = src_spy();
        this.off = src_spy();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/uid.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/uid
 */
// A hash table of hex numbers to avoid using toString() in uid() which is costly.
// [ '00', '01', '02', ..., 'fe', 'ff' ]
const HEX_NUMBERS = new Array(256).fill('')
    .map((_, index) => ('0' + (index).toString(16)).slice(-2));
/**
 * Returns a unique id. The id starts with an "e" character and a randomly generated string of
 * 32 alphanumeric characters.
 *
 * **Note**: The characters the unique id is built from correspond to the hex number notation
 * (from "0" to "9", from "a" to "f"). In other words, each id corresponds to an "e" followed
 * by 16 8-bit numbers next to each other.
 *
 * @returns {String} An unique id string.
 */
function uid() {
    // Let's create some positive random 32bit integers first.
    //
    // 1. Math.random() is a float between 0 and 1.
    // 2. 0x100000000 is 2^32 = 4294967296.
    // 3. >>> 0 enforces integer (in JS all numbers are floating point).
    //
    // For instance:
    //		Math.random() * 0x100000000 = 3366450031.853859
    // but
    //		Math.random() * 0x100000000 >>> 0 = 3366450031.
    const r1 = Math.random() * 0x100000000 >>> 0;
    const r2 = Math.random() * 0x100000000 >>> 0;
    const r3 = Math.random() * 0x100000000 >>> 0;
    const r4 = Math.random() * 0x100000000 >>> 0;
    // Make sure that id does not start with number.
    return 'e' +
        HEX_NUMBERS[r1 >> 0 & 0xFF] +
        HEX_NUMBERS[r1 >> 8 & 0xFF] +
        HEX_NUMBERS[r1 >> 16 & 0xFF] +
        HEX_NUMBERS[r1 >> 24 & 0xFF] +
        HEX_NUMBERS[r2 >> 0 & 0xFF] +
        HEX_NUMBERS[r2 >> 8 & 0xFF] +
        HEX_NUMBERS[r2 >> 16 & 0xFF] +
        HEX_NUMBERS[r2 >> 24 & 0xFF] +
        HEX_NUMBERS[r3 >> 0 & 0xFF] +
        HEX_NUMBERS[r3 >> 8 & 0xFF] +
        HEX_NUMBERS[r3 >> 16 & 0xFF] +
        HEX_NUMBERS[r3 >> 24 & 0xFF] +
        HEX_NUMBERS[r4 >> 0 & 0xFF] +
        HEX_NUMBERS[r4 >> 8 & 0xFF] +
        HEX_NUMBERS[r4 >> 16 & 0xFF] +
        HEX_NUMBERS[r4 >> 24 & 0xFF];
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/priorities.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Provides group of constants to use instead of hardcoding numeric priority values.
 *
 * @namespace
 */
const priorities = {
    /**
     * Converts a string with priority name to it's numeric value. If `Number` is given, it just returns it.
     *
     * @static
     * @param {module:utils/priorities~PriorityString} [priority] Priority to convert.
     * @returns {Number} Converted priority.
     */
    get(priority = 'normal') {
        if (typeof priority != 'number') {
            return this[priority] || this.normal;
        }
        else {
            return priority;
        }
    },
    highest: 100000,
    high: 1000,
    normal: 0,
    low: -1000,
    lowest: -100000
};
/* harmony default export */ const src_priorities = (priorities);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/inserttopriorityarray.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * Inserts any object with priority at correct index by priority so registered objects are always sorted from highest to lowest priority.
 *
 * @param {Array.<module:utils/inserttopriorityarray~ObjectWithPriority>} objects Array of objects with priority to insert object to.
 * @param {module:utils/inserttopriorityarray~ObjectWithPriority} objectToInsert Object with `priority` property.
 */
function insertToPriorityArray(objects, objectToInsert) {
    const priority = src_priorities.get(objectToInsert.priority);
    for (let i = 0; i < objects.length; i++) {
        if (src_priorities.get(objects[i].priority) < priority) {
            objects.splice(i, 0, objectToInsert);
            return;
        }
    }
    objects.push(objectToInsert);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/ckeditorerror.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/ckeditorerror
 */
/* globals console */
/**
 * URL to the documentation with error codes.
 */
const DOCUMENTATION_URL = 'https://ckeditor.com/docs/ckeditor5/latest/support/error-codes.html';
/**
 * The CKEditor error class.
 *
 * You should throw `CKEditorError` when:
 *
 * * An unexpected situation occurred and the editor (most probably) will not work properly. Such exception will be handled
 * by the {@link module:watchdog/watchdog~Watchdog watchdog} (if it is integrated),
 * * If the editor is incorrectly integrated or the editor API is used in the wrong way. This way you will give
 * feedback to the developer as soon as possible. Keep in mind that for common integration issues which should not
 * stop editor initialization (like missing upload adapter, wrong name of a toolbar component) we use
 * {@link module:utils/ckeditorerror~logWarning `logWarning()`} and
 * {@link module:utils/ckeditorerror~logError `logError()`}
 * to improve developers experience and let them see the a working editor as soon as possible.
 *
 *		/**
 *		 * Error thrown when a plugin cannot be loaded due to JavaScript errors, lack of plugins with a given name, etc.
 *		 *
 *		 * @error plugin-load
 *		 * @param pluginName The name of the plugin that could not be loaded.
 *		 * @param moduleName The name of the module which tried to load this plugin.
 *		 * /
 *		throw new CKEditorError( 'plugin-load', {
 *			pluginName: 'foo',
 *			moduleName: 'bar'
 *		} );
 *
 * @extends Error
 */
class CKEditorError extends Error {
    /**
     * Creates an instance of the CKEditorError class.
     *
     * @param {String} errorName The error id in an `error-name` format. A link to this error documentation page will be added
     * to the thrown error's `message`.
     * @param {Object|null} context A context of the error by which the {@link module:watchdog/watchdog~Watchdog watchdog}
     * is able to determine which editor crashed. It should be an editor instance or a property connected to it. It can be also
     * a `null` value if the editor should not be restarted in case of the error (e.g. during the editor initialization).
     * The error context should be checked using the `areConnectedThroughProperties( editor, context )` utility
     * to check if the object works as the context.
     * @param {Object} [data] Additional data describing the error. A stringified version of this object
     * will be appended to the error message, so the data are quickly visible in the console. The original
     * data object will also be later available under the {@link #data} property.
     */
    constructor(errorName, context, data) {
        super(getErrorMessage(errorName, data));
        /**
         * @type {String}
         */
        this.name = 'CKEditorError';
        /**
         * A context of the error by which the Watchdog is able to determine which editor crashed.
         *
         * @type {Object|null}
         */
        this.context = context;
        /**
         * The additional error data passed to the constructor. Undefined if none was passed.
         *
         * @type {Object|undefined}
         */
        this.data = data;
    }
    /**
     * Checks if the error is of the `CKEditorError` type.
     * @returns {Boolean}
     */
    is(type) {
        return type === 'CKEditorError';
    }
    /**
     * A utility that ensures that the thrown error is a {@link module:utils/ckeditorerror~CKEditorError} one.
     * It is useful when combined with the {@link module:watchdog/watchdog~Watchdog} feature, which can restart the editor in case
     * of a {@link module:utils/ckeditorerror~CKEditorError} error.
     *
     * @static
     * @param {Error} err The error to rethrow.
     * @param {Object} context An object connected through properties with the editor instance. This context will be used
     * by the watchdog to verify which editor should be restarted.
     */
    static rethrowUnexpectedError(err, context) {
        if (err.is && err.is('CKEditorError')) {
            throw err;
        }
        /**
         * An unexpected error occurred inside the CKEditor 5 codebase. This error will look like the original one
         * to make the debugging easier.
         *
         * This error is only useful when the editor is initialized using the {@link module:watchdog/watchdog~Watchdog} feature.
         * In case of such error (or any {@link module:utils/ckeditorerror~CKEditorError} error) the watchdog should restart the editor.
         *
         * @error unexpected-error
         */
        const error = new CKEditorError(err.message, context);
        // Restore the original stack trace to make the error look like the original one.
        // See https://github.com/ckeditor/ckeditor5/issues/5595 for more details.
        error.stack = err.stack;
        throw error;
    }
}
/**
 * Logs a warning to the console with a properly formatted message and adds a link to the documentation.
 * Use whenever you want to log a warning to the console.
 *
 *		/**
 *		 * There was a problem processing the configuration of the toolbar. The item with the given
 *		 * name does not exist, so it was omitted when rendering the toolbar.
 *		 *
 *		 * @error toolbarview-item-unavailable
 *		 * @param {String} name The name of the component.
 *		 * /
 *		logWarning( 'toolbarview-item-unavailable', { name } );
 *
 * See also {@link module:utils/ckeditorerror~CKEditorError} for an explanation when to throw an error and when to log
 * a warning or an error to the console.
 *
 * @param {String} errorName The error name to be logged.
 * @param {Object} [data] Additional data to be logged.
 */
function logWarning(errorName, data) {
    console.warn(...formatConsoleArguments(errorName, data));
}
/**
 * Logs an error to the console with a properly formatted message and adds a link to the documentation.
 * Use whenever you want to log an error to the console.
 *
 *		/**
 *		 * There was a problem processing the configuration of the toolbar. The item with the given
 *		 * name does not exist, so it was omitted when rendering the toolbar.
 *		 *
 *		 * @error toolbarview-item-unavailable
 *		 * @param {String} name The name of the component.
 *		 * /
 *		 logError( 'toolbarview-item-unavailable', { name } );
 *
 * **Note**: In most cases logging a warning using {@link module:utils/ckeditorerror~logWarning} is enough.
 *
 * See also {@link module:utils/ckeditorerror~CKEditorError} for an explanation when to use each method.
 *
 * @param {String} errorName The error name to be logged.
 * @param {Object} [data] Additional data to be logged.
 */
function logError(errorName, data) {
    console.error(...formatConsoleArguments(errorName, data));
}
// Returns formatted link to documentation message.
//
// @private
// @param {String} errorName
// @returns {string}
function getLinkToDocumentationMessage(errorName) {
    return `\nRead more: ${DOCUMENTATION_URL}#error-${errorName}`;
}
// Returns formatted error message.
//
// @private
// @param {String} errorName
// @param {Object} [data]
// @returns {string}
function getErrorMessage(errorName, data) {
    const processedObjects = new WeakSet();
    const circularReferencesReplacer = (key, value) => {
        if (typeof value === 'object' && value !== null) {
            if (processedObjects.has(value)) {
                return `[object ${value.constructor.name}]`;
            }
            processedObjects.add(value);
        }
        return value;
    };
    const stringifiedData = data ? ` ${JSON.stringify(data, circularReferencesReplacer)}` : '';
    const documentationLink = getLinkToDocumentationMessage(errorName);
    return errorName + stringifiedData + documentationLink;
}
// Returns formatted console error arguments.
//
// @private
// @param {String} errorName
// @param {Object} [data]
// @returns {Array}
function formatConsoleArguments(errorName, data) {
    const documentationMessage = getLinkToDocumentationMessage(errorName);
    return data ? [errorName, data, documentationMessage] : [errorName, documentationMessage];
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/version.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/version
 */
/* globals window, global */

const version = '35.3.2';
/* harmony default export */ const src_version = ((/* unused pure expression or super */ null && (version)));
/* istanbul ignore next */
const windowOrGlobal = typeof window === 'object' ? window : __webpack_require__.g;
/* istanbul ignore next */
if (windowOrGlobal.CKEDITOR_VERSION) {
    /**
     * This error is thrown when due to a mistake in how CKEditor 5 was installed or initialized, some
     * of its modules were duplicated (evaluated and executed twice). Module duplication leads to inevitable runtime
     * errors.
     *
     * There are many situations in which some modules can be loaded twice. In the worst case scenario,
     * you may need to check your project for each of these issues and fix them all.
     *
     * # Trying to add a plugin to an existing build
     *
     * If you import an existing CKEditor 5 build and a plugin like this:
     *
     *		import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
     *		import Highlight from '@ckeditor/ckeditor5-highlight/src/highlight';
     *
     * Then your project loads some CKEditor 5 packages twice. How does it happen?
     *
     * The build package contains a file which is already compiled with webpack. This means
     * that it contains all the necessary code from e.g. `@ckeditor/ckeditor5-engine` and `@ckeditor/ckeditor5-utils`.
     *
     * However, the `Highlight` plugin imports some of the modules from these packages, too. If you ask webpack to
     * build such a project, you will end up with the modules being included (and run) twice &mdash; first, because they are
     * included inside the build package, and second, because they are required by the `Highlight` plugin.
     *
     * Therefore, **you must never add plugins to an existing build** unless your plugin has no dependencies.
     *
     * Adding plugins to a build is done by taking the source version of this build (so, before it was built with webpack)
     * and adding plugins there. In this situation, webpack will know that it only needs to load each plugin once.
     *
     * Read more in the {@glink installation/getting-started/installing-plugins "Installing plugins"} guide.
     *
     * # Confused an editor build with an editor implementation
     *
     * This scenario is very similar to the previous one, but has a different origin.
     *
     * Let's assume that you wanted to use CKEditor 5 from source, as explained in the
     * {@glink installation/advanced/alternative-setups/integrating-from-source "Building from source"} section
     * or in the {@glink framework/guides/quick-start "Quick start"} guide of CKEditor 5 Framework.
     *
     * The correct way to do so is to import an editor and plugins and run them together like this:
     *
     *		import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
     *		import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
     *		import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
     *		import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
     *		import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
     *
     *		ClassicEditor
     *			.create( document.querySelector( '#editor' ), {
     *				plugins: [ Essentials, Paragraph, Bold, Italic ],
     *				toolbar: [ 'bold', 'italic' ]
     *			} )
     *			.then( editor => {
     *				console.log( 'Editor was initialized', editor );
     *			} )
     *			.catch( error => {
     *				console.error( error.stack );
     *			} );
     *
     * However, you might have mistakenly imported a build instead of the source `ClassicEditor`. In this case
     * your imports will look like this:
     *
     *		import ClassicEditor from '@ckeditor/ckeditor5-build-classic';
     *		import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
     *		import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
     *		import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
     *		import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
     *
     * This creates the same situation as in the previous section because you use a build together with source plugins.
     *
     * Remember: `@ckeditor/ckeditor5-build-*` packages contain editor builds and `@ckeditor/ckeditor5-editor-*` contain source editors.
     *
     * # Loading two or more builds on one page
     *
     * If you use CKEditor 5 builds, you might have loaded two (or more) `ckeditor.js` files on one web page.
     * Check your web page for duplicated `<script>` elements or make sure your page builder/bundler includes CKEditor only once.
     *
     * If you want to use two different types of editors at once, see the
     * {@glink installation/advanced/using-two-editors "Using two different editors"}
     * section.
     *
     * # Using outdated packages
     *
     * Building CKEditor 5 from source requires using multiple npm packages. These packages have their dependencies
     * to other packages. If you use the latest version of, for example, `@ckeditor/ckeditor5-editor-classic` with
     * an outdated version of `@ckeditor/ckeditor5-image`, npm or yarn will need to install two different versions of
     * `@ckeditor/ckeditor5-core` because `@ckeditor/ckeditor5-editor-classic` and `@ckeditor/ckeditor5-image` may require
     * different versions of the core package.
     *
     * The solution to this issue is to update all packages to their latest version. We recommend
     * using tools like [`npm-check-updates`](https://www.npmjs.com/package/npm-check-updates) which simplify this process.
     *
     * # Conflicting version of dependencies
     *
     * This is a special case of the previous scenario. If you use CKEditor 5 with some third-party plugins,
     * it may happen that even if you use the latest versions of the official packages and the latest version of
     * these third-party packages, there will be a conflict between some of their dependencies.
     *
     * Such a problem can be resolved by either downgrading CKEditor 5 packages (which we do not recommend) or
     * asking the author of the third-party package to upgrade its depdendencies (or forking their project and doing this yourself).
     *
     * **Note:** All official CKEditor 5 packages (excluding integrations and `ckeditor5-dev-*` packages) are released in the
     * same major version. This is &mdash; in the `x.y.z`, the `x` is the same for all packages. This is the simplest way to check
     * whether you use packages coming from the same CKEditor 5 version. You can read more about versioning in the
     * {@glink support/versioning-policy Versioning policy} guide.
     *
     * # Packages were duplicated in `node_modules`
     *
     * In some situations, especially when calling `npm install` multiple times, it may happen
     * that npm will not correctly "deduplicate" packages.
     *
     * Normally, npm deduplicates all packages so, for example, `@ckeditor/ckeditor5-core` is installed only once in `node_modules/`.
     * However, it is known to fail to do so from time to time.
     *
     * We recommend checking if any of the steps listed below help:
     *
     * * `rm -rf node_modules && npm install` to make sure you have a clean `node_modules/` directory. This step
     * is known to help in most cases.
     * * If you use `yarn.lock` or `package-lock.json`, remove it before `npm install`.
     * * Check whether all CKEditor 5 packages are up to date and reinstall them
     * if you changed anything (`rm -rf node_modules && npm install`).
     *
     * If all packages are correct and compatible with each other, the steps above are known to help. If not, you may
     * try to check with `npm ls` how many times packages like `@ckeditor/ckeditor5-core`, `@ckeditor/ckeditor5-engine` and
     *`@ckeditor/ckeditor5-utils` are installed. If more than once, verify which package causes that.
     *
     * @error ckeditor-duplicated-modules
     */
    throw new CKEditorError('ckeditor-duplicated-modules', null);
}
else {
    windowOrGlobal.CKEDITOR_VERSION = version;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/emittermixin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/emittermixin
 */
/* eslint-disable new-cap */




// To check if component is loaded more than once.


const _listeningTo = Symbol('listeningTo');
const _emitterId = Symbol('emitterId');
const _delegations = Symbol('delegations');
/**
 * Mixin that injects the {@link ~Emitter events API} into its host.
 *
 * Read more about the concept of emitters in the:
 * * {@glink framework/guides/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
 * section of the {@glink framework/guides/architecture/core-editor-architecture Core editor architecture} guide.
 * * {@glink framework/guides/deep-dive/event-system Event system} deep dive guide.
 *
 * @mixin EmitterMixin
 * @implements module:utils/emittermixin~Emitter
 */
function EmitterMixin(base) {
    class Mixin extends base {
        on(event, callback, options) {
            this.listenTo(this, event, callback, options);
        }
        once(event, callback, options) {
            let wasFired = false;
            const onceCallback = (event, ...args) => {
                // Ensure the callback is called only once even if the callback itself leads to re-firing the event
                // (which would call the callback again).
                if (!wasFired) {
                    wasFired = true;
                    // Go off() at the first call.
                    event.off();
                    // Go with the original callback.
                    callback.call(this, event, ...args);
                }
            };
            // Make a similar on() call, simply replacing the callback.
            this.listenTo(this, event, onceCallback, options);
        }
        off(event, callback) {
            this.stopListening(this, event, callback);
        }
        listenTo(emitter, event, callback, options = {}) {
            let emitterInfo, eventCallbacks;
            // _listeningTo contains a list of emitters that this object is listening to.
            // This list has the following format:
            //
            // _listeningTo: {
            //     emitterId: {
            //         emitter: emitter,
            //         callbacks: {
            //             event1: [ callback1, callback2, ... ]
            //             ....
            //         }
            //     },
            //     ...
            // }
            if (!this[_listeningTo]) {
                this[_listeningTo] = {};
            }
            const emitters = this[_listeningTo];
            if (!_getEmitterId(emitter)) {
                _setEmitterId(emitter);
            }
            const emitterId = _getEmitterId(emitter);
            if (!(emitterInfo = emitters[emitterId])) {
                emitterInfo = emitters[emitterId] = {
                    emitter,
                    callbacks: {}
                };
            }
            if (!(eventCallbacks = emitterInfo.callbacks[event])) {
                eventCallbacks = emitterInfo.callbacks[event] = [];
            }
            eventCallbacks.push(callback);
            // Finally register the callback to the event.
            addEventListener(this, emitter, event, callback, options);
        }
        stopListening(emitter, event, callback) {
            const emitters = this[_listeningTo];
            let emitterId = emitter && _getEmitterId(emitter);
            const emitterInfo = (emitters && emitterId) ? emitters[emitterId] : undefined;
            const eventCallbacks = (emitterInfo && event) ? emitterInfo.callbacks[event] : undefined;
            // Stop if nothing has been listened.
            if (!emitters || (emitter && !emitterInfo) || (event && !eventCallbacks)) {
                return;
            }
            // All params provided. off() that single callback.
            if (callback) {
                removeEventListener(this, emitter, event, callback);
                // We must remove callbacks as well in order to prevent memory leaks.
                // See https://github.com/ckeditor/ckeditor5/pull/8480
                const index = eventCallbacks.indexOf(callback);
                if (index !== -1) {
                    if (eventCallbacks.length === 1) {
                        delete emitterInfo.callbacks[event];
                    }
                    else {
                        removeEventListener(this, emitter, event, callback);
                    }
                }
            }
            // Only `emitter` and `event` provided. off() all callbacks for that event.
            else if (eventCallbacks) {
                while ((callback = eventCallbacks.pop())) {
                    removeEventListener(this, emitter, event, callback);
                }
                delete emitterInfo.callbacks[event];
            }
            // Only `emitter` provided. off() all events for that emitter.
            else if (emitterInfo) {
                for (event in emitterInfo.callbacks) {
                    this.stopListening(emitter, event);
                }
                delete emitters[emitterId];
            }
            // No params provided. off() all emitters.
            else {
                for (emitterId in emitters) {
                    this.stopListening(emitters[emitterId].emitter);
                }
                delete this[_listeningTo];
            }
        }
        fire(eventOrInfo, ...args) {
            try {
                const eventInfo = eventOrInfo instanceof EventInfo ? eventOrInfo : new EventInfo(this, eventOrInfo);
                const event = eventInfo.name;
                let callbacks = getCallbacksForEvent(this, event);
                // Record that the event passed this emitter on its path.
                eventInfo.path.push(this);
                // Handle event listener callbacks first.
                if (callbacks) {
                    // Arguments passed to each callback.
                    const callbackArgs = [eventInfo, ...args];
                    // Copying callbacks array is the easiest and most secure way of preventing infinite loops, when event callbacks
                    // are added while processing other callbacks. Previous solution involved adding counters (unique ids) but
                    // failed if callbacks were added to the queue before currently processed callback.
                    // If this proves to be too inefficient, another method is to change `.on()` so callbacks are stored if same
                    // event is currently processed. Then, `.fire()` at the end, would have to add all stored events.
                    callbacks = Array.from(callbacks);
                    for (let i = 0; i < callbacks.length; i++) {
                        callbacks[i].callback.apply(this, callbackArgs);
                        // Remove the callback from future requests if off() has been called.
                        if (eventInfo.off.called) {
                            // Remove the called mark for the next calls.
                            delete eventInfo.off.called;
                            this._removeEventListener(event, callbacks[i].callback);
                        }
                        // Do not execute next callbacks if stop() was called.
                        if (eventInfo.stop.called) {
                            break;
                        }
                    }
                }
                // Delegate event to other emitters if needed.
                const delegations = this[_delegations];
                if (delegations) {
                    const destinations = delegations.get(event);
                    const passAllDestinations = delegations.get('*');
                    if (destinations) {
                        fireDelegatedEvents(destinations, eventInfo, args);
                    }
                    if (passAllDestinations) {
                        fireDelegatedEvents(passAllDestinations, eventInfo, args);
                    }
                }
                return eventInfo.return;
            }
            catch (err) {
                // @if CK_DEBUG // throw err;
                /* istanbul ignore next */
                CKEditorError.rethrowUnexpectedError(err, this);
            }
        }
        delegate(...events) {
            return {
                to: (emitter, nameOrFunction) => {
                    if (!this[_delegations]) {
                        this[_delegations] = new Map();
                    }
                    // Originally there was a for..of loop which unfortunately caused an error in Babel that didn't allow
                    // build an application. See: https://github.com/ckeditor/ckeditor5-react/issues/40.
                    events.forEach(eventName => {
                        const destinations = this[_delegations].get(eventName);
                        if (!destinations) {
                            this[_delegations].set(eventName, new Map([[emitter, nameOrFunction]]));
                        }
                        else {
                            destinations.set(emitter, nameOrFunction);
                        }
                    });
                }
            };
        }
        stopDelegating(event, emitter) {
            if (!this[_delegations]) {
                return;
            }
            if (!event) {
                this[_delegations].clear();
            }
            else if (!emitter) {
                this[_delegations].delete(event);
            }
            else {
                const destinations = this[_delegations].get(event);
                if (destinations) {
                    destinations.delete(emitter);
                }
            }
        }
        _addEventListener(event, callback, options) {
            createEventNamespace(this, event);
            const lists = getCallbacksListsForNamespace(this, event);
            const priority = src_priorities.get(options.priority);
            const callbackDefinition = {
                callback,
                priority
            };
            // Add the callback to all callbacks list.
            for (const callbacks of lists) {
                // Add the callback to the list in the right priority position.
                insertToPriorityArray(callbacks, callbackDefinition);
            }
        }
        _removeEventListener(event, callback) {
            const lists = getCallbacksListsForNamespace(this, event);
            for (const callbacks of lists) {
                for (let i = 0; i < callbacks.length; i++) {
                    if (callbacks[i].callback == callback) {
                        // Remove the callback from the list (fixing the next index).
                        callbacks.splice(i, 1);
                        i--;
                    }
                }
            }
        }
    }
    return Mixin;
}
const Emitter = EmitterMixin(Object);
// Backward compatibility with `mix`
([
    'on', 'once', 'off', 'listenTo',
    'stopListening', 'fire', 'delegate', 'stopDelegating',
    '_addEventListener', '_removeEventListener'
]).forEach(key => {
    EmitterMixin[key] = Emitter.prototype[key];
});
/**
 * Checks if `listeningEmitter` listens to an emitter with given `listenedToEmitterId` and if so, returns that emitter.
 * If not, returns `null`.
 *
 * @internal
 * @protected
 * @param {module:utils/emittermixin~Emitter} listeningEmitter An emitter that listens.
 * @param {String} listenedToEmitterId Unique emitter id of emitter listened to.
 * @returns {module:utils/emittermixin~Emitter|null}
 */
function _getEmitterListenedTo(listeningEmitter, listenedToEmitterId) {
    const listeningTo = listeningEmitter[_listeningTo];
    if (listeningTo && listeningTo[listenedToEmitterId]) {
        return listeningTo[listenedToEmitterId].emitter;
    }
    return null;
}
/**
 * Sets emitter's unique id.
 *
 * **Note:** `_emitterId` can be set only once.
 *
 * @internal
 * @protected
 * @param {module:utils/emittermixin~Emitter} emitter An emitter for which id will be set.
 * @param {String} [id] Unique id to set. If not passed, random unique id will be set.
 */
function _setEmitterId(emitter, id) {
    if (!emitter[_emitterId]) {
        emitter[_emitterId] = id || uid();
    }
}
/**
 * Returns emitter's unique id.
 *
 * @internal
 * @protected
 * @param {module:utils/emittermixin~Emitter} emitter An emitter which id will be returned.
 * @returns {String|undefined}
 */
function _getEmitterId(emitter) {
    return emitter[_emitterId];
}
// Gets the internal `_events` property of the given object.
// `_events` property store all lists with callbacks for registered event names.
// If there were no events registered on the object, empty `_events` object is created.
function getEvents(source) {
    if (!source._events) {
        Object.defineProperty(source, '_events', {
            value: {}
        });
    }
    return source._events;
}
// Creates event node for generic-specific events relation architecture.
function makeEventNode() {
    return {
        callbacks: [],
        childEvents: []
    };
}
// Creates an architecture for generic-specific events relation.
// If needed, creates all events for given eventName, i.e. if the first registered event
// is foo:bar:abc, it will create foo:bar:abc, foo:bar and foo event and tie them together.
// It also copies callbacks from more generic events to more specific events when
// specific events are created.
function createEventNamespace(source, eventName) {
    const events = getEvents(source);
    // First, check if the event we want to add to the structure already exists.
    if (events[eventName]) {
        // If it exists, we don't have to do anything.
        return;
    }
    // In other case, we have to create the structure for the event.
    // Note, that we might need to create intermediate events too.
    // I.e. if foo:bar:abc is being registered and we only have foo in the structure,
    // we need to also register foo:bar.
    // Currently processed event name.
    let name = eventName;
    // Name of the event that is a child event for currently processed event.
    let childEventName = null;
    // Array containing all newly created specific events.
    const newEventNodes = [];
    // While loop can't check for ':' index because we have to handle generic events too.
    // In each loop, we truncate event name, going from the most specific name to the generic one.
    // I.e. foo:bar:abc -> foo:bar -> foo.
    while (name !== '') {
        if (events[name]) {
            // If the currently processed event name is already registered, we can be sure
            // that it already has all the structure created, so we can break the loop here
            // as no more events need to be registered.
            break;
        }
        // If this event is not yet registered, create a new object for it.
        events[name] = makeEventNode();
        // Add it to the array with newly created events.
        newEventNodes.push(events[name]);
        // Add previously processed event name as a child of this event.
        if (childEventName) {
            events[name].childEvents.push(childEventName);
        }
        childEventName = name;
        // If `.lastIndexOf()` returns -1, `.substr()` will return '' which will break the loop.
        name = name.substr(0, name.lastIndexOf(':'));
    }
    if (name !== '') {
        // If name is not empty, we found an already registered event that was a parent of the
        // event we wanted to register.
        // Copy that event's callbacks to newly registered events.
        for (const node of newEventNodes) {
            node.callbacks = events[name].callbacks.slice();
        }
        // Add last newly created event to the already registered event.
        events[name].childEvents.push(childEventName);
    }
}
// Gets an array containing callbacks list for a given event and it's more specific events.
// I.e. if given event is foo:bar and there is also foo:bar:abc event registered, this will
// return callback list of foo:bar and foo:bar:abc (but not foo).
function getCallbacksListsForNamespace(source, eventName) {
    const eventNode = getEvents(source)[eventName];
    if (!eventNode) {
        return [];
    }
    let callbacksLists = [eventNode.callbacks];
    for (let i = 0; i < eventNode.childEvents.length; i++) {
        const childCallbacksLists = getCallbacksListsForNamespace(source, eventNode.childEvents[i]);
        callbacksLists = callbacksLists.concat(childCallbacksLists);
    }
    return callbacksLists;
}
// Get the list of callbacks for a given event, but only if there any callbacks have been registered.
// If there are no callbacks registered for given event, it checks if this is a specific event and looks
// for callbacks for it's more generic version.
function getCallbacksForEvent(source, eventName) {
    let event;
    if (!source._events || !(event = source._events[eventName]) || !event.callbacks.length) {
        // There are no callbacks registered for specified eventName.
        // But this could be a specific-type event that is in a namespace.
        if (eventName.indexOf(':') > -1) {
            // If the eventName is specific, try to find callback lists for more generic event.
            return getCallbacksForEvent(source, eventName.substr(0, eventName.lastIndexOf(':')));
        }
        else {
            // If this is a top-level generic event, return null;
            return null;
        }
    }
    return event.callbacks;
}
// Fires delegated events for given map of destinations.
//
// @private
// * @param {Map.<utils.Emitter>} destinations A map containing
// `[ {@link module:utils/emittermixin~Emitter}, "event name" ]` pair destinations.
// * @param {utils.EventInfo} eventInfo The original event info object.
// * @param {Array.<*>} fireArgs Arguments the original event was fired with.
function fireDelegatedEvents(destinations, eventInfo, fireArgs) {
    for (let [emitter, name] of destinations) {
        if (!name) {
            name = eventInfo.name;
        }
        else if (typeof name == 'function') {
            name = name(eventInfo.name);
        }
        const delegatedInfo = new EventInfo(eventInfo.source, name);
        delegatedInfo.path = [...eventInfo.path];
        emitter.fire(delegatedInfo, ...fireArgs);
    }
}
// Helper for registering event callback on the emitter.
function addEventListener(listener, emitter, event, callback, options) {
    if (emitter._addEventListener) {
        emitter._addEventListener(event, callback, options);
    }
    else {
        // Allow listening on objects that do not implement Emitter interface.
        // This is needed in some tests that are using mocks instead of the real objects with EmitterMixin mixed.
        (listener._addEventListener).call(emitter, event, callback, options);
    }
}
// Helper for removing event callback from the emitter.
function removeEventListener(listener, emitter, event, callback) {
    if (emitter._removeEventListener) {
        emitter._removeEventListener(event, callback);
    }
    else {
        // Allow listening on objects that do not implement Emitter interface.
        // This is needed in some tests that are using mocks instead of the real objects with EmitterMixin mixed.
        listener._removeEventListener.call(emitter, event, callback);
    }
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/isObject.js
/**
 * Checks if `value` is the
 * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
 * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an object, else `false`.
 * @example
 *
 * _.isObject({});
 * // => true
 *
 * _.isObject([1, 2, 3]);
 * // => true
 *
 * _.isObject(_.noop);
 * // => true
 *
 * _.isObject(null);
 * // => false
 */
function isObject(value) {
  var type = typeof value;
  return value != null && (type == 'object' || type == 'function');
}

/* harmony default export */ const lodash_es_isObject = (isObject);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/observablemixin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable @typescript-eslint/unified-signatures, new-cap */
/**
 * @module utils/observablemixin
 */



const observablePropertiesSymbol = Symbol('observableProperties');
const boundObservablesSymbol = Symbol('boundObservables');
const boundPropertiesSymbol = Symbol('boundProperties');
const decoratedMethods = Symbol('decoratedMethods');
const decoratedOriginal = Symbol('decoratedOriginal');
/**
 * A mixin that injects the "observable properties" and data binding functionality described in the
 * {@link ~Observable} interface.
 *
 * Read more about the concept of observables in the:
 * * {@glink framework/guides/architecture/core-editor-architecture#event-system-and-observables Event system and observables}
 * section of the {@glink framework/guides/architecture/core-editor-architecture Core editor architecture} guide,
 * * {@glink framework/guides/deep-dive/observables Observables deep dive} guide.
 *
 * @mixin ObservableMixin
 * @mixes module:utils/emittermixin~EmitterMixin
 * @implements module:utils/observablemixin~Observable
 */
function ObservableMixin(base) {
    class Mixin extends base {
        set(name, value) {
            // If the first parameter is an Object, iterate over its properties.
            if (lodash_es_isObject(name)) {
                Object.keys(name).forEach(property => {
                    this.set(property, name[property]);
                }, this);
                return;
            }
            initObservable(this);
            const properties = this[observablePropertiesSymbol];
            if ((name in this) && !properties.has(name)) {
                /**
                 * Cannot override an existing property.
                 *
                 * This error is thrown when trying to {@link ~Observable#set set} a property with
                 * a name of an already existing property. For example:
                 *
                 *		let observable = new Model();
                 *		observable.property = 1;
                 *		observable.set( 'property', 2 );			// throws
                 *
                 *		observable.set( 'property', 1 );
                 *		observable.set( 'property', 2 );			// ok, because this is an existing property.
                 *
                 * @error observable-set-cannot-override
                 */
                throw new CKEditorError('observable-set-cannot-override', this);
            }
            Object.defineProperty(this, name, {
                enumerable: true,
                configurable: true,
                get() {
                    return properties.get(name);
                },
                set(value) {
                    const oldValue = properties.get(name);
                    // Fire `set` event before the new value will be set to make it possible
                    // to override observable property without affecting `change` event.
                    // See https://github.com/ckeditor/ckeditor5-utils/issues/171.
                    let newValue = this.fire(`set:${name}`, name, value, oldValue);
                    if (newValue === undefined) {
                        newValue = value;
                    }
                    // Allow undefined as an initial value like A.define( 'x', undefined ) (#132).
                    // Note: When properties map has no such own property, then its value is undefined.
                    if (oldValue !== newValue || !properties.has(name)) {
                        properties.set(name, newValue);
                        this.fire(`change:${name}`, name, newValue, oldValue);
                    }
                }
            });
            this[name] = value;
        }
        bind(...bindProperties) {
            if (!bindProperties.length || !isStringArray(bindProperties)) {
                /**
                 * All properties must be strings.
                 *
                 * @error observable-bind-wrong-properties
                 */
                throw new CKEditorError('observable-bind-wrong-properties', this);
            }
            if ((new Set(bindProperties)).size !== bindProperties.length) {
                /**
                 * Properties must be unique.
                 *
                 * @error observable-bind-duplicate-properties
                 */
                throw new CKEditorError('observable-bind-duplicate-properties', this);
            }
            initObservable(this);
            const boundProperties = this[boundPropertiesSymbol];
            bindProperties.forEach(propertyName => {
                if (boundProperties.has(propertyName)) {
                    /**
                     * Cannot bind the same property more than once.
                     *
                     * @error observable-bind-rebind
                     */
                    throw new CKEditorError('observable-bind-rebind', this);
                }
            });
            const bindings = new Map();
            // @typedef {Object} Binding
            // @property {Array} property Property which is bound.
            // @property {Array} to Array of observable–property components of the binding (`{ observable: ..., property: .. }`).
            // @property {Array} callback A function which processes `to` components.
            bindProperties.forEach(a => {
                const binding = { property: a, to: [] };
                boundProperties.set(a, binding);
                bindings.set(a, binding);
            });
            // @typedef {Object} BindChain
            // @property {Function} to See {@link ~ObservableMixin#_bindTo}.
            // @property {Function} toMany See {@link ~ObservableMixin#_bindToMany}.
            // @property {module:utils/observablemixin~Observable} _observable The observable which initializes the binding.
            // @property {Array} _bindProperties Array of `_observable` properties to be bound.
            // @property {Array} _to Array of `to()` observable–properties (`{ observable: toObservable, properties: ...toProperties }`).
            // @property {Map} _bindings Stores bindings to be kept in
            // {@link ~ObservableMixin#_boundProperties}/{@link ~ObservableMixin#_boundObservables}
            // initiated in this binding chain.
            return {
                to: bindTo,
                toMany: bindToMany,
                _observable: this,
                _bindProperties: bindProperties,
                _to: [],
                _bindings: bindings
            };
        }
        unbind(...unbindProperties) {
            // Nothing to do here if not inited yet.
            if (!(this[observablePropertiesSymbol])) {
                return;
            }
            const boundProperties = this[boundPropertiesSymbol];
            const boundObservables = this[boundObservablesSymbol];
            if (unbindProperties.length) {
                if (!isStringArray(unbindProperties)) {
                    /**
                     * Properties must be strings.
                     *
                     * @error observable-unbind-wrong-properties
                     */
                    throw new CKEditorError('observable-unbind-wrong-properties', this);
                }
                unbindProperties.forEach(propertyName => {
                    const binding = boundProperties.get(propertyName);
                    // Nothing to do if the binding is not defined
                    if (!binding) {
                        return;
                    }
                    binding.to.forEach(([toObservable, toProperty]) => {
                        const toProperties = boundObservables.get(toObservable);
                        const toPropertyBindings = toProperties[toProperty];
                        toPropertyBindings.delete(binding);
                        if (!toPropertyBindings.size) {
                            delete toProperties[toProperty];
                        }
                        if (!Object.keys(toProperties).length) {
                            boundObservables.delete(toObservable);
                            this.stopListening(toObservable, 'change');
                        }
                    });
                    boundProperties.delete(propertyName);
                });
            }
            else {
                boundObservables.forEach((bindings, boundObservable) => {
                    this.stopListening(boundObservable, 'change');
                });
                boundObservables.clear();
                boundProperties.clear();
            }
        }
        decorate(methodName) {
            initObservable(this);
            const originalMethod = this[methodName];
            if (!originalMethod) {
                /**
                 * Cannot decorate an undefined method.
                 *
                 * @error observablemixin-cannot-decorate-undefined
                 * @param {Object} object The object which method should be decorated.
                 * @param {String} methodName Name of the method which does not exist.
                 */
                throw new CKEditorError('observablemixin-cannot-decorate-undefined', this, { object: this, methodName });
            }
            this.on(methodName, (evt, args) => {
                evt.return = originalMethod.apply(this, args);
            });
            this[methodName] = function (...args) {
                return this.fire(methodName, args);
            };
            this[methodName][decoratedOriginal] = originalMethod;
            if (!this[decoratedMethods]) {
                this[decoratedMethods] = [];
            }
            this[decoratedMethods].push(methodName);
        }
        // Override the EmitterMixin stopListening method to be able to clean (and restore) decorated methods.
        // This is needed in case of:
        //  1. Have x.foo() decorated.
        //  2. Call x.stopListening()
        //  3. Call x.foo(). Problem: nothing happens (the original foo() method is not executed)
        stopListening(emitter, event, callback) {
            // Removing all listeners so let's clean the decorated methods to the original state.
            if (!emitter && this[decoratedMethods]) {
                for (const methodName of this[decoratedMethods]) {
                    this[methodName] = this[methodName][decoratedOriginal];
                }
                delete this[decoratedMethods];
            }
            Emitter.prototype.stopListening.call(this, emitter, event, callback);
        }
    }
    return Mixin;
}
const Observable = ObservableMixin(Emitter);
// Backward compatibility with `mix`
([
    'set', 'bind', 'unbind', 'decorate',
    'on', 'once', 'off', 'listenTo',
    'stopListening', 'fire', 'delegate', 'stopDelegating',
    '_addEventListener', '_removeEventListener'
]).forEach(key => {
    ObservableMixin[key] = Observable.prototype[key];
});
// Init symbol properties needed for the observable mechanism to work.
//
// @private
// @param {module:utils/observablemixin~ObservableMixin} observable
function initObservable(observable) {
    // Do nothing if already inited.
    if (observable[observablePropertiesSymbol]) {
        return;
    }
    // The internal hash containing the observable's state.
    //
    // @private
    // @type {Map}
    Object.defineProperty(observable, observablePropertiesSymbol, {
        value: new Map()
    });
    // Map containing bindings to external observables. It shares the binding objects
    // (`{ observable: A, property: 'a', to: ... }`) with {@link module:utils/observablemixin~ObservableMixin#_boundProperties} and
    // it is used to observe external observables to update own properties accordingly.
    // See {@link module:utils/observablemixin~ObservableMixin#bind}.
    //
    //		A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
    //		console.log( A._boundObservables );
    //
    //			Map( {
    //				B: {
    //					x: Set( [
    //						{ observable: A, property: 'a', to: [ [ B, 'x' ] ] },
    //						{ observable: A, property: 'c', to: [ [ B, 'x' ] ] }
    //					] ),
    //					y: Set( [
    //						{ observable: A, property: 'b', to: [ [ B, 'y' ] ] },
    //					] )
    //				}
    //			} )
    //
    //		A.bind( 'd' ).to( B, 'z' ).to( C, 'w' ).as( callback );
    //		console.log( A._boundObservables );
    //
    //			Map( {
    //				B: {
    //					x: Set( [
    //						{ observable: A, property: 'a', to: [ [ B, 'x' ] ] },
    //						{ observable: A, property: 'c', to: [ [ B, 'x' ] ] }
    //					] ),
    //					y: Set( [
    //						{ observable: A, property: 'b', to: [ [ B, 'y' ] ] },
    //					] ),
    //					z: Set( [
    //						{ observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
    //					] )
    //				},
    //				C: {
    //					w: Set( [
    //						{ observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
    //					] )
    //				}
    //			} )
    //
    // @private
    // @type {Map}
    Object.defineProperty(observable, boundObservablesSymbol, {
        value: new Map()
    });
    // Object that stores which properties of this observable are bound and how. It shares
    // the binding objects (`{ observable: A, property: 'a', to: ... }`) with
    // {@link module:utils/observablemixin~ObservableMixin#_boundObservables}. This data structure is
    // a reverse of {@link module:utils/observablemixin~ObservableMixin#_boundObservables} and it is helpful for
    // {@link module:utils/observablemixin~ObservableMixin#unbind}.
    //
    // See {@link module:utils/observablemixin~ObservableMixin#bind}.
    //
    //		A.bind( 'a', 'b', 'c' ).to( B, 'x', 'y', 'x' );
    //		console.log( A._boundProperties );
    //
    //			Map( {
    //				a: { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
    //				b: { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
    //				c: { observable: A, property: 'c', to: [ [ B, 'x' ] ] }
    //			} )
    //
    //		A.bind( 'd' ).to( B, 'z' ).to( C, 'w' ).as( callback );
    //		console.log( A._boundProperties );
    //
    //			Map( {
    //				a: { observable: A, property: 'a', to: [ [ B, 'x' ] ] },
    //				b: { observable: A, property: 'b', to: [ [ B, 'y' ] ] },
    //				c: { observable: A, property: 'c', to: [ [ B, 'x' ] ] },
    //				d: { observable: A, property: 'd', to: [ [ B, 'z' ], [ C, 'w' ] ], callback: callback }
    //			} )
    //
    // @private
    // @type {Map}
    Object.defineProperty(observable, boundPropertiesSymbol, {
        value: new Map()
    });
}
// A chaining for {@link module:utils/observablemixin~ObservableMixin#bind} providing `.to()` interface.
//
// @private
// @param {...[Observable|String|Function]} args Arguments of the `.to( args )` binding.
function bindTo(...args) {
    const parsedArgs = parseBindToArgs(...args);
    const bindingsKeys = Array.from(this._bindings.keys());
    const numberOfBindings = bindingsKeys.length;
    // Eliminate A.bind( 'x' ).to( B, C )
    if (!parsedArgs.callback && parsedArgs.to.length > 1) {
        /**
         * Binding multiple observables only possible with callback.
         *
         * @error observable-bind-to-no-callback
         */
        throw new CKEditorError('observable-bind-to-no-callback', this);
    }
    // Eliminate A.bind( 'x', 'y' ).to( B, callback )
    if (numberOfBindings > 1 && parsedArgs.callback) {
        /**
         * Cannot bind multiple properties and use a callback in one binding.
         *
         * @error observable-bind-to-extra-callback
         */
        throw new CKEditorError('observable-bind-to-extra-callback', this);
    }
    parsedArgs.to.forEach(to => {
        // Eliminate A.bind( 'x', 'y' ).to( B, 'a' )
        if (to.properties.length && to.properties.length !== numberOfBindings) {
            /**
             * The number of properties must match.
             *
             * @error observable-bind-to-properties-length
             */
            throw new CKEditorError('observable-bind-to-properties-length', this);
        }
        // When no to.properties specified, observing source properties instead i.e.
        // A.bind( 'x', 'y' ).to( B ) -> Observe B.x and B.y
        if (!to.properties.length) {
            to.properties = this._bindProperties;
        }
    });
    this._to = parsedArgs.to;
    // Fill {@link BindChain#_bindings} with callback. When the callback is set there's only one binding.
    if (parsedArgs.callback) {
        this._bindings.get(bindingsKeys[0]).callback = parsedArgs.callback;
    }
    attachBindToListeners(this._observable, this._to);
    // Update observable._boundProperties and observable._boundObservables.
    updateBindToBound(this);
    // Set initial values of bound properties.
    this._bindProperties.forEach(propertyName => {
        updateBoundObservableProperty(this._observable, propertyName);
    });
}
// Binds to an attribute in a set of iterable observables.
//
// @private
// @param {Array.<Observable>} observables
// @param {String} attribute
// @param {Function} callback
function bindToMany(observables, attribute, callback) {
    if (this._bindings.size > 1) {
        /**
         * Binding one attribute to many observables only possible with one attribute.
         *
         * @error observable-bind-to-many-not-one-binding
         */
        throw new CKEditorError('observable-bind-to-many-not-one-binding', this);
    }
    this.to(
    // Bind to #attribute of each observable...
    ...getBindingTargets(observables, attribute), 
    // ...using given callback to parse attribute values.
    callback);
}
// Returns an array of binding components for
// {@link Observable#bind} from a set of iterable observables.
//
// @param {Array.<Observable>} observables
// @param {String} attribute
// @returns {Array.<String|Observable>}
function getBindingTargets(observables, attribute) {
    const observableAndAttributePairs = observables.map(observable => [observable, attribute]);
    // Merge pairs to one-dimension array of observables and attributes.
    return Array.prototype.concat.apply([], observableAndAttributePairs);
}
// Check if all entries of the array are of `String` type.
//
// @private
// @param {Array} arr An array to be checked.
// @returns {Boolean}
function isStringArray(arr) {
    return arr.every(a => typeof a == 'string');
}
// Parses and validates {@link Observable#bind}`.to( args )` arguments and returns
// an object with a parsed structure. For example
//
//		A.bind( 'x' ).to( B, 'a', C, 'b', call );
//
// becomes
//
//		{
//			to: [
//				{ observable: B, properties: [ 'a' ] },
//				{ observable: C, properties: [ 'b' ] },
//			],
//			callback: call
// 		}
//
// @private
// @param {...*} args Arguments of {@link Observable#bind}`.to( args )`.
// @returns {Object}
function parseBindToArgs(...args) {
    // Eliminate A.bind( 'x' ).to()
    if (!args.length) {
        /**
         * Invalid argument syntax in `to()`.
         *
         * @error observable-bind-to-parse-error
         */
        throw new CKEditorError('observable-bind-to-parse-error', null);
    }
    const parsed = { to: [] };
    let lastObservable;
    if (typeof args[args.length - 1] == 'function') {
        parsed.callback = args.pop();
    }
    args.forEach(a => {
        if (typeof a == 'string') {
            lastObservable.properties.push(a);
        }
        else if (typeof a == 'object') {
            lastObservable = { observable: a, properties: [] };
            parsed.to.push(lastObservable);
        }
        else {
            throw new CKEditorError('observable-bind-to-parse-error', null);
        }
    });
    return parsed;
}
// Synchronizes {@link module:utils/observablemixin#_boundObservables} with {@link Binding}.
//
// @private
// @param {Binding} binding A binding to store in {@link Observable#_boundObservables}.
// @param {Observable} toObservable A observable, which is a new component of `binding`.
// @param {String} toPropertyName A name of `toObservable`'s property, a new component of the `binding`.
function updateBoundObservables(observable, binding, toObservable, toPropertyName) {
    const boundObservables = observable[boundObservablesSymbol];
    const bindingsToObservable = boundObservables.get(toObservable);
    const bindings = bindingsToObservable || {};
    if (!bindings[toPropertyName]) {
        bindings[toPropertyName] = new Set();
    }
    // Pass the binding to a corresponding Set in `observable._boundObservables`.
    bindings[toPropertyName].add(binding);
    if (!bindingsToObservable) {
        boundObservables.set(toObservable, bindings);
    }
}
// Synchronizes {@link Observable#_boundProperties} and {@link Observable#_boundObservables}
// with {@link BindChain}.
//
// Assuming the following binding being created
//
// 		A.bind( 'a', 'b' ).to( B, 'x', 'y' );
//
// the following bindings were initialized by {@link Observable#bind} in {@link BindChain#_bindings}:
//
// 		{
// 			a: { observable: A, property: 'a', to: [] },
// 			b: { observable: A, property: 'b', to: [] },
// 		}
//
// Iterate over all bindings in this chain and fill their `to` properties with
// corresponding to( ... ) arguments (components of the binding), so
//
// 		{
// 			a: { observable: A, property: 'a', to: [ B, 'x' ] },
// 			b: { observable: A, property: 'b', to: [ B, 'y' ] },
// 		}
//
// Then update the structure of {@link Observable#_boundObservables} with updated
// binding, so it becomes:
//
// 		Map( {
// 			B: {
// 				x: Set( [
// 					{ observable: A, property: 'a', to: [ [ B, 'x' ] ] }
// 				] ),
// 				y: Set( [
// 					{ observable: A, property: 'b', to: [ [ B, 'y' ] ] },
// 				] )
//			}
// 		} )
//
// @private
// @param {BindChain} chain The binding initialized by {@link Observable#bind}.
function updateBindToBound(chain) {
    let toProperty;
    chain._bindings.forEach((binding, propertyName) => {
        // Note: For a binding without a callback, this will run only once
        // like in A.bind( 'x', 'y' ).to( B, 'a', 'b' )
        // TODO: ES6 destructuring.
        chain._to.forEach(to => {
            toProperty = to.properties[binding.callback ? 0 : chain._bindProperties.indexOf(propertyName)];
            binding.to.push([to.observable, toProperty]);
            updateBoundObservables(chain._observable, binding, to.observable, toProperty);
        });
    });
}
// Updates an property of a {@link Observable} with a value
// determined by an entry in {@link Observable#_boundProperties}.
//
// @private
// @param {Observable} observable A observable which property is to be updated.
// @param {String} propertyName An property to be updated.
function updateBoundObservableProperty(observable, propertyName) {
    const boundProperties = observable[boundPropertiesSymbol];
    const binding = boundProperties.get(propertyName);
    let propertyValue;
    // When a binding with callback is created like
    //
    // 		A.bind( 'a' ).to( B, 'b', C, 'c', callback );
    //
    // collect B.b and C.c, then pass them to callback to set A.a.
    if (binding.callback) {
        propertyValue = binding.callback.apply(observable, binding.to.map(to => to[0][to[1]]));
    }
    else {
        propertyValue = binding.to[0];
        propertyValue = propertyValue[0][propertyValue[1]];
    }
    if (Object.prototype.hasOwnProperty.call(observable, propertyName)) {
        observable[propertyName] = propertyValue;
    }
    else {
        observable.set(propertyName, propertyValue);
    }
}
// Starts listening to changes in {@link BindChain._to} observables to update
// {@link BindChain._observable} {@link BindChain._bindProperties}. Also sets the
// initial state of {@link BindChain._observable}.
//
// @private
// @param {Observable} observable
// @param {BindChain} chain The chain initialized by {@link Observable#bind}.
function attachBindToListeners(observable, toBindings) {
    toBindings.forEach(to => {
        const boundObservables = observable[boundObservablesSymbol];
        let bindings;
        // If there's already a chain between the observables (`observable` listens to
        // `to.observable`), there's no need to create another `change` event listener.
        if (!boundObservables.get(to.observable)) {
            observable.listenTo(to.observable, 'change', (evt, propertyName) => {
                bindings = boundObservables.get(to.observable)[propertyName];
                // Note: to.observable will fire for any property change, react
                // to changes of properties which are bound only.
                if (bindings) {
                    bindings.forEach(binding => {
                        updateBoundObservableProperty(observable, binding.property);
                    });
                }
            });
        }
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/plugin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable @typescript-eslint/no-invalid-void-type */
/**
 * @module core/plugin
 */

/**
 * The base class for CKEditor plugin classes.
 *
 * @implements module:core/plugin~PluginInterface
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class plugin_Plugin extends Observable {
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super();
        /**
         * The editor instance.
         *
         * Note that most editors implement the {@link module:core/editor/editorwithui~EditorWithUI} interface in addition
         * to the base {@link module:core/editor/editor~Editor} interface. However, editors with an external UI
         * (i.e. Bootstrap-based) or a headless editor may not implement the {@link module:core/editor/editorwithui~EditorWithUI}
         * interface.
         *
         * Because of above, to make plugins more universal, it is recommended to split features into:
         *  - The "editing" part that only uses the {@link module:core/editor/editor~Editor} interface.
         *  - The "UI" part that uses both the {@link module:core/editor/editor~Editor} interface and
         *  the {@link module:core/editor/editorwithui~EditorWithUI} interface.
         *
         * @readonly
         * @member {module:core/editor/editor~Editor} #editor
         */
        this.editor = editor;
        /**
         * Flag indicating whether a plugin is enabled or disabled.
         * A disabled plugin will not transform text.
         *
         * Plugin can be simply disabled like that:
         *
         *		// Disable the plugin so that no toolbars are visible.
         *		editor.plugins.get( 'TextTransformation' ).isEnabled = false;
         *
         * You can also use {@link #forceDisabled} method.
         *
         * @observable
         * @readonly
         * @member {Boolean} #isEnabled
         */
        this.set('isEnabled', true);
        /**
         * Holds identifiers for {@link #forceDisabled} mechanism.
         *
         * @type {Set.<String>}
         * @private
         */
        this._disableStack = new Set();
    }
    /**
     * Disables the plugin.
     *
     * Plugin may be disabled by multiple features or algorithms (at once). When disabling a plugin, unique id should be passed
     * (e.g. feature name). The same identifier should be used when {@link #clearForceDisabled enabling back} the plugin.
     * The plugin becomes enabled only after all features {@link #clearForceDisabled enabled it back}.
     *
     * Disabling and enabling a plugin:
     *
     *		plugin.isEnabled; // -> true
     *		plugin.forceDisabled( 'MyFeature' );
     *		plugin.isEnabled; // -> false
     *		plugin.clearForceDisabled( 'MyFeature' );
     *		plugin.isEnabled; // -> true
     *
     * Plugin disabled by multiple features:
     *
     *		plugin.forceDisabled( 'MyFeature' );
     *		plugin.forceDisabled( 'OtherFeature' );
     *		plugin.clearForceDisabled( 'MyFeature' );
     *		plugin.isEnabled; // -> false
     *		plugin.clearForceDisabled( 'OtherFeature' );
     *		plugin.isEnabled; // -> true
     *
     * Multiple disabling with the same identifier is redundant:
     *
     *		plugin.forceDisabled( 'MyFeature' );
     *		plugin.forceDisabled( 'MyFeature' );
     *		plugin.clearForceDisabled( 'MyFeature' );
     *		plugin.isEnabled; // -> true
     *
     * **Note:** some plugins or algorithms may have more complex logic when it comes to enabling or disabling certain plugins,
     * so the plugin might be still disabled after {@link #clearForceDisabled} was used.
     *
     * @param {String} id Unique identifier for disabling. Use the same id when {@link #clearForceDisabled enabling back} the plugin.
     */
    forceDisabled(id) {
        this._disableStack.add(id);
        if (this._disableStack.size == 1) {
            this.on('set:isEnabled', forceDisable, { priority: 'highest' });
            this.isEnabled = false;
        }
    }
    /**
     * Clears forced disable previously set through {@link #forceDisabled}. See {@link #forceDisabled}.
     *
     * @param {String} id Unique identifier, equal to the one passed in {@link #forceDisabled} call.
     */
    clearForceDisabled(id) {
        this._disableStack.delete(id);
        if (this._disableStack.size == 0) {
            this.off('set:isEnabled', forceDisable);
            this.isEnabled = true;
        }
    }
    /**
     * @inheritDoc
     */
    destroy() {
        this.stopListening();
    }
    /**
     * @inheritDoc
     */
    static get isContextPlugin() {
        return false;
    }
}
// Helper function that forces plugin to be disabled.
function forceDisable(evt) {
    evt.return = false;
    evt.stop();
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/command.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * The base class for CKEditor commands.
 *
 * Commands are the main way to manipulate editor contents and state. They are mostly used by UI elements (or by other
 * commands) to make changes in the model. Commands are available in every part of code that has access to
 * the {@link module:core/editor/editor~Editor editor} instance.
 *
 * Instances of registered commands can be retrieved from {@link module:core/editor/editor~Editor#commands `editor.commands`}.
 * The easiest way to execute a command is through {@link module:core/editor/editor~Editor#execute `editor.execute()`}.
 *
 * By default, commands are disabled when the editor is in {@link module:core/editor/editor~Editor#isReadOnly read-only} mode
 * but commands with the {@link module:core/command~Command#affectsData `affectsData`} flag set to `false` will not be disabled.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class command_Command extends Observable {
    /**
     * Creates a new `Command` instance.
     *
     * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used.
     */
    constructor(editor) {
        super();
        /**
         * The editor on which this command will be used.
         *
         * @readonly
         * @member {module:core/editor/editor~Editor}
         */
        this.editor = editor;
        /**
         * The value of the command. A concrete command class should define what it represents for it.
         *
         * For example, the `'bold'` command's value indicates whether the selection starts in a bolded text.
         * And the value of the `'link'` command may be an object with links details.
         *
         * It is possible for a command to have no value (e.g. for stateless actions such as `'uploadImage'`).
         *
         * A concrete command class should control this value by overriding the {@link #refresh `refresh()`} method.
         *
         * @observable
         * @readonly
         * @member #value
         */
        this.set('value', undefined);
        /**
         * Flag indicating whether a command is enabled or disabled.
         * A disabled command will do nothing when executed.
         *
         * A concrete command class should control this value by overriding the {@link #refresh `refresh()`} method.
         *
         * It is possible to disable a command from "outside". For instance, in your integration you may want to disable
         * a certain set of commands for the time being. To do that, you can use the fact that `isEnabled` is observable
         * and it fires the `set:isEnabled` event every time anyone tries to modify its value:
         *
         *		function disableCommand( cmd ) {
         *			cmd.on( 'set:isEnabled', forceDisable, { priority: 'highest' } );
         *
         *			cmd.isEnabled = false;
         *
         *			// Make it possible to enable the command again.
         *			return () => {
         *				cmd.off( 'set:isEnabled', forceDisable );
         *				cmd.refresh();
         *			};
         *
         *			function forceDisable( evt ) {
         *				evt.return = false;
         *				evt.stop();
         *			}
         *		}
         *
         *		// Usage:
         *
         *		// Disabling the command.
         *		const enableBold = disableCommand( editor.commands.get( 'bold' ) );
         *
         *		// Enabling the command again.
         *		enableBold();
         *
         * @observable
         * @readonly
         * @member {Boolean} #isEnabled
         */
        this.set('isEnabled', false);
        /**
         * A flag indicating whether a command execution changes the editor data or not.
         *
         * Commands with `affectsData` set to `false` will not be automatically disabled in
         * the {@link module:core/editor/editor~Editor#isReadOnly read-only mode} and
         * {@glink features/read-only#related-features other editor modes} with restricted user write permissions.
         *
         * **Note:** You do not have to set it for your every command. It is `true` by default.
         *
         * @readonly
         * @default true
         * @member {Boolean} #affectsData
         */
        this._affectsData = true;
        /**
         * Holds identifiers for {@link #forceDisabled} mechanism.
         *
         * @type {Set.<String>}
         * @private
         */
        this._disableStack = new Set();
        this.decorate('execute');
        // By default every command is refreshed when changes are applied to the model.
        this.listenTo(this.editor.model.document, 'change', () => {
            this.refresh();
        });
        this.on('execute', evt => {
            if (!this.isEnabled) {
                evt.stop();
            }
        }, { priority: 'high' });
        // By default commands are disabled when the editor is in read-only mode.
        this.listenTo(editor, 'change:isReadOnly', (evt, name, value) => {
            if (value && this.affectsData) {
                this.forceDisabled('readOnlyMode');
            }
            else {
                this.clearForceDisabled('readOnlyMode');
            }
        });
    }
    get affectsData() {
        return this._affectsData;
    }
    set affectsData(affectsData) {
        this._affectsData = affectsData;
    }
    /**
     * Refreshes the command. The command should update its {@link #isEnabled} and {@link #value} properties
     * in this method.
     *
     * This method is automatically called when
     * {@link module:engine/model/document~Document#event:change any changes are applied to the document}.
     */
    refresh() {
        this.isEnabled = true;
    }
    /**
     * Disables the command.
     *
     * Command may be disabled by multiple features or algorithms (at once). When disabling a command, unique id should be passed
     * (e.g. feature name). The same identifier should be used when {@link #clearForceDisabled enabling back} the command.
     * The command becomes enabled only after all features {@link #clearForceDisabled enabled it back}.
     *
     * Disabling and enabling a command:
     *
     *		command.isEnabled; // -> true
     *		command.forceDisabled( 'MyFeature' );
     *		command.isEnabled; // -> false
     *		command.clearForceDisabled( 'MyFeature' );
     *		command.isEnabled; // -> true
     *
     * Command disabled by multiple features:
     *
     *		command.forceDisabled( 'MyFeature' );
     *		command.forceDisabled( 'OtherFeature' );
     *		command.clearForceDisabled( 'MyFeature' );
     *		command.isEnabled; // -> false
     *		command.clearForceDisabled( 'OtherFeature' );
     *		command.isEnabled; // -> true
     *
     * Multiple disabling with the same identifier is redundant:
     *
     *		command.forceDisabled( 'MyFeature' );
     *		command.forceDisabled( 'MyFeature' );
     *		command.clearForceDisabled( 'MyFeature' );
     *		command.isEnabled; // -> true
     *
     * **Note:** some commands or algorithms may have more complex logic when it comes to enabling or disabling certain commands,
     * so the command might be still disabled after {@link #clearForceDisabled} was used.
     *
     * @param {String} id Unique identifier for disabling. Use the same id when {@link #clearForceDisabled enabling back} the command.
     */
    forceDisabled(id) {
        this._disableStack.add(id);
        if (this._disableStack.size == 1) {
            this.on('set:isEnabled', command_forceDisable, { priority: 'highest' });
            this.isEnabled = false;
        }
    }
    /**
     * Clears forced disable previously set through {@link #forceDisabled}. See {@link #forceDisabled}.
     *
     * @param {String} id Unique identifier, equal to the one passed in {@link #forceDisabled} call.
     */
    clearForceDisabled(id) {
        this._disableStack.delete(id);
        if (this._disableStack.size == 0) {
            this.off('set:isEnabled', command_forceDisable);
            this.refresh();
        }
    }
    /**
     * Executes the command.
     *
     * A command may accept parameters. They will be passed from {@link module:core/editor/editor~Editor#execute `editor.execute()`}
     * to the command.
     *
     * The `execute()` method will automatically abort when the command is disabled ({@link #isEnabled} is `false`).
     * This behavior is implemented by a high priority listener to the {@link #event:execute} event.
     *
     * In order to see how to disable a command from "outside" see the {@link #isEnabled} documentation.
     *
     * This method may return a value, which would be forwarded all the way down to the
     * {@link module:core/editor/editor~Editor#execute `editor.execute()`}.
     *
     * @fires execute
     */
    execute(...args) { return undefined; }
    /**
     * Destroys the command.
     */
    destroy() {
        this.stopListening();
    }
}
// Helper function that forces command to be disabled.
function command_forceDisable(evt) {
    evt.return = false;
    evt.stop();
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/multicommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */


/**
 * @module core/multicommand
 */
/**
 * A CKEditor command that aggregates other commands.
 *
 * This command is used to proxy multiple commands. The multi-command is enabled when
 * at least one of its registered child commands is enabled.
 * When executing a multi-command the first enabled command with highest priority will be executed.
 *
 *		const multiCommand = new MultiCommand( editor );
 *
 *		const commandFoo = new Command( editor );
 *		const commandBar = new Command( editor );
 *
 *		// Register a child command.
 *		multiCommand.registerChildCommand( commandFoo );
 *		// Register a child command with a low priority.
 *		multiCommand.registerChildCommand( commandBar, { priority: 'low' } );
 *
 *		// Enable one of the commands.
 *		commandBar.isEnabled = true;
 *
 *		multiCommand.execute(); // Will execute commandBar.
 *
 * @extends module:core/command~Command
 */
class MultiCommand extends command_Command {
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        /**
         * Registered child commands definitions.
         *
         * @type {Array.<Object>}
         * @private
         */
        this._childCommandsDefinitions = [];
    }
    /**
     * @inheritDoc
     */
    refresh() {
        // Override base command refresh(): the command's state is changed when one of child commands changes states.
    }
    /**
     * Executes the first enabled command which has the highest priority of all registered child commands.
     *
     * @returns {*} The value returned by the {@link module:core/command~Command#execute `command.execute()`}.
     */
    execute(...args) {
        const command = this._getFirstEnabledCommand();
        return !!command && command.execute(args);
    }
    /**
     * Registers a child command.
     *
     * @param {module:core/command~Command} command
     * @param {Object} options An object with configuration options.
     * @param {module:utils/priorities~PriorityString} [options.priority='normal'] Priority of a command to register.
     */
    registerChildCommand(command, options = {}) {
        insertToPriorityArray(this._childCommandsDefinitions, { command, priority: options.priority || 'normal' });
        // Change multi command enabled state when one of registered commands changes state.
        command.on('change:isEnabled', () => this._checkEnabled());
        this._checkEnabled();
    }
    /**
     * Checks if any of child commands is enabled.
     *
     * @private
     */
    _checkEnabled() {
        this.isEnabled = !!this._getFirstEnabledCommand();
    }
    /**
     * Returns a first enabled command with the highest priority or `undefined` if none of them is enabled.
     *
     * @returns {module:core/command~Command|undefined}
     * @private
     */
    _getFirstEnabledCommand() {
        const commandDefinition = this._childCommandsDefinitions.find(({ command }) => command.isEnabled);
        return commandDefinition && commandDefinition.command;
    }
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/_freeGlobal.js
/** Detect free variable `global` from Node.js. */
var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;

/* harmony default export */ const _freeGlobal = (freeGlobal);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_root.js


/** Detect free variable `self`. */
var freeSelf = typeof self == 'object' && self && self.Object === Object && self;

/** Used as a reference to the global object. */
var root = _freeGlobal || freeSelf || Function('return this')();

/* harmony default export */ const _root = (root);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_Symbol.js


/** Built-in value references. */
var _Symbol_Symbol = _root.Symbol;

/* harmony default export */ const _Symbol = (_Symbol_Symbol);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getRawTag.js


/** Used for built-in method references. */
var objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _getRawTag_hasOwnProperty = objectProto.hasOwnProperty;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var nativeObjectToString = objectProto.toString;

/** Built-in value references. */
var symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;

/**
 * A specialized version of `baseGetTag` which ignores `Symbol.toStringTag` values.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the raw `toStringTag`.
 */
function getRawTag(value) {
  var isOwn = _getRawTag_hasOwnProperty.call(value, symToStringTag),
      tag = value[symToStringTag];

  try {
    value[symToStringTag] = undefined;
    var unmasked = true;
  } catch (e) {}

  var result = nativeObjectToString.call(value);
  if (unmasked) {
    if (isOwn) {
      value[symToStringTag] = tag;
    } else {
      delete value[symToStringTag];
    }
  }
  return result;
}

/* harmony default export */ const _getRawTag = (getRawTag);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_objectToString.js
/** Used for built-in method references. */
var _objectToString_objectProto = Object.prototype;

/**
 * Used to resolve the
 * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
 * of values.
 */
var _objectToString_nativeObjectToString = _objectToString_objectProto.toString;

/**
 * Converts `value` to a string using `Object.prototype.toString`.
 *
 * @private
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 */
function objectToString(value) {
  return _objectToString_nativeObjectToString.call(value);
}

/* harmony default export */ const _objectToString = (objectToString);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseGetTag.js




/** `Object#toString` result references. */
var nullTag = '[object Null]',
    undefinedTag = '[object Undefined]';

/** Built-in value references. */
var _baseGetTag_symToStringTag = _Symbol ? _Symbol.toStringTag : undefined;

/**
 * The base implementation of `getTag` without fallbacks for buggy environments.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
function baseGetTag(value) {
  if (value == null) {
    return value === undefined ? undefinedTag : nullTag;
  }
  return (_baseGetTag_symToStringTag && _baseGetTag_symToStringTag in Object(value))
    ? _getRawTag(value)
    : _objectToString(value);
}

/* harmony default export */ const _baseGetTag = (baseGetTag);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_overArg.js
/**
 * Creates a unary function that invokes `func` with its argument transformed.
 *
 * @private
 * @param {Function} func The function to wrap.
 * @param {Function} transform The argument transform.
 * @returns {Function} Returns the new function.
 */
function overArg(func, transform) {
  return function(arg) {
    return func(transform(arg));
  };
}

/* harmony default export */ const _overArg = (overArg);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getPrototype.js


/** Built-in value references. */
var getPrototype = _overArg(Object.getPrototypeOf, Object);

/* harmony default export */ const _getPrototype = (getPrototype);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isObjectLike.js
/**
 * Checks if `value` is object-like. A value is object-like if it's not `null`
 * and has a `typeof` result of "object".
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is object-like, else `false`.
 * @example
 *
 * _.isObjectLike({});
 * // => true
 *
 * _.isObjectLike([1, 2, 3]);
 * // => true
 *
 * _.isObjectLike(_.noop);
 * // => false
 *
 * _.isObjectLike(null);
 * // => false
 */
function isObjectLike(value) {
  return value != null && typeof value == 'object';
}

/* harmony default export */ const lodash_es_isObjectLike = (isObjectLike);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isPlainObject.js




/** `Object#toString` result references. */
var objectTag = '[object Object]';

/** Used for built-in method references. */
var funcProto = Function.prototype,
    isPlainObject_objectProto = Object.prototype;

/** Used to resolve the decompiled source of functions. */
var funcToString = funcProto.toString;

/** Used to check objects for own properties. */
var isPlainObject_hasOwnProperty = isPlainObject_objectProto.hasOwnProperty;

/** Used to infer the `Object` constructor. */
var objectCtorString = funcToString.call(Object);

/**
 * Checks if `value` is a plain object, that is, an object created by the
 * `Object` constructor or one with a `[[Prototype]]` of `null`.
 *
 * @static
 * @memberOf _
 * @since 0.8.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a plain object, else `false`.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 * }
 *
 * _.isPlainObject(new Foo);
 * // => false
 *
 * _.isPlainObject([1, 2, 3]);
 * // => false
 *
 * _.isPlainObject({ 'x': 0, 'y': 0 });
 * // => true
 *
 * _.isPlainObject(Object.create(null));
 * // => true
 */
function isPlainObject(value) {
  if (!lodash_es_isObjectLike(value) || _baseGetTag(value) != objectTag) {
    return false;
  }
  var proto = _getPrototype(value);
  if (proto === null) {
    return true;
  }
  var Ctor = isPlainObject_hasOwnProperty.call(proto, 'constructor') && proto.constructor;
  return typeof Ctor == 'function' && Ctor instanceof Ctor &&
    funcToString.call(Ctor) == objectCtorString;
}

/* harmony default export */ const lodash_es_isPlainObject = (isPlainObject);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_listCacheClear.js
/**
 * Removes all key-value entries from the list cache.
 *
 * @private
 * @name clear
 * @memberOf ListCache
 */
function listCacheClear() {
  this.__data__ = [];
  this.size = 0;
}

/* harmony default export */ const _listCacheClear = (listCacheClear);

;// CONCATENATED MODULE: ./node_modules/lodash-es/eq.js
/**
 * Performs a
 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * comparison between two values to determine if they are equivalent.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 * @example
 *
 * var object = { 'a': 1 };
 * var other = { 'a': 1 };
 *
 * _.eq(object, object);
 * // => true
 *
 * _.eq(object, other);
 * // => false
 *
 * _.eq('a', 'a');
 * // => true
 *
 * _.eq('a', Object('a'));
 * // => false
 *
 * _.eq(NaN, NaN);
 * // => true
 */
function eq(value, other) {
  return value === other || (value !== value && other !== other);
}

/* harmony default export */ const lodash_es_eq = (eq);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_assocIndexOf.js


/**
 * Gets the index at which the `key` is found in `array` of key-value pairs.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} key The key to search for.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function assocIndexOf(array, key) {
  var length = array.length;
  while (length--) {
    if (lodash_es_eq(array[length][0], key)) {
      return length;
    }
  }
  return -1;
}

/* harmony default export */ const _assocIndexOf = (assocIndexOf);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_listCacheDelete.js


/** Used for built-in method references. */
var arrayProto = Array.prototype;

/** Built-in value references. */
var splice = arrayProto.splice;

/**
 * Removes `key` and its value from the list cache.
 *
 * @private
 * @name delete
 * @memberOf ListCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function listCacheDelete(key) {
  var data = this.__data__,
      index = _assocIndexOf(data, key);

  if (index < 0) {
    return false;
  }
  var lastIndex = data.length - 1;
  if (index == lastIndex) {
    data.pop();
  } else {
    splice.call(data, index, 1);
  }
  --this.size;
  return true;
}

/* harmony default export */ const _listCacheDelete = (listCacheDelete);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_listCacheGet.js


/**
 * Gets the list cache value for `key`.
 *
 * @private
 * @name get
 * @memberOf ListCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function listCacheGet(key) {
  var data = this.__data__,
      index = _assocIndexOf(data, key);

  return index < 0 ? undefined : data[index][1];
}

/* harmony default export */ const _listCacheGet = (listCacheGet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_listCacheHas.js


/**
 * Checks if a list cache value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf ListCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function listCacheHas(key) {
  return _assocIndexOf(this.__data__, key) > -1;
}

/* harmony default export */ const _listCacheHas = (listCacheHas);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_listCacheSet.js


/**
 * Sets the list cache `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf ListCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the list cache instance.
 */
function listCacheSet(key, value) {
  var data = this.__data__,
      index = _assocIndexOf(data, key);

  if (index < 0) {
    ++this.size;
    data.push([key, value]);
  } else {
    data[index][1] = value;
  }
  return this;
}

/* harmony default export */ const _listCacheSet = (listCacheSet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_ListCache.js






/**
 * Creates an list cache object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function ListCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `ListCache`.
ListCache.prototype.clear = _listCacheClear;
ListCache.prototype['delete'] = _listCacheDelete;
ListCache.prototype.get = _listCacheGet;
ListCache.prototype.has = _listCacheHas;
ListCache.prototype.set = _listCacheSet;

/* harmony default export */ const _ListCache = (ListCache);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_stackClear.js


/**
 * Removes all key-value entries from the stack.
 *
 * @private
 * @name clear
 * @memberOf Stack
 */
function stackClear() {
  this.__data__ = new _ListCache;
  this.size = 0;
}

/* harmony default export */ const _stackClear = (stackClear);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_stackDelete.js
/**
 * Removes `key` and its value from the stack.
 *
 * @private
 * @name delete
 * @memberOf Stack
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function stackDelete(key) {
  var data = this.__data__,
      result = data['delete'](key);

  this.size = data.size;
  return result;
}

/* harmony default export */ const _stackDelete = (stackDelete);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_stackGet.js
/**
 * Gets the stack value for `key`.
 *
 * @private
 * @name get
 * @memberOf Stack
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function stackGet(key) {
  return this.__data__.get(key);
}

/* harmony default export */ const _stackGet = (stackGet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_stackHas.js
/**
 * Checks if a stack value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Stack
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function stackHas(key) {
  return this.__data__.has(key);
}

/* harmony default export */ const _stackHas = (stackHas);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isFunction.js



/** `Object#toString` result references. */
var asyncTag = '[object AsyncFunction]',
    funcTag = '[object Function]',
    genTag = '[object GeneratorFunction]',
    proxyTag = '[object Proxy]';

/**
 * Checks if `value` is classified as a `Function` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a function, else `false`.
 * @example
 *
 * _.isFunction(_);
 * // => true
 *
 * _.isFunction(/abc/);
 * // => false
 */
function isFunction(value) {
  if (!lodash_es_isObject(value)) {
    return false;
  }
  // The use of `Object#toString` avoids issues with the `typeof` operator
  // in Safari 9 which returns 'object' for typed arrays and other constructors.
  var tag = _baseGetTag(value);
  return tag == funcTag || tag == genTag || tag == asyncTag || tag == proxyTag;
}

/* harmony default export */ const lodash_es_isFunction = (isFunction);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_coreJsData.js


/** Used to detect overreaching core-js shims. */
var coreJsData = _root["__core-js_shared__"];

/* harmony default export */ const _coreJsData = (coreJsData);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_isMasked.js


/** Used to detect methods masquerading as native. */
var maskSrcKey = (function() {
  var uid = /[^.]+$/.exec(_coreJsData && _coreJsData.keys && _coreJsData.keys.IE_PROTO || '');
  return uid ? ('Symbol(src)_1.' + uid) : '';
}());

/**
 * Checks if `func` has its source masked.
 *
 * @private
 * @param {Function} func The function to check.
 * @returns {boolean} Returns `true` if `func` is masked, else `false`.
 */
function isMasked(func) {
  return !!maskSrcKey && (maskSrcKey in func);
}

/* harmony default export */ const _isMasked = (isMasked);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_toSource.js
/** Used for built-in method references. */
var _toSource_funcProto = Function.prototype;

/** Used to resolve the decompiled source of functions. */
var _toSource_funcToString = _toSource_funcProto.toString;

/**
 * Converts `func` to its source code.
 *
 * @private
 * @param {Function} func The function to convert.
 * @returns {string} Returns the source code.
 */
function toSource(func) {
  if (func != null) {
    try {
      return _toSource_funcToString.call(func);
    } catch (e) {}
    try {
      return (func + '');
    } catch (e) {}
  }
  return '';
}

/* harmony default export */ const _toSource = (toSource);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsNative.js





/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;

/** Used to detect host constructors (Safari). */
var reIsHostCtor = /^\[object .+?Constructor\]$/;

/** Used for built-in method references. */
var _baseIsNative_funcProto = Function.prototype,
    _baseIsNative_objectProto = Object.prototype;

/** Used to resolve the decompiled source of functions. */
var _baseIsNative_funcToString = _baseIsNative_funcProto.toString;

/** Used to check objects for own properties. */
var _baseIsNative_hasOwnProperty = _baseIsNative_objectProto.hasOwnProperty;

/** Used to detect if a method is native. */
var reIsNative = RegExp('^' +
  _baseIsNative_funcToString.call(_baseIsNative_hasOwnProperty).replace(reRegExpChar, '\\$&')
  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
);

/**
 * The base implementation of `_.isNative` without bad shim checks.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a native function,
 *  else `false`.
 */
function baseIsNative(value) {
  if (!lodash_es_isObject(value) || _isMasked(value)) {
    return false;
  }
  var pattern = lodash_es_isFunction(value) ? reIsNative : reIsHostCtor;
  return pattern.test(_toSource(value));
}

/* harmony default export */ const _baseIsNative = (baseIsNative);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getValue.js
/**
 * Gets the value at `key` of `object`.
 *
 * @private
 * @param {Object} [object] The object to query.
 * @param {string} key The key of the property to get.
 * @returns {*} Returns the property value.
 */
function getValue(object, key) {
  return object == null ? undefined : object[key];
}

/* harmony default export */ const _getValue = (getValue);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getNative.js



/**
 * Gets the native function at `key` of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the method to get.
 * @returns {*} Returns the function if it's native, else `undefined`.
 */
function getNative(object, key) {
  var value = _getValue(object, key);
  return _baseIsNative(value) ? value : undefined;
}

/* harmony default export */ const _getNative = (getNative);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_Map.js



/* Built-in method references that are verified to be native. */
var _Map_Map = _getNative(_root, 'Map');

/* harmony default export */ const _Map = (_Map_Map);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_nativeCreate.js


/* Built-in method references that are verified to be native. */
var nativeCreate = _getNative(Object, 'create');

/* harmony default export */ const _nativeCreate = (nativeCreate);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_hashClear.js


/**
 * Removes all key-value entries from the hash.
 *
 * @private
 * @name clear
 * @memberOf Hash
 */
function hashClear() {
  this.__data__ = _nativeCreate ? _nativeCreate(null) : {};
  this.size = 0;
}

/* harmony default export */ const _hashClear = (hashClear);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_hashDelete.js
/**
 * Removes `key` and its value from the hash.
 *
 * @private
 * @name delete
 * @memberOf Hash
 * @param {Object} hash The hash to modify.
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function hashDelete(key) {
  var result = this.has(key) && delete this.__data__[key];
  this.size -= result ? 1 : 0;
  return result;
}

/* harmony default export */ const _hashDelete = (hashDelete);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_hashGet.js


/** Used to stand-in for `undefined` hash values. */
var HASH_UNDEFINED = '__lodash_hash_undefined__';

/** Used for built-in method references. */
var _hashGet_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _hashGet_hasOwnProperty = _hashGet_objectProto.hasOwnProperty;

/**
 * Gets the hash value for `key`.
 *
 * @private
 * @name get
 * @memberOf Hash
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function hashGet(key) {
  var data = this.__data__;
  if (_nativeCreate) {
    var result = data[key];
    return result === HASH_UNDEFINED ? undefined : result;
  }
  return _hashGet_hasOwnProperty.call(data, key) ? data[key] : undefined;
}

/* harmony default export */ const _hashGet = (hashGet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_hashHas.js


/** Used for built-in method references. */
var _hashHas_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _hashHas_hasOwnProperty = _hashHas_objectProto.hasOwnProperty;

/**
 * Checks if a hash value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf Hash
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function hashHas(key) {
  var data = this.__data__;
  return _nativeCreate ? (data[key] !== undefined) : _hashHas_hasOwnProperty.call(data, key);
}

/* harmony default export */ const _hashHas = (hashHas);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_hashSet.js


/** Used to stand-in for `undefined` hash values. */
var _hashSet_HASH_UNDEFINED = '__lodash_hash_undefined__';

/**
 * Sets the hash `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Hash
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the hash instance.
 */
function hashSet(key, value) {
  var data = this.__data__;
  this.size += this.has(key) ? 0 : 1;
  data[key] = (_nativeCreate && value === undefined) ? _hashSet_HASH_UNDEFINED : value;
  return this;
}

/* harmony default export */ const _hashSet = (hashSet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_Hash.js






/**
 * Creates a hash object.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Hash(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `Hash`.
Hash.prototype.clear = _hashClear;
Hash.prototype['delete'] = _hashDelete;
Hash.prototype.get = _hashGet;
Hash.prototype.has = _hashHas;
Hash.prototype.set = _hashSet;

/* harmony default export */ const _Hash = (Hash);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_mapCacheClear.js




/**
 * Removes all key-value entries from the map.
 *
 * @private
 * @name clear
 * @memberOf MapCache
 */
function mapCacheClear() {
  this.size = 0;
  this.__data__ = {
    'hash': new _Hash,
    'map': new (_Map || _ListCache),
    'string': new _Hash
  };
}

/* harmony default export */ const _mapCacheClear = (mapCacheClear);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_isKeyable.js
/**
 * Checks if `value` is suitable for use as unique object key.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
 */
function isKeyable(value) {
  var type = typeof value;
  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
    ? (value !== '__proto__')
    : (value === null);
}

/* harmony default export */ const _isKeyable = (isKeyable);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getMapData.js


/**
 * Gets the data for `map`.
 *
 * @private
 * @param {Object} map The map to query.
 * @param {string} key The reference key.
 * @returns {*} Returns the map data.
 */
function getMapData(map, key) {
  var data = map.__data__;
  return _isKeyable(key)
    ? data[typeof key == 'string' ? 'string' : 'hash']
    : data.map;
}

/* harmony default export */ const _getMapData = (getMapData);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_mapCacheDelete.js


/**
 * Removes `key` and its value from the map.
 *
 * @private
 * @name delete
 * @memberOf MapCache
 * @param {string} key The key of the value to remove.
 * @returns {boolean} Returns `true` if the entry was removed, else `false`.
 */
function mapCacheDelete(key) {
  var result = _getMapData(this, key)['delete'](key);
  this.size -= result ? 1 : 0;
  return result;
}

/* harmony default export */ const _mapCacheDelete = (mapCacheDelete);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_mapCacheGet.js


/**
 * Gets the map value for `key`.
 *
 * @private
 * @name get
 * @memberOf MapCache
 * @param {string} key The key of the value to get.
 * @returns {*} Returns the entry value.
 */
function mapCacheGet(key) {
  return _getMapData(this, key).get(key);
}

/* harmony default export */ const _mapCacheGet = (mapCacheGet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_mapCacheHas.js


/**
 * Checks if a map value for `key` exists.
 *
 * @private
 * @name has
 * @memberOf MapCache
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function mapCacheHas(key) {
  return _getMapData(this, key).has(key);
}

/* harmony default export */ const _mapCacheHas = (mapCacheHas);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_mapCacheSet.js


/**
 * Sets the map `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf MapCache
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the map cache instance.
 */
function mapCacheSet(key, value) {
  var data = _getMapData(this, key),
      size = data.size;

  data.set(key, value);
  this.size += data.size == size ? 0 : 1;
  return this;
}

/* harmony default export */ const _mapCacheSet = (mapCacheSet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_MapCache.js






/**
 * Creates a map cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function MapCache(entries) {
  var index = -1,
      length = entries == null ? 0 : entries.length;

  this.clear();
  while (++index < length) {
    var entry = entries[index];
    this.set(entry[0], entry[1]);
  }
}

// Add methods to `MapCache`.
MapCache.prototype.clear = _mapCacheClear;
MapCache.prototype['delete'] = _mapCacheDelete;
MapCache.prototype.get = _mapCacheGet;
MapCache.prototype.has = _mapCacheHas;
MapCache.prototype.set = _mapCacheSet;

/* harmony default export */ const _MapCache = (MapCache);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_stackSet.js




/** Used as the size to enable large array optimizations. */
var LARGE_ARRAY_SIZE = 200;

/**
 * Sets the stack `key` to `value`.
 *
 * @private
 * @name set
 * @memberOf Stack
 * @param {string} key The key of the value to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns the stack cache instance.
 */
function stackSet(key, value) {
  var data = this.__data__;
  if (data instanceof _ListCache) {
    var pairs = data.__data__;
    if (!_Map || (pairs.length < LARGE_ARRAY_SIZE - 1)) {
      pairs.push([key, value]);
      this.size = ++data.size;
      return this;
    }
    data = this.__data__ = new _MapCache(pairs);
  }
  data.set(key, value);
  this.size = data.size;
  return this;
}

/* harmony default export */ const _stackSet = (stackSet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_Stack.js







/**
 * Creates a stack cache object to store key-value pairs.
 *
 * @private
 * @constructor
 * @param {Array} [entries] The key-value pairs to cache.
 */
function Stack(entries) {
  var data = this.__data__ = new _ListCache(entries);
  this.size = data.size;
}

// Add methods to `Stack`.
Stack.prototype.clear = _stackClear;
Stack.prototype['delete'] = _stackDelete;
Stack.prototype.get = _stackGet;
Stack.prototype.has = _stackHas;
Stack.prototype.set = _stackSet;

/* harmony default export */ const _Stack = (Stack);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_arrayEach.js
/**
 * A specialized version of `_.forEach` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns `array`.
 */
function arrayEach(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length;

  while (++index < length) {
    if (iteratee(array[index], index, array) === false) {
      break;
    }
  }
  return array;
}

/* harmony default export */ const _arrayEach = (arrayEach);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_defineProperty.js


var defineProperty = (function() {
  try {
    var func = _getNative(Object, 'defineProperty');
    func({}, '', {});
    return func;
  } catch (e) {}
}());

/* harmony default export */ const _defineProperty = (defineProperty);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseAssignValue.js


/**
 * The base implementation of `assignValue` and `assignMergeValue` without
 * value checks.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function baseAssignValue(object, key, value) {
  if (key == '__proto__' && _defineProperty) {
    _defineProperty(object, key, {
      'configurable': true,
      'enumerable': true,
      'value': value,
      'writable': true
    });
  } else {
    object[key] = value;
  }
}

/* harmony default export */ const _baseAssignValue = (baseAssignValue);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_assignValue.js



/** Used for built-in method references. */
var _assignValue_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _assignValue_hasOwnProperty = _assignValue_objectProto.hasOwnProperty;

/**
 * Assigns `value` to `key` of `object` if the existing value is not equivalent
 * using [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignValue(object, key, value) {
  var objValue = object[key];
  if (!(_assignValue_hasOwnProperty.call(object, key) && lodash_es_eq(objValue, value)) ||
      (value === undefined && !(key in object))) {
    _baseAssignValue(object, key, value);
  }
}

/* harmony default export */ const _assignValue = (assignValue);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_copyObject.js



/**
 * Copies properties of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy properties from.
 * @param {Array} props The property identifiers to copy.
 * @param {Object} [object={}] The object to copy properties to.
 * @param {Function} [customizer] The function to customize copied values.
 * @returns {Object} Returns `object`.
 */
function copyObject(source, props, object, customizer) {
  var isNew = !object;
  object || (object = {});

  var index = -1,
      length = props.length;

  while (++index < length) {
    var key = props[index];

    var newValue = customizer
      ? customizer(object[key], source[key], key, object, source)
      : undefined;

    if (newValue === undefined) {
      newValue = source[key];
    }
    if (isNew) {
      _baseAssignValue(object, key, newValue);
    } else {
      _assignValue(object, key, newValue);
    }
  }
  return object;
}

/* harmony default export */ const _copyObject = (copyObject);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseTimes.js
/**
 * The base implementation of `_.times` without support for iteratee shorthands
 * or max array length checks.
 *
 * @private
 * @param {number} n The number of times to invoke `iteratee`.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the array of results.
 */
function baseTimes(n, iteratee) {
  var index = -1,
      result = Array(n);

  while (++index < n) {
    result[index] = iteratee(index);
  }
  return result;
}

/* harmony default export */ const _baseTimes = (baseTimes);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsArguments.js



/** `Object#toString` result references. */
var argsTag = '[object Arguments]';

/**
 * The base implementation of `_.isArguments`.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 */
function baseIsArguments(value) {
  return lodash_es_isObjectLike(value) && _baseGetTag(value) == argsTag;
}

/* harmony default export */ const _baseIsArguments = (baseIsArguments);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isArguments.js



/** Used for built-in method references. */
var isArguments_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var isArguments_hasOwnProperty = isArguments_objectProto.hasOwnProperty;

/** Built-in value references. */
var propertyIsEnumerable = isArguments_objectProto.propertyIsEnumerable;

/**
 * Checks if `value` is likely an `arguments` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an `arguments` object,
 *  else `false`.
 * @example
 *
 * _.isArguments(function() { return arguments; }());
 * // => true
 *
 * _.isArguments([1, 2, 3]);
 * // => false
 */
var isArguments = _baseIsArguments(function() { return arguments; }()) ? _baseIsArguments : function(value) {
  return lodash_es_isObjectLike(value) && isArguments_hasOwnProperty.call(value, 'callee') &&
    !propertyIsEnumerable.call(value, 'callee');
};

/* harmony default export */ const lodash_es_isArguments = (isArguments);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isArray.js
/**
 * Checks if `value` is classified as an `Array` object.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array, else `false`.
 * @example
 *
 * _.isArray([1, 2, 3]);
 * // => true
 *
 * _.isArray(document.body.children);
 * // => false
 *
 * _.isArray('abc');
 * // => false
 *
 * _.isArray(_.noop);
 * // => false
 */
var isArray = Array.isArray;

/* harmony default export */ const lodash_es_isArray = (isArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/stubFalse.js
/**
 * This method returns `false`.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {boolean} Returns `false`.
 * @example
 *
 * _.times(2, _.stubFalse);
 * // => [false, false]
 */
function stubFalse() {
  return false;
}

/* harmony default export */ const lodash_es_stubFalse = (stubFalse);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isBuffer.js



/** Detect free variable `exports`. */
var freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var freeModule = freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var moduleExports = freeModule && freeModule.exports === freeExports;

/** Built-in value references. */
var Buffer = moduleExports ? _root.Buffer : undefined;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeIsBuffer = Buffer ? Buffer.isBuffer : undefined;

/**
 * Checks if `value` is a buffer.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a buffer, else `false`.
 * @example
 *
 * _.isBuffer(new Buffer(2));
 * // => true
 *
 * _.isBuffer(new Uint8Array(2));
 * // => false
 */
var isBuffer = nativeIsBuffer || lodash_es_stubFalse;

/* harmony default export */ const lodash_es_isBuffer = (isBuffer);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_isIndex.js
/** Used as references for various `Number` constants. */
var MAX_SAFE_INTEGER = 9007199254740991;

/** Used to detect unsigned integer values. */
var reIsUint = /^(?:0|[1-9]\d*)$/;

/**
 * Checks if `value` is a valid array-like index.
 *
 * @private
 * @param {*} value The value to check.
 * @param {number} [length=MAX_SAFE_INTEGER] The upper bounds of a valid index.
 * @returns {boolean} Returns `true` if `value` is a valid index, else `false`.
 */
function isIndex(value, length) {
  var type = typeof value;
  length = length == null ? MAX_SAFE_INTEGER : length;

  return !!length &&
    (type == 'number' ||
      (type != 'symbol' && reIsUint.test(value))) &&
        (value > -1 && value % 1 == 0 && value < length);
}

/* harmony default export */ const _isIndex = (isIndex);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isLength.js
/** Used as references for various `Number` constants. */
var isLength_MAX_SAFE_INTEGER = 9007199254740991;

/**
 * Checks if `value` is a valid array-like length.
 *
 * **Note:** This method is loosely based on
 * [`ToLength`](http://ecma-international.org/ecma-262/7.0/#sec-tolength).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a valid length, else `false`.
 * @example
 *
 * _.isLength(3);
 * // => true
 *
 * _.isLength(Number.MIN_VALUE);
 * // => false
 *
 * _.isLength(Infinity);
 * // => false
 *
 * _.isLength('3');
 * // => false
 */
function isLength(value) {
  return typeof value == 'number' &&
    value > -1 && value % 1 == 0 && value <= isLength_MAX_SAFE_INTEGER;
}

/* harmony default export */ const lodash_es_isLength = (isLength);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsTypedArray.js




/** `Object#toString` result references. */
var _baseIsTypedArray_argsTag = '[object Arguments]',
    arrayTag = '[object Array]',
    boolTag = '[object Boolean]',
    dateTag = '[object Date]',
    errorTag = '[object Error]',
    _baseIsTypedArray_funcTag = '[object Function]',
    mapTag = '[object Map]',
    numberTag = '[object Number]',
    _baseIsTypedArray_objectTag = '[object Object]',
    regexpTag = '[object RegExp]',
    setTag = '[object Set]',
    stringTag = '[object String]',
    weakMapTag = '[object WeakMap]';

var arrayBufferTag = '[object ArrayBuffer]',
    dataViewTag = '[object DataView]',
    float32Tag = '[object Float32Array]',
    float64Tag = '[object Float64Array]',
    int8Tag = '[object Int8Array]',
    int16Tag = '[object Int16Array]',
    int32Tag = '[object Int32Array]',
    uint8Tag = '[object Uint8Array]',
    uint8ClampedTag = '[object Uint8ClampedArray]',
    uint16Tag = '[object Uint16Array]',
    uint32Tag = '[object Uint32Array]';

/** Used to identify `toStringTag` values of typed arrays. */
var typedArrayTags = {};
typedArrayTags[float32Tag] = typedArrayTags[float64Tag] =
typedArrayTags[int8Tag] = typedArrayTags[int16Tag] =
typedArrayTags[int32Tag] = typedArrayTags[uint8Tag] =
typedArrayTags[uint8ClampedTag] = typedArrayTags[uint16Tag] =
typedArrayTags[uint32Tag] = true;
typedArrayTags[_baseIsTypedArray_argsTag] = typedArrayTags[arrayTag] =
typedArrayTags[arrayBufferTag] = typedArrayTags[boolTag] =
typedArrayTags[dataViewTag] = typedArrayTags[dateTag] =
typedArrayTags[errorTag] = typedArrayTags[_baseIsTypedArray_funcTag] =
typedArrayTags[mapTag] = typedArrayTags[numberTag] =
typedArrayTags[_baseIsTypedArray_objectTag] = typedArrayTags[regexpTag] =
typedArrayTags[setTag] = typedArrayTags[stringTag] =
typedArrayTags[weakMapTag] = false;

/**
 * The base implementation of `_.isTypedArray` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 */
function baseIsTypedArray(value) {
  return lodash_es_isObjectLike(value) &&
    lodash_es_isLength(value.length) && !!typedArrayTags[_baseGetTag(value)];
}

/* harmony default export */ const _baseIsTypedArray = (baseIsTypedArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseUnary.js
/**
 * The base implementation of `_.unary` without support for storing metadata.
 *
 * @private
 * @param {Function} func The function to cap arguments for.
 * @returns {Function} Returns the new capped function.
 */
function baseUnary(func) {
  return function(value) {
    return func(value);
  };
}

/* harmony default export */ const _baseUnary = (baseUnary);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_nodeUtil.js


/** Detect free variable `exports`. */
var _nodeUtil_freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var _nodeUtil_freeModule = _nodeUtil_freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var _nodeUtil_moduleExports = _nodeUtil_freeModule && _nodeUtil_freeModule.exports === _nodeUtil_freeExports;

/** Detect free variable `process` from Node.js. */
var freeProcess = _nodeUtil_moduleExports && _freeGlobal.process;

/** Used to access faster Node.js helpers. */
var nodeUtil = (function() {
  try {
    // Use `util.types` for Node.js 10+.
    var types = _nodeUtil_freeModule && _nodeUtil_freeModule.require && _nodeUtil_freeModule.require('util').types;

    if (types) {
      return types;
    }

    // Legacy `process.binding('util')` for Node.js < 10.
    return freeProcess && freeProcess.binding && freeProcess.binding('util');
  } catch (e) {}
}());

/* harmony default export */ const _nodeUtil = (nodeUtil);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isTypedArray.js




/* Node.js helper references. */
var nodeIsTypedArray = _nodeUtil && _nodeUtil.isTypedArray;

/**
 * Checks if `value` is classified as a typed array.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a typed array, else `false`.
 * @example
 *
 * _.isTypedArray(new Uint8Array);
 * // => true
 *
 * _.isTypedArray([]);
 * // => false
 */
var isTypedArray = nodeIsTypedArray ? _baseUnary(nodeIsTypedArray) : _baseIsTypedArray;

/* harmony default export */ const lodash_es_isTypedArray = (isTypedArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_arrayLikeKeys.js







/** Used for built-in method references. */
var _arrayLikeKeys_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _arrayLikeKeys_hasOwnProperty = _arrayLikeKeys_objectProto.hasOwnProperty;

/**
 * Creates an array of the enumerable property names of the array-like `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @param {boolean} inherited Specify returning inherited property names.
 * @returns {Array} Returns the array of property names.
 */
function arrayLikeKeys(value, inherited) {
  var isArr = lodash_es_isArray(value),
      isArg = !isArr && lodash_es_isArguments(value),
      isBuff = !isArr && !isArg && lodash_es_isBuffer(value),
      isType = !isArr && !isArg && !isBuff && lodash_es_isTypedArray(value),
      skipIndexes = isArr || isArg || isBuff || isType,
      result = skipIndexes ? _baseTimes(value.length, String) : [],
      length = result.length;

  for (var key in value) {
    if ((inherited || _arrayLikeKeys_hasOwnProperty.call(value, key)) &&
        !(skipIndexes && (
           // Safari 9 has enumerable `arguments.length` in strict mode.
           key == 'length' ||
           // Node.js 0.10 has enumerable non-index properties on buffers.
           (isBuff && (key == 'offset' || key == 'parent')) ||
           // PhantomJS 2 has enumerable non-index properties on typed arrays.
           (isType && (key == 'buffer' || key == 'byteLength' || key == 'byteOffset')) ||
           // Skip index properties.
           _isIndex(key, length)
        ))) {
      result.push(key);
    }
  }
  return result;
}

/* harmony default export */ const _arrayLikeKeys = (arrayLikeKeys);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_isPrototype.js
/** Used for built-in method references. */
var _isPrototype_objectProto = Object.prototype;

/**
 * Checks if `value` is likely a prototype object.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a prototype, else `false`.
 */
function isPrototype(value) {
  var Ctor = value && value.constructor,
      proto = (typeof Ctor == 'function' && Ctor.prototype) || _isPrototype_objectProto;

  return value === proto;
}

/* harmony default export */ const _isPrototype = (isPrototype);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_nativeKeys.js


/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeKeys = _overArg(Object.keys, Object);

/* harmony default export */ const _nativeKeys = (nativeKeys);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseKeys.js



/** Used for built-in method references. */
var _baseKeys_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _baseKeys_hasOwnProperty = _baseKeys_objectProto.hasOwnProperty;

/**
 * The base implementation of `_.keys` which doesn't treat sparse arrays as dense.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function baseKeys(object) {
  if (!_isPrototype(object)) {
    return _nativeKeys(object);
  }
  var result = [];
  for (var key in Object(object)) {
    if (_baseKeys_hasOwnProperty.call(object, key) && key != 'constructor') {
      result.push(key);
    }
  }
  return result;
}

/* harmony default export */ const _baseKeys = (baseKeys);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isArrayLike.js



/**
 * Checks if `value` is array-like. A value is considered array-like if it's
 * not a function and has a `value.length` that's an integer greater than or
 * equal to `0` and less than or equal to `Number.MAX_SAFE_INTEGER`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is array-like, else `false`.
 * @example
 *
 * _.isArrayLike([1, 2, 3]);
 * // => true
 *
 * _.isArrayLike(document.body.children);
 * // => true
 *
 * _.isArrayLike('abc');
 * // => true
 *
 * _.isArrayLike(_.noop);
 * // => false
 */
function isArrayLike(value) {
  return value != null && lodash_es_isLength(value.length) && !lodash_es_isFunction(value);
}

/* harmony default export */ const lodash_es_isArrayLike = (isArrayLike);

;// CONCATENATED MODULE: ./node_modules/lodash-es/keys.js




/**
 * Creates an array of the own enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects. See the
 * [ES spec](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * for more details.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keys(new Foo);
 * // => ['a', 'b'] (iteration order is not guaranteed)
 *
 * _.keys('hi');
 * // => ['0', '1']
 */
function keys(object) {
  return lodash_es_isArrayLike(object) ? _arrayLikeKeys(object) : _baseKeys(object);
}

/* harmony default export */ const lodash_es_keys = (keys);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseAssign.js



/**
 * The base implementation of `_.assign` without support for multiple sources
 * or `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @returns {Object} Returns `object`.
 */
function baseAssign(object, source) {
  return object && _copyObject(source, lodash_es_keys(source), object);
}

/* harmony default export */ const _baseAssign = (baseAssign);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_nativeKeysIn.js
/**
 * This function is like
 * [`Object.keys`](http://ecma-international.org/ecma-262/7.0/#sec-object.keys)
 * except that it includes inherited enumerable properties.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function nativeKeysIn(object) {
  var result = [];
  if (object != null) {
    for (var key in Object(object)) {
      result.push(key);
    }
  }
  return result;
}

/* harmony default export */ const _nativeKeysIn = (nativeKeysIn);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseKeysIn.js




/** Used for built-in method references. */
var _baseKeysIn_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _baseKeysIn_hasOwnProperty = _baseKeysIn_objectProto.hasOwnProperty;

/**
 * The base implementation of `_.keysIn` which doesn't treat sparse arrays as dense.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 */
function baseKeysIn(object) {
  if (!lodash_es_isObject(object)) {
    return _nativeKeysIn(object);
  }
  var isProto = _isPrototype(object),
      result = [];

  for (var key in object) {
    if (!(key == 'constructor' && (isProto || !_baseKeysIn_hasOwnProperty.call(object, key)))) {
      result.push(key);
    }
  }
  return result;
}

/* harmony default export */ const _baseKeysIn = (baseKeysIn);

;// CONCATENATED MODULE: ./node_modules/lodash-es/keysIn.js




/**
 * Creates an array of the own and inherited enumerable property names of `object`.
 *
 * **Note:** Non-object values are coerced to objects.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Object
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names.
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.keysIn(new Foo);
 * // => ['a', 'b', 'c'] (iteration order is not guaranteed)
 */
function keysIn(object) {
  return lodash_es_isArrayLike(object) ? _arrayLikeKeys(object, true) : _baseKeysIn(object);
}

/* harmony default export */ const lodash_es_keysIn = (keysIn);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseAssignIn.js



/**
 * The base implementation of `_.assignIn` without support for multiple sources
 * or `customizer` functions.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @returns {Object} Returns `object`.
 */
function baseAssignIn(object, source) {
  return object && _copyObject(source, lodash_es_keysIn(source), object);
}

/* harmony default export */ const _baseAssignIn = (baseAssignIn);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_cloneBuffer.js


/** Detect free variable `exports`. */
var _cloneBuffer_freeExports = typeof exports == 'object' && exports && !exports.nodeType && exports;

/** Detect free variable `module`. */
var _cloneBuffer_freeModule = _cloneBuffer_freeExports && typeof module == 'object' && module && !module.nodeType && module;

/** Detect the popular CommonJS extension `module.exports`. */
var _cloneBuffer_moduleExports = _cloneBuffer_freeModule && _cloneBuffer_freeModule.exports === _cloneBuffer_freeExports;

/** Built-in value references. */
var _cloneBuffer_Buffer = _cloneBuffer_moduleExports ? _root.Buffer : undefined,
    allocUnsafe = _cloneBuffer_Buffer ? _cloneBuffer_Buffer.allocUnsafe : undefined;

/**
 * Creates a clone of  `buffer`.
 *
 * @private
 * @param {Buffer} buffer The buffer to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Buffer} Returns the cloned buffer.
 */
function cloneBuffer(buffer, isDeep) {
  if (isDeep) {
    return buffer.slice();
  }
  var length = buffer.length,
      result = allocUnsafe ? allocUnsafe(length) : new buffer.constructor(length);

  buffer.copy(result);
  return result;
}

/* harmony default export */ const _cloneBuffer = (cloneBuffer);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_copyArray.js
/**
 * Copies the values of `source` to `array`.
 *
 * @private
 * @param {Array} source The array to copy values from.
 * @param {Array} [array=[]] The array to copy values to.
 * @returns {Array} Returns `array`.
 */
function copyArray(source, array) {
  var index = -1,
      length = source.length;

  array || (array = Array(length));
  while (++index < length) {
    array[index] = source[index];
  }
  return array;
}

/* harmony default export */ const _copyArray = (copyArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_arrayFilter.js
/**
 * A specialized version of `_.filter` for arrays without support for
 * iteratee shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} predicate The function invoked per iteration.
 * @returns {Array} Returns the new filtered array.
 */
function arrayFilter(array, predicate) {
  var index = -1,
      length = array == null ? 0 : array.length,
      resIndex = 0,
      result = [];

  while (++index < length) {
    var value = array[index];
    if (predicate(value, index, array)) {
      result[resIndex++] = value;
    }
  }
  return result;
}

/* harmony default export */ const _arrayFilter = (arrayFilter);

;// CONCATENATED MODULE: ./node_modules/lodash-es/stubArray.js
/**
 * This method returns a new empty array.
 *
 * @static
 * @memberOf _
 * @since 4.13.0
 * @category Util
 * @returns {Array} Returns the new empty array.
 * @example
 *
 * var arrays = _.times(2, _.stubArray);
 *
 * console.log(arrays);
 * // => [[], []]
 *
 * console.log(arrays[0] === arrays[1]);
 * // => false
 */
function stubArray() {
  return [];
}

/* harmony default export */ const lodash_es_stubArray = (stubArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getSymbols.js



/** Used for built-in method references. */
var _getSymbols_objectProto = Object.prototype;

/** Built-in value references. */
var _getSymbols_propertyIsEnumerable = _getSymbols_objectProto.propertyIsEnumerable;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeGetSymbols = Object.getOwnPropertySymbols;

/**
 * Creates an array of the own enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbols = !nativeGetSymbols ? lodash_es_stubArray : function(object) {
  if (object == null) {
    return [];
  }
  object = Object(object);
  return _arrayFilter(nativeGetSymbols(object), function(symbol) {
    return _getSymbols_propertyIsEnumerable.call(object, symbol);
  });
};

/* harmony default export */ const _getSymbols = (getSymbols);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_copySymbols.js



/**
 * Copies own symbols of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy symbols from.
 * @param {Object} [object={}] The object to copy symbols to.
 * @returns {Object} Returns `object`.
 */
function copySymbols(source, object) {
  return _copyObject(source, _getSymbols(source), object);
}

/* harmony default export */ const _copySymbols = (copySymbols);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_arrayPush.js
/**
 * Appends the elements of `values` to `array`.
 *
 * @private
 * @param {Array} array The array to modify.
 * @param {Array} values The values to append.
 * @returns {Array} Returns `array`.
 */
function arrayPush(array, values) {
  var index = -1,
      length = values.length,
      offset = array.length;

  while (++index < length) {
    array[offset + index] = values[index];
  }
  return array;
}

/* harmony default export */ const _arrayPush = (arrayPush);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getSymbolsIn.js





/* Built-in method references for those with the same name as other `lodash` methods. */
var _getSymbolsIn_nativeGetSymbols = Object.getOwnPropertySymbols;

/**
 * Creates an array of the own and inherited enumerable symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of symbols.
 */
var getSymbolsIn = !_getSymbolsIn_nativeGetSymbols ? lodash_es_stubArray : function(object) {
  var result = [];
  while (object) {
    _arrayPush(result, _getSymbols(object));
    object = _getPrototype(object);
  }
  return result;
};

/* harmony default export */ const _getSymbolsIn = (getSymbolsIn);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_copySymbolsIn.js



/**
 * Copies own and inherited symbols of `source` to `object`.
 *
 * @private
 * @param {Object} source The object to copy symbols from.
 * @param {Object} [object={}] The object to copy symbols to.
 * @returns {Object} Returns `object`.
 */
function copySymbolsIn(source, object) {
  return _copyObject(source, _getSymbolsIn(source), object);
}

/* harmony default export */ const _copySymbolsIn = (copySymbolsIn);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseGetAllKeys.js



/**
 * The base implementation of `getAllKeys` and `getAllKeysIn` which uses
 * `keysFunc` and `symbolsFunc` to get the enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @param {Function} symbolsFunc The function to get the symbols of `object`.
 * @returns {Array} Returns the array of property names and symbols.
 */
function baseGetAllKeys(object, keysFunc, symbolsFunc) {
  var result = keysFunc(object);
  return lodash_es_isArray(object) ? result : _arrayPush(result, symbolsFunc(object));
}

/* harmony default export */ const _baseGetAllKeys = (baseGetAllKeys);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getAllKeys.js




/**
 * Creates an array of own enumerable property names and symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeys(object) {
  return _baseGetAllKeys(object, lodash_es_keys, _getSymbols);
}

/* harmony default export */ const _getAllKeys = (getAllKeys);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getAllKeysIn.js




/**
 * Creates an array of own and inherited enumerable property names and
 * symbols of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @returns {Array} Returns the array of property names and symbols.
 */
function getAllKeysIn(object) {
  return _baseGetAllKeys(object, lodash_es_keysIn, _getSymbolsIn);
}

/* harmony default export */ const _getAllKeysIn = (getAllKeysIn);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_DataView.js



/* Built-in method references that are verified to be native. */
var DataView = _getNative(_root, 'DataView');

/* harmony default export */ const _DataView = (DataView);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_Promise.js



/* Built-in method references that are verified to be native. */
var _Promise_Promise = _getNative(_root, 'Promise');

/* harmony default export */ const _Promise = (_Promise_Promise);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_Set.js



/* Built-in method references that are verified to be native. */
var _Set_Set = _getNative(_root, 'Set');

/* harmony default export */ const _Set = (_Set_Set);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_WeakMap.js



/* Built-in method references that are verified to be native. */
var _WeakMap_WeakMap = _getNative(_root, 'WeakMap');

/* harmony default export */ const _WeakMap = (_WeakMap_WeakMap);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_getTag.js








/** `Object#toString` result references. */
var _getTag_mapTag = '[object Map]',
    _getTag_objectTag = '[object Object]',
    promiseTag = '[object Promise]',
    _getTag_setTag = '[object Set]',
    _getTag_weakMapTag = '[object WeakMap]';

var _getTag_dataViewTag = '[object DataView]';

/** Used to detect maps, sets, and weakmaps. */
var dataViewCtorString = _toSource(_DataView),
    mapCtorString = _toSource(_Map),
    promiseCtorString = _toSource(_Promise),
    setCtorString = _toSource(_Set),
    weakMapCtorString = _toSource(_WeakMap);

/**
 * Gets the `toStringTag` of `value`.
 *
 * @private
 * @param {*} value The value to query.
 * @returns {string} Returns the `toStringTag`.
 */
var getTag = _baseGetTag;

// Fallback for data views, maps, sets, and weak maps in IE 11 and promises in Node.js < 6.
if ((_DataView && getTag(new _DataView(new ArrayBuffer(1))) != _getTag_dataViewTag) ||
    (_Map && getTag(new _Map) != _getTag_mapTag) ||
    (_Promise && getTag(_Promise.resolve()) != promiseTag) ||
    (_Set && getTag(new _Set) != _getTag_setTag) ||
    (_WeakMap && getTag(new _WeakMap) != _getTag_weakMapTag)) {
  getTag = function(value) {
    var result = _baseGetTag(value),
        Ctor = result == _getTag_objectTag ? value.constructor : undefined,
        ctorString = Ctor ? _toSource(Ctor) : '';

    if (ctorString) {
      switch (ctorString) {
        case dataViewCtorString: return _getTag_dataViewTag;
        case mapCtorString: return _getTag_mapTag;
        case promiseCtorString: return promiseTag;
        case setCtorString: return _getTag_setTag;
        case weakMapCtorString: return _getTag_weakMapTag;
      }
    }
    return result;
  };
}

/* harmony default export */ const _getTag = (getTag);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_initCloneArray.js
/** Used for built-in method references. */
var _initCloneArray_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _initCloneArray_hasOwnProperty = _initCloneArray_objectProto.hasOwnProperty;

/**
 * Initializes an array clone.
 *
 * @private
 * @param {Array} array The array to clone.
 * @returns {Array} Returns the initialized clone.
 */
function initCloneArray(array) {
  var length = array.length,
      result = new array.constructor(length);

  // Add properties assigned by `RegExp#exec`.
  if (length && typeof array[0] == 'string' && _initCloneArray_hasOwnProperty.call(array, 'index')) {
    result.index = array.index;
    result.input = array.input;
  }
  return result;
}

/* harmony default export */ const _initCloneArray = (initCloneArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_Uint8Array.js


/** Built-in value references. */
var Uint8Array = _root.Uint8Array;

/* harmony default export */ const _Uint8Array = (Uint8Array);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_cloneArrayBuffer.js


/**
 * Creates a clone of `arrayBuffer`.
 *
 * @private
 * @param {ArrayBuffer} arrayBuffer The array buffer to clone.
 * @returns {ArrayBuffer} Returns the cloned array buffer.
 */
function cloneArrayBuffer(arrayBuffer) {
  var result = new arrayBuffer.constructor(arrayBuffer.byteLength);
  new _Uint8Array(result).set(new _Uint8Array(arrayBuffer));
  return result;
}

/* harmony default export */ const _cloneArrayBuffer = (cloneArrayBuffer);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_cloneDataView.js


/**
 * Creates a clone of `dataView`.
 *
 * @private
 * @param {Object} dataView The data view to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned data view.
 */
function cloneDataView(dataView, isDeep) {
  var buffer = isDeep ? _cloneArrayBuffer(dataView.buffer) : dataView.buffer;
  return new dataView.constructor(buffer, dataView.byteOffset, dataView.byteLength);
}

/* harmony default export */ const _cloneDataView = (cloneDataView);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_cloneRegExp.js
/** Used to match `RegExp` flags from their coerced string values. */
var reFlags = /\w*$/;

/**
 * Creates a clone of `regexp`.
 *
 * @private
 * @param {Object} regexp The regexp to clone.
 * @returns {Object} Returns the cloned regexp.
 */
function cloneRegExp(regexp) {
  var result = new regexp.constructor(regexp.source, reFlags.exec(regexp));
  result.lastIndex = regexp.lastIndex;
  return result;
}

/* harmony default export */ const _cloneRegExp = (cloneRegExp);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_cloneSymbol.js


/** Used to convert symbols to primitives and strings. */
var symbolProto = _Symbol ? _Symbol.prototype : undefined,
    symbolValueOf = symbolProto ? symbolProto.valueOf : undefined;

/**
 * Creates a clone of the `symbol` object.
 *
 * @private
 * @param {Object} symbol The symbol object to clone.
 * @returns {Object} Returns the cloned symbol object.
 */
function cloneSymbol(symbol) {
  return symbolValueOf ? Object(symbolValueOf.call(symbol)) : {};
}

/* harmony default export */ const _cloneSymbol = (cloneSymbol);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_cloneTypedArray.js


/**
 * Creates a clone of `typedArray`.
 *
 * @private
 * @param {Object} typedArray The typed array to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the cloned typed array.
 */
function cloneTypedArray(typedArray, isDeep) {
  var buffer = isDeep ? _cloneArrayBuffer(typedArray.buffer) : typedArray.buffer;
  return new typedArray.constructor(buffer, typedArray.byteOffset, typedArray.length);
}

/* harmony default export */ const _cloneTypedArray = (cloneTypedArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_initCloneByTag.js






/** `Object#toString` result references. */
var _initCloneByTag_boolTag = '[object Boolean]',
    _initCloneByTag_dateTag = '[object Date]',
    _initCloneByTag_mapTag = '[object Map]',
    _initCloneByTag_numberTag = '[object Number]',
    _initCloneByTag_regexpTag = '[object RegExp]',
    _initCloneByTag_setTag = '[object Set]',
    _initCloneByTag_stringTag = '[object String]',
    symbolTag = '[object Symbol]';

var _initCloneByTag_arrayBufferTag = '[object ArrayBuffer]',
    _initCloneByTag_dataViewTag = '[object DataView]',
    _initCloneByTag_float32Tag = '[object Float32Array]',
    _initCloneByTag_float64Tag = '[object Float64Array]',
    _initCloneByTag_int8Tag = '[object Int8Array]',
    _initCloneByTag_int16Tag = '[object Int16Array]',
    _initCloneByTag_int32Tag = '[object Int32Array]',
    _initCloneByTag_uint8Tag = '[object Uint8Array]',
    _initCloneByTag_uint8ClampedTag = '[object Uint8ClampedArray]',
    _initCloneByTag_uint16Tag = '[object Uint16Array]',
    _initCloneByTag_uint32Tag = '[object Uint32Array]';

/**
 * Initializes an object clone based on its `toStringTag`.
 *
 * **Note:** This function only supports cloning values with tags of
 * `Boolean`, `Date`, `Error`, `Map`, `Number`, `RegExp`, `Set`, or `String`.
 *
 * @private
 * @param {Object} object The object to clone.
 * @param {string} tag The `toStringTag` of the object to clone.
 * @param {boolean} [isDeep] Specify a deep clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneByTag(object, tag, isDeep) {
  var Ctor = object.constructor;
  switch (tag) {
    case _initCloneByTag_arrayBufferTag:
      return _cloneArrayBuffer(object);

    case _initCloneByTag_boolTag:
    case _initCloneByTag_dateTag:
      return new Ctor(+object);

    case _initCloneByTag_dataViewTag:
      return _cloneDataView(object, isDeep);

    case _initCloneByTag_float32Tag: case _initCloneByTag_float64Tag:
    case _initCloneByTag_int8Tag: case _initCloneByTag_int16Tag: case _initCloneByTag_int32Tag:
    case _initCloneByTag_uint8Tag: case _initCloneByTag_uint8ClampedTag: case _initCloneByTag_uint16Tag: case _initCloneByTag_uint32Tag:
      return _cloneTypedArray(object, isDeep);

    case _initCloneByTag_mapTag:
      return new Ctor;

    case _initCloneByTag_numberTag:
    case _initCloneByTag_stringTag:
      return new Ctor(object);

    case _initCloneByTag_regexpTag:
      return _cloneRegExp(object);

    case _initCloneByTag_setTag:
      return new Ctor;

    case symbolTag:
      return _cloneSymbol(object);
  }
}

/* harmony default export */ const _initCloneByTag = (initCloneByTag);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseCreate.js


/** Built-in value references. */
var objectCreate = Object.create;

/**
 * The base implementation of `_.create` without support for assigning
 * properties to the created object.
 *
 * @private
 * @param {Object} proto The object to inherit from.
 * @returns {Object} Returns the new object.
 */
var baseCreate = (function() {
  function object() {}
  return function(proto) {
    if (!lodash_es_isObject(proto)) {
      return {};
    }
    if (objectCreate) {
      return objectCreate(proto);
    }
    object.prototype = proto;
    var result = new object;
    object.prototype = undefined;
    return result;
  };
}());

/* harmony default export */ const _baseCreate = (baseCreate);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_initCloneObject.js




/**
 * Initializes an object clone.
 *
 * @private
 * @param {Object} object The object to clone.
 * @returns {Object} Returns the initialized clone.
 */
function initCloneObject(object) {
  return (typeof object.constructor == 'function' && !_isPrototype(object))
    ? _baseCreate(_getPrototype(object))
    : {};
}

/* harmony default export */ const _initCloneObject = (initCloneObject);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsMap.js



/** `Object#toString` result references. */
var _baseIsMap_mapTag = '[object Map]';

/**
 * The base implementation of `_.isMap` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a map, else `false`.
 */
function baseIsMap(value) {
  return lodash_es_isObjectLike(value) && _getTag(value) == _baseIsMap_mapTag;
}

/* harmony default export */ const _baseIsMap = (baseIsMap);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isMap.js




/* Node.js helper references. */
var nodeIsMap = _nodeUtil && _nodeUtil.isMap;

/**
 * Checks if `value` is classified as a `Map` object.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a map, else `false`.
 * @example
 *
 * _.isMap(new Map);
 * // => true
 *
 * _.isMap(new WeakMap);
 * // => false
 */
var isMap = nodeIsMap ? _baseUnary(nodeIsMap) : _baseIsMap;

/* harmony default export */ const lodash_es_isMap = (isMap);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsSet.js



/** `Object#toString` result references. */
var _baseIsSet_setTag = '[object Set]';

/**
 * The base implementation of `_.isSet` without Node.js optimizations.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a set, else `false`.
 */
function baseIsSet(value) {
  return lodash_es_isObjectLike(value) && _getTag(value) == _baseIsSet_setTag;
}

/* harmony default export */ const _baseIsSet = (baseIsSet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isSet.js




/* Node.js helper references. */
var nodeIsSet = _nodeUtil && _nodeUtil.isSet;

/**
 * Checks if `value` is classified as a `Set` object.
 *
 * @static
 * @memberOf _
 * @since 4.3.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a set, else `false`.
 * @example
 *
 * _.isSet(new Set);
 * // => true
 *
 * _.isSet(new WeakSet);
 * // => false
 */
var isSet = nodeIsSet ? _baseUnary(nodeIsSet) : _baseIsSet;

/* harmony default export */ const lodash_es_isSet = (isSet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseClone.js























/** Used to compose bitmasks for cloning. */
var CLONE_DEEP_FLAG = 1,
    CLONE_FLAT_FLAG = 2,
    CLONE_SYMBOLS_FLAG = 4;

/** `Object#toString` result references. */
var _baseClone_argsTag = '[object Arguments]',
    _baseClone_arrayTag = '[object Array]',
    _baseClone_boolTag = '[object Boolean]',
    _baseClone_dateTag = '[object Date]',
    _baseClone_errorTag = '[object Error]',
    _baseClone_funcTag = '[object Function]',
    _baseClone_genTag = '[object GeneratorFunction]',
    _baseClone_mapTag = '[object Map]',
    _baseClone_numberTag = '[object Number]',
    _baseClone_objectTag = '[object Object]',
    _baseClone_regexpTag = '[object RegExp]',
    _baseClone_setTag = '[object Set]',
    _baseClone_stringTag = '[object String]',
    _baseClone_symbolTag = '[object Symbol]',
    _baseClone_weakMapTag = '[object WeakMap]';

var _baseClone_arrayBufferTag = '[object ArrayBuffer]',
    _baseClone_dataViewTag = '[object DataView]',
    _baseClone_float32Tag = '[object Float32Array]',
    _baseClone_float64Tag = '[object Float64Array]',
    _baseClone_int8Tag = '[object Int8Array]',
    _baseClone_int16Tag = '[object Int16Array]',
    _baseClone_int32Tag = '[object Int32Array]',
    _baseClone_uint8Tag = '[object Uint8Array]',
    _baseClone_uint8ClampedTag = '[object Uint8ClampedArray]',
    _baseClone_uint16Tag = '[object Uint16Array]',
    _baseClone_uint32Tag = '[object Uint32Array]';

/** Used to identify `toStringTag` values supported by `_.clone`. */
var cloneableTags = {};
cloneableTags[_baseClone_argsTag] = cloneableTags[_baseClone_arrayTag] =
cloneableTags[_baseClone_arrayBufferTag] = cloneableTags[_baseClone_dataViewTag] =
cloneableTags[_baseClone_boolTag] = cloneableTags[_baseClone_dateTag] =
cloneableTags[_baseClone_float32Tag] = cloneableTags[_baseClone_float64Tag] =
cloneableTags[_baseClone_int8Tag] = cloneableTags[_baseClone_int16Tag] =
cloneableTags[_baseClone_int32Tag] = cloneableTags[_baseClone_mapTag] =
cloneableTags[_baseClone_numberTag] = cloneableTags[_baseClone_objectTag] =
cloneableTags[_baseClone_regexpTag] = cloneableTags[_baseClone_setTag] =
cloneableTags[_baseClone_stringTag] = cloneableTags[_baseClone_symbolTag] =
cloneableTags[_baseClone_uint8Tag] = cloneableTags[_baseClone_uint8ClampedTag] =
cloneableTags[_baseClone_uint16Tag] = cloneableTags[_baseClone_uint32Tag] = true;
cloneableTags[_baseClone_errorTag] = cloneableTags[_baseClone_funcTag] =
cloneableTags[_baseClone_weakMapTag] = false;

/**
 * The base implementation of `_.clone` and `_.cloneDeep` which tracks
 * traversed objects.
 *
 * @private
 * @param {*} value The value to clone.
 * @param {boolean} bitmask The bitmask flags.
 *  1 - Deep clone
 *  2 - Flatten inherited properties
 *  4 - Clone symbols
 * @param {Function} [customizer] The function to customize cloning.
 * @param {string} [key] The key of `value`.
 * @param {Object} [object] The parent object of `value`.
 * @param {Object} [stack] Tracks traversed objects and their clone counterparts.
 * @returns {*} Returns the cloned value.
 */
function baseClone(value, bitmask, customizer, key, object, stack) {
  var result,
      isDeep = bitmask & CLONE_DEEP_FLAG,
      isFlat = bitmask & CLONE_FLAT_FLAG,
      isFull = bitmask & CLONE_SYMBOLS_FLAG;

  if (customizer) {
    result = object ? customizer(value, key, object, stack) : customizer(value);
  }
  if (result !== undefined) {
    return result;
  }
  if (!lodash_es_isObject(value)) {
    return value;
  }
  var isArr = lodash_es_isArray(value);
  if (isArr) {
    result = _initCloneArray(value);
    if (!isDeep) {
      return _copyArray(value, result);
    }
  } else {
    var tag = _getTag(value),
        isFunc = tag == _baseClone_funcTag || tag == _baseClone_genTag;

    if (lodash_es_isBuffer(value)) {
      return _cloneBuffer(value, isDeep);
    }
    if (tag == _baseClone_objectTag || tag == _baseClone_argsTag || (isFunc && !object)) {
      result = (isFlat || isFunc) ? {} : _initCloneObject(value);
      if (!isDeep) {
        return isFlat
          ? _copySymbolsIn(value, _baseAssignIn(result, value))
          : _copySymbols(value, _baseAssign(result, value));
      }
    } else {
      if (!cloneableTags[tag]) {
        return object ? value : {};
      }
      result = _initCloneByTag(value, tag, isDeep);
    }
  }
  // Check for circular references and return its corresponding clone.
  stack || (stack = new _Stack);
  var stacked = stack.get(value);
  if (stacked) {
    return stacked;
  }
  stack.set(value, result);

  if (lodash_es_isSet(value)) {
    value.forEach(function(subValue) {
      result.add(baseClone(subValue, bitmask, customizer, subValue, value, stack));
    });
  } else if (lodash_es_isMap(value)) {
    value.forEach(function(subValue, key) {
      result.set(key, baseClone(subValue, bitmask, customizer, key, value, stack));
    });
  }

  var keysFunc = isFull
    ? (isFlat ? _getAllKeysIn : _getAllKeys)
    : (isFlat ? lodash_es_keysIn : lodash_es_keys);

  var props = isArr ? undefined : keysFunc(value);
  _arrayEach(props || value, function(subValue, key) {
    if (props) {
      key = subValue;
      subValue = value[key];
    }
    // Recursively populate clone (susceptible to call stack limits).
    _assignValue(result, key, baseClone(subValue, bitmask, customizer, key, value, stack));
  });
  return result;
}

/* harmony default export */ const _baseClone = (baseClone);

;// CONCATENATED MODULE: ./node_modules/lodash-es/cloneDeepWith.js


/** Used to compose bitmasks for cloning. */
var cloneDeepWith_CLONE_DEEP_FLAG = 1,
    cloneDeepWith_CLONE_SYMBOLS_FLAG = 4;

/**
 * This method is like `_.cloneWith` except that it recursively clones `value`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to recursively clone.
 * @param {Function} [customizer] The function to customize cloning.
 * @returns {*} Returns the deep cloned value.
 * @see _.cloneWith
 * @example
 *
 * function customizer(value) {
 *   if (_.isElement(value)) {
 *     return value.cloneNode(true);
 *   }
 * }
 *
 * var el = _.cloneDeepWith(document.body, customizer);
 *
 * console.log(el === document.body);
 * // => false
 * console.log(el.nodeName);
 * // => 'BODY'
 * console.log(el.childNodes.length);
 * // => 20
 */
function cloneDeepWith(value, customizer) {
  customizer = typeof customizer == 'function' ? customizer : undefined;
  return _baseClone(value, cloneDeepWith_CLONE_DEEP_FLAG | cloneDeepWith_CLONE_SYMBOLS_FLAG, customizer);
}

/* harmony default export */ const lodash_es_cloneDeepWith = (cloneDeepWith);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isElement.js



/**
 * Checks if `value` is likely a DOM element.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a DOM element, else `false`.
 * @example
 *
 * _.isElement(document.body);
 * // => true
 *
 * _.isElement('<body>');
 * // => false
 */
function isElement(value) {
  return lodash_es_isObjectLike(value) && value.nodeType === 1 && !lodash_es_isPlainObject(value);
}

/* harmony default export */ const lodash_es_isElement = (isElement);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/config.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/config
 */

/**
 * Handles a configuration dictionary.
 */
class Config {
    /**
     * Creates an instance of the {@link ~Config} class.
     *
     * @param {Object} [configurations] The initial configurations to be set. Usually, provided by the user.
     * @param {Object} [defaultConfigurations] The default configurations. Usually, provided by the system.
     */
    constructor(configurations, defaultConfigurations) {
        /**
         * Store for the whole configuration.
         *
         * @private
         * @member {Object}
         */
        this._config = {};
        // Set default configuration.
        if (defaultConfigurations) {
            // Clone the configuration to make sure that the properties will not be shared
            // between editors and make the watchdog feature work correctly.
            this.define(cloneConfig(defaultConfigurations));
        }
        // Set initial configuration.
        if (configurations) {
            this._setObjectToTarget(this._config, configurations);
        }
    }
    /**
     * Set configuration values.
     *
     * It accepts both a name/value pair or an object, which properties and values will be used to set
     * configurations.
     *
     * It also accepts setting a "deep configuration" by using dots in the name. For example, `'resize.width'` sets
     * the value for the `width` configuration in the `resize` subset.
     *
     *		config.set( 'width', 500 );
     *		config.set( 'toolbar.collapsed', true );
     *
     *		// Equivalent to:
     *		config.set( {
     *			width: 500
     *			toolbar: {
     *				collapsed: true
     *			}
     *		} );
     *
     * Passing an object as the value will amend the configuration, not replace it.
     *
     *		config.set( 'toolbar', {
     *			collapsed: true,
     *		} );
     *
     *		config.set( 'toolbar', {
     *			color: 'red',
     *		} );
     *
     *		config.get( 'toolbar.collapsed' ); // true
     *		config.get( 'toolbar.color' ); // 'red'
     *
     * @param {String|Object} name The configuration name or an object from which take properties as
     * configuration entries. Configuration names are case-sensitive.
     * @param {*} value The configuration value. Used if a name is passed.
     */
    set(name, value) {
        this._setToTarget(this._config, name, value);
    }
    /**
     * Does exactly the same as {@link #set} with one exception – passed configuration extends
     * existing one, but does not overwrite already defined values.
     *
     * This method is supposed to be called by plugin developers to setup plugin's configurations. It would be
     * rarely used for other needs.
     *
     * @param {String|Object} name The configuration name or an object from which take properties as
     * configuration entries. Configuration names are case-sensitive.
     * @param {*} value The configuration value. Used if a name is passed.
     */
    define(name, value) {
        const isDefine = true;
        this._setToTarget(this._config, name, value, isDefine);
    }
    /**
     * Gets the value for a configuration entry.
     *
     *		config.get( 'name' );
     *
     * Deep configurations can be retrieved by separating each part with a dot.
     *
     *		config.get( 'toolbar.collapsed' );
     *
     * @param {String} name The configuration name. Configuration names are case-sensitive.
     * @returns {*} The configuration value or `undefined` if the configuration entry was not found.
     */
    get(name) {
        return this._getFromSource(this._config, name);
    }
    /**
     * Iterates over all top level configuration names.
     *
     * @returns {Iterable.<String>}
     */
    *names() {
        for (const name of Object.keys(this._config)) {
            yield name;
        }
    }
    /**
     * Saves passed configuration to the specified target (nested object).
     *
     * @private
     * @param {Object} target Nested config object.
     * @param {String|Object} name The configuration name or an object from which take properties as
     * configuration entries. Configuration names are case-sensitive.
     * @param {*} value The configuration value. Used if a name is passed.
     * @param {Boolean} [isDefine=false] Define if passed configuration should overwrite existing one.
     */
    _setToTarget(target, name, value, isDefine = false) {
        // In case of an object, iterate through it and call `_setToTarget` again for each property.
        if (lodash_es_isPlainObject(name)) {
            this._setObjectToTarget(target, name, isDefine);
            return;
        }
        // The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`].
        const parts = name.split('.');
        // Take the name of the configuration out of the parts. E.g. `resize.width` -> `width`.
        name = parts.pop();
        // Iterate over parts to check if currently stored configuration has proper structure.
        for (const part of parts) {
            // If there is no object for specified part then create one.
            if (!lodash_es_isPlainObject(target[part])) {
                target[part] = {};
            }
            // Nested object becomes a target.
            target = target[part];
        }
        // In case of value is an object.
        if (lodash_es_isPlainObject(value)) {
            // We take care of proper config structure.
            if (!lodash_es_isPlainObject(target[name])) {
                target[name] = {};
            }
            target = target[name];
            // And iterate through this object calling `_setToTarget` again for each property.
            this._setObjectToTarget(target, value, isDefine);
            return;
        }
        // Do nothing if we are defining configuration for non empty name.
        if (isDefine && typeof target[name] != 'undefined') {
            return;
        }
        target[name] = value;
    }
    /**
     * Get specified configuration from specified source (nested object).
     *
     * @private
     * @param {Object} source level of nested object.
     * @param {String} name The configuration name. Configuration names are case-sensitive.
     * @returns {*} The configuration value or `undefined` if the configuration entry was not found.
     */
    _getFromSource(source, name) {
        // The configuration name should be split into parts if it has dots. E.g. `resize.width` -> [`resize`, `width`].
        const parts = name.split('.');
        // Take the name of the configuration out of the parts. E.g. `resize.width` -> `width`.
        name = parts.pop();
        // Iterate over parts to check if currently stored configuration has proper structure.
        for (const part of parts) {
            if (!lodash_es_isPlainObject(source[part])) {
                source = null;
                break;
            }
            // Nested object becomes a source.
            source = source[part];
        }
        // Always returns undefined for non existing configuration.
        return source ? cloneConfig(source[name]) : undefined;
    }
    /**
     * Iterates through passed object and calls {@link #_setToTarget} method with object key and value for each property.
     *
     * @private
     * @param {Object} target Nested config object.
     * @param {Object} configuration Configuration data set
     * @param {Boolean} [isDefine] Defines if passed configuration is default configuration or not.
     */
    _setObjectToTarget(target, configuration, isDefine) {
        Object.keys(configuration).forEach(key => {
            this._setToTarget(target, key, configuration[key], isDefine);
        });
    }
}
// Clones configuration object or value.
// @param {*} source Source configuration
// @returns {*} Cloned configuration value.
function cloneConfig(source) {
    return lodash_es_cloneDeepWith(source, leaveDOMReferences);
}
// A customized function for cloneDeepWith.
// It will leave references to DOM Elements instead of cloning them.
//
// @param {*} value
// @returns {Element|undefined}
function leaveDOMReferences(value) {
    return lodash_es_isElement(value) ? value : undefined;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/isiterable.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/isiterable
 */
/**
 * Checks if value implements iterator interface.
 *
 * @param {*} value The value to check.
 * @returns {Boolean} True if value implements iterator interface.
 */
function isIterable(value) {
    return !!(value && value[Symbol.iterator]);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/collection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/collection
 */




/**
 * Collections are ordered sets of objects. Items in the collection can be retrieved by their indexes
 * in the collection (like in an array) or by their ids.
 *
 * If an object without an `id` property is being added to the collection, the `id` property will be generated
 * automatically. Note that the automatically generated id is unique only within this single collection instance.
 *
 * By default an item in the collection is identified by its `id` property. The name of the identifier can be
 * configured through the constructor of the collection.
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class Collection extends Emitter {
    /**
     * Creates a new Collection instance.
     *
     * You can provide an iterable of initial items the collection will be created with:
     *
     *		const collection = new Collection( [ { id: 'John' }, { id: 'Mike' } ] );
     *
     *		console.log( collection.get( 0 ) ); // -> { id: 'John' }
     *		console.log( collection.get( 1 ) ); // -> { id: 'Mike' }
     *		console.log( collection.get( 'Mike' ) ); // -> { id: 'Mike' }
     *
     * Or you can first create a collection and then add new items using the {@link #add} method:
     *
     *		const collection = new Collection();
     *
     *		collection.add( { id: 'John' } );
     *		console.log( collection.get( 0 ) ); // -> { id: 'John' }
     *
     * Whatever option you choose, you can always pass a configuration object as the last argument
     * of the constructor:
     *
     *		const emptyCollection = new Collection( { idProperty: 'name' } );
     *		emptyCollection.add( { name: 'John' } );
     *		console.log( collection.get( 'John' ) ); // -> { name: 'John' }
     *
     *		const nonEmptyCollection = new Collection( [ { name: 'John' } ], { idProperty: 'name' } );
     *		nonEmptyCollection.add( { name: 'George' } );
     *		console.log( collection.get( 'George' ) ); // -> { name: 'George' }
     *		console.log( collection.get( 'John' ) ); // -> { name: 'John' }
     *
     * @param {Iterable.<Object>|Object} [initialItemsOrOptions] The initial items of the collection or
     * the options object.
     * @param {Object} [options={}] The options object, when the first argument is an array of initial items.
     * @param {String} [options.idProperty='id'] The name of the property which is used to identify an item.
     * Items that do not have such a property will be assigned one when added to the collection.
     */
    constructor(initialItemsOrOptions = {}, options = {}) {
        super();
        const hasInitialItems = isIterable(initialItemsOrOptions);
        if (!hasInitialItems) {
            options = initialItemsOrOptions;
        }
        this._items = [];
        this._itemMap = new Map();
        this._idProperty = options.idProperty || 'id';
        this._bindToExternalToInternalMap = new WeakMap();
        this._bindToInternalToExternalMap = new WeakMap();
        this._skippedIndexesFromExternal = [];
        // Set the initial content of the collection (if provided in the constructor).
        if (hasInitialItems) {
            for (const item of initialItemsOrOptions) {
                this._items.push(item);
                this._itemMap.set(this._getItemIdBeforeAdding(item), item);
            }
        }
    }
    /**
     * The number of items available in the collection.
     *
     * @member {Number} #length
     */
    get length() {
        return this._items.length;
    }
    /**
     * Returns the first item from the collection or null when collection is empty.
     *
     * @returns {Object|null} The first item or `null` if collection is empty.
     */
    get first() {
        return this._items[0] || null;
    }
    /**
     * Returns the last item from the collection or null when collection is empty.
     *
     * @returns {Object|null} The last item or `null` if collection is empty.
     */
    get last() {
        return this._items[this.length - 1] || null;
    }
    /**
     * Adds an item into the collection.
     *
     * If the item does not have an id, then it will be automatically generated and set on the item.
     *
     * @chainable
     * @param {Object} item
     * @param {Number} [index] The position of the item in the collection. The item
     * is pushed to the collection when `index` not specified.
     * @fires add
     * @fires change
     */
    add(item, index) {
        return this.addMany([item], index);
    }
    /**
     * Adds multiple items into the collection.
     *
     * Any item not containing an id will get an automatically generated one.
     *
     * @chainable
     * @param {Iterable.<Object>} items
     * @param {Number} [index] The position of the insertion. Items will be appended if no `index` is specified.
     * @fires add
     * @fires change
     */
    addMany(items, index) {
        if (index === undefined) {
            index = this._items.length;
        }
        else if (index > this._items.length || index < 0) {
            /**
             * The `index` passed to {@link module:utils/collection~Collection#addMany `Collection#addMany()`}
             * is invalid. It must be a number between 0 and the collection's length.
             *
             * @error collection-add-item-invalid-index
             */
            throw new CKEditorError('collection-add-item-invalid-index', this);
        }
        let offset = 0;
        for (const item of items) {
            const itemId = this._getItemIdBeforeAdding(item);
            const currentItemIndex = index + offset;
            this._items.splice(currentItemIndex, 0, item);
            this._itemMap.set(itemId, item);
            this.fire('add', item, currentItemIndex);
            offset++;
        }
        this.fire('change', {
            added: items,
            removed: [],
            index
        });
        return this;
    }
    /**
     * Gets an item by its ID or index.
     *
     * @param {String|Number} idOrIndex The item ID or index in the collection.
     * @returns {Object|null} The requested item or `null` if such item does not exist.
     */
    get(idOrIndex) {
        let item;
        if (typeof idOrIndex == 'string') {
            item = this._itemMap.get(idOrIndex);
        }
        else if (typeof idOrIndex == 'number') {
            item = this._items[idOrIndex];
        }
        else {
            /**
             * An index or ID must be given.
             *
             * @error collection-get-invalid-arg
             */
            throw new CKEditorError('collection-get-invalid-arg', this);
        }
        return item || null;
    }
    /**
     * Returns a Boolean indicating whether the collection contains an item.
     *
     * @param {Object|String} itemOrId The item or its ID in the collection.
     * @returns {Boolean} `true` if the collection contains the item, `false` otherwise.
     */
    has(itemOrId) {
        if (typeof itemOrId == 'string') {
            return this._itemMap.has(itemOrId);
        }
        else { // Object
            const idProperty = this._idProperty;
            const id = itemOrId[idProperty];
            return id && this._itemMap.has(id);
        }
    }
    /**
     * Gets an index of an item in the collection.
     * When an item is not defined in the collection, the index will equal -1.
     *
     * @param {Object|String} itemOrId The item or its ID in the collection.
     * @returns {Number} The index of a given item.
     */
    getIndex(itemOrId) {
        let item;
        if (typeof itemOrId == 'string') {
            item = this._itemMap.get(itemOrId);
        }
        else {
            item = itemOrId;
        }
        return item ? this._items.indexOf(item) : -1;
    }
    /**
     * Removes an item from the collection.
     *
     * @param {Object|Number|String} subject The item to remove, its ID or index in the collection.
     * @returns {Object} The removed item.
     * @fires remove
     * @fires change
     */
    remove(subject) {
        const [item, index] = this._remove(subject);
        this.fire('change', {
            added: [],
            removed: [item],
            index
        });
        return item;
    }
    /**
     * Executes the callback for each item in the collection and composes an array or values returned by this callback.
     *
     * @param {Function} callback
     * @param {Object} callback.item
     * @param {Number} callback.index
     * @param {Object} [ctx] Context in which the `callback` will be called.
     * @returns {Array} The result of mapping.
     */
    map(callback, ctx) {
        return this._items.map(callback, ctx);
    }
    /**
     * Finds the first item in the collection for which the `callback` returns a true value.
     *
     * @param {Function} callback
     * @param {Object} callback.item
     * @param {Number} callback.index
     * @param {Object} [ctx] Context in which the `callback` will be called.
     * @returns {Object|undefined} The item for which `callback` returned a true value.
     */
    find(callback, ctx) {
        return this._items.find(callback, ctx);
    }
    /**
     * Returns an array with items for which the `callback` returned a true value.
     *
     * @param {Function} callback
     * @param {Object} callback.item
     * @param {Number} callback.index
     * @param {Object} [ctx] Context in which the `callback` will be called.
     * @returns {Array} The array with matching items.
     */
    filter(callback, ctx) {
        return this._items.filter(callback, ctx);
    }
    /**
     * Removes all items from the collection and destroys the binding created using
     * {@link #bindTo}.
     *
     * @fires remove
     * @fires change
     */
    clear() {
        if (this._bindToCollection) {
            this.stopListening(this._bindToCollection);
            this._bindToCollection = null;
        }
        const removedItems = Array.from(this._items);
        while (this.length) {
            this._remove(0);
        }
        this.fire('change', {
            added: [],
            removed: removedItems,
            index: 0
        });
    }
    /**
     * Binds and synchronizes the collection with another one.
     *
     * The binding can be a simple factory:
     *
     *		class FactoryClass {
     *			constructor( data ) {
     *				this.label = data.label;
     *			}
     *		}
     *
     *		const source = new Collection( { idProperty: 'label' } );
     *		const target = new Collection();
     *
     *		target.bindTo( source ).as( FactoryClass );
     *
     *		source.add( { label: 'foo' } );
     *		source.add( { label: 'bar' } );
     *
     *		console.log( target.length ); // 2
     *		console.log( target.get( 1 ).label ); // 'bar'
     *
     *		source.remove( 0 );
     *		console.log( target.length ); // 1
     *		console.log( target.get( 0 ).label ); // 'bar'
     *
     * or the factory driven by a custom callback:
     *
     *		class FooClass {
     *			constructor( data ) {
     *				this.label = data.label;
     *			}
     *		}
     *
     *		class BarClass {
     *			constructor( data ) {
     *				this.label = data.label;
     *			}
     *		}
     *
     *		const source = new Collection( { idProperty: 'label' } );
     *		const target = new Collection();
     *
     *		target.bindTo( source ).using( ( item ) => {
     *			if ( item.label == 'foo' ) {
     *				return new FooClass( item );
     *			} else {
     *				return new BarClass( item );
     *			}
     *		} );
     *
     *		source.add( { label: 'foo' } );
     *		source.add( { label: 'bar' } );
     *
     *		console.log( target.length ); // 2
     *		console.log( target.get( 0 ) instanceof FooClass ); // true
     *		console.log( target.get( 1 ) instanceof BarClass ); // true
     *
     * or the factory out of property name:
     *
     *		const source = new Collection( { idProperty: 'label' } );
     *		const target = new Collection();
     *
     *		target.bindTo( source ).using( 'label' );
     *
     *		source.add( { label: { value: 'foo' } } );
     *		source.add( { label: { value: 'bar' } } );
     *
     *		console.log( target.length ); // 2
     *		console.log( target.get( 0 ).value ); // 'foo'
     *		console.log( target.get( 1 ).value ); // 'bar'
     *
     * It's possible to skip specified items by returning falsy value:
     *
     *		const source = new Collection();
     *		const target = new Collection();
     *
     *		target.bindTo( source ).using( item => {
     *			if ( item.hidden ) {
     *				return null;
     *			}
     *
     *			return item;
     *		} );
     *
     *		source.add( { hidden: true } );
     *		source.add( { hidden: false } );
     *
     *		console.log( source.length ); // 2
     *		console.log( target.length ); // 1
     *
     * **Note**: {@link #clear} can be used to break the binding.
     *
     * @param {module:utils/collection~Collection} externalCollection A collection to be bound.
     * @returns {module:utils/collection~CollectionBindToChain} The binding chain object.
     */
    bindTo(externalCollection) {
        if (this._bindToCollection) {
            /**
             * The collection cannot be bound more than once.
             *
             * @error collection-bind-to-rebind
             */
            throw new CKEditorError('collection-bind-to-rebind', this);
        }
        this._bindToCollection = externalCollection;
        return {
            as: Class => {
                this._setUpBindToBinding(item => new Class(item));
            },
            using: callbackOrProperty => {
                if (typeof callbackOrProperty == 'function') {
                    this._setUpBindToBinding(callbackOrProperty);
                }
                else {
                    this._setUpBindToBinding(item => item[callbackOrProperty]);
                }
            }
        };
    }
    /**
     * Finalizes and activates a binding initiated by {#bindTo}.
     *
     * @private
     * @param {Function} factory A function which produces collection items.
     */
    _setUpBindToBinding(factory) {
        const externalCollection = this._bindToCollection;
        // Adds the item to the collection once a change has been done to the external collection.
        //
        // @private
        const addItem = (evt, externalItem, index) => {
            const isExternalBoundToThis = externalCollection._bindToCollection == this;
            const externalItemBound = externalCollection._bindToInternalToExternalMap.get(externalItem);
            // If an external collection is bound to this collection, which makes it a 2–way binding,
            // and the particular external collection item is already bound, don't add it here.
            // The external item has been created **out of this collection's item** and (re)adding it will
            // cause a loop.
            if (isExternalBoundToThis && externalItemBound) {
                this._bindToExternalToInternalMap.set(externalItem, externalItemBound);
                this._bindToInternalToExternalMap.set(externalItemBound, externalItem);
            }
            else {
                const item = factory(externalItem);
                // When there is no item we need to remember skipped index first and then we can skip this item.
                if (!item) {
                    this._skippedIndexesFromExternal.push(index);
                    return;
                }
                // Lets try to put item at the same index as index in external collection
                // but when there are a skipped items in one or both collections we need to recalculate this index.
                let finalIndex = index;
                // When we try to insert item after some skipped items from external collection we need
                // to include this skipped items and decrease index.
                //
                // For the following example:
                // external -> [ 'A', 'B - skipped for internal', 'C - skipped for internal' ]
                // internal -> [ A ]
                //
                // Another item is been added at the end of external collection:
                // external.add( 'D' )
                // external -> [ 'A', 'B - skipped for internal', 'C - skipped for internal', 'D' ]
                //
                // We can't just add 'D' to internal at the same index as index in external because
                // this will produce empty indexes what is invalid:
                // internal -> [ 'A', empty, empty, 'D' ]
                //
                // So we need to include skipped items and decrease index
                // internal -> [ 'A', 'D' ]
                for (const skipped of this._skippedIndexesFromExternal) {
                    if (index > skipped) {
                        finalIndex--;
                    }
                }
                // We need to take into consideration that external collection could skip some items from
                // internal collection.
                //
                // For the following example:
                // internal -> [ 'A', 'B - skipped for external', 'C - skipped for external' ]
                // external -> [ A ]
                //
                // Another item is been added at the end of external collection:
                // external.add( 'D' )
                // external -> [ 'A', 'D' ]
                //
                // We need to include skipped items and place new item after them:
                // internal -> [ 'A', 'B - skipped for external', 'C - skipped for external', 'D' ]
                for (const skipped of externalCollection._skippedIndexesFromExternal) {
                    if (finalIndex >= skipped) {
                        finalIndex++;
                    }
                }
                this._bindToExternalToInternalMap.set(externalItem, item);
                this._bindToInternalToExternalMap.set(item, externalItem);
                this.add(item, finalIndex);
                // After adding new element to internal collection we need update indexes
                // of skipped items in external collection.
                for (let i = 0; i < externalCollection._skippedIndexesFromExternal.length; i++) {
                    if (finalIndex <= externalCollection._skippedIndexesFromExternal[i]) {
                        externalCollection._skippedIndexesFromExternal[i]++;
                    }
                }
            }
        };
        // Load the initial content of the collection.
        for (const externalItem of externalCollection) {
            addItem(null, externalItem, externalCollection.getIndex(externalItem));
        }
        // Synchronize the with collection as new items are added.
        this.listenTo(externalCollection, 'add', addItem);
        // Synchronize the with collection as new items are removed.
        this.listenTo(externalCollection, 'remove', (evt, externalItem, index) => {
            const item = this._bindToExternalToInternalMap.get(externalItem);
            if (item) {
                this.remove(item);
            }
            // After removing element from external collection we need update/remove indexes
            // of skipped items in internal collection.
            this._skippedIndexesFromExternal = this._skippedIndexesFromExternal.reduce((result, skipped) => {
                if (index < skipped) {
                    result.push(skipped - 1);
                }
                if (index > skipped) {
                    result.push(skipped);
                }
                return result;
            }, []);
        });
    }
    /**
     * Returns an unique id property for a given `item`.
     *
     * The method will generate new id and assign it to the `item` if it doesn't have any.
     *
     * @private
     * @param {Object} item Item to be added.
     * @returns {String}
     */
    _getItemIdBeforeAdding(item) {
        const idProperty = this._idProperty;
        let itemId;
        if ((idProperty in item)) {
            itemId = item[idProperty];
            if (typeof itemId != 'string') {
                /**
                 * This item's ID should be a string.
                 *
                 * @error collection-add-invalid-id
                 */
                throw new CKEditorError('collection-add-invalid-id', this);
            }
            if (this.get(itemId)) {
                /**
                 * This item already exists in the collection.
                 *
                 * @error collection-add-item-already-exists
                 */
                throw new CKEditorError('collection-add-item-already-exists', this);
            }
        }
        else {
            item[idProperty] = itemId = uid();
        }
        return itemId;
    }
    /**
     * Core {@link #remove} method implementation shared in other functions.
     *
     * In contrast this method **does not** fire the {@link #event:change} event.
     *
     * @private
     * @param {Object|Number|String} subject The item to remove, its id or index in the collection.
     * @returns {Array} Returns an array with the removed item and its index.
     * @fires remove
     */
    _remove(subject) {
        let index, id, item;
        let itemDoesNotExist = false;
        const idProperty = this._idProperty;
        if (typeof subject == 'string') {
            id = subject;
            item = this._itemMap.get(id);
            itemDoesNotExist = !item;
            if (item) {
                index = this._items.indexOf(item);
            }
        }
        else if (typeof subject == 'number') {
            index = subject;
            item = this._items[index];
            itemDoesNotExist = !item;
            if (item) {
                id = item[idProperty];
            }
        }
        else {
            item = subject;
            id = item[idProperty];
            index = this._items.indexOf(item);
            itemDoesNotExist = (index == -1 || !this._itemMap.get(id));
        }
        if (itemDoesNotExist) {
            /**
             * Item not found.
             *
             * @error collection-remove-404
             */
            throw new CKEditorError('collection-remove-404', this);
        }
        this._items.splice(index, 1);
        this._itemMap.delete(id);
        const externalItem = this._bindToInternalToExternalMap.get(item);
        this._bindToInternalToExternalMap.delete(item);
        this._bindToExternalToInternalMap.delete(externalItem);
        this.fire('remove', item, index);
        return [item, index];
    }
    /**
     * Iterable interface.
     *
     * @returns {Iterator.<*>}
     */
    [Symbol.iterator]() {
        return this._items[Symbol.iterator]();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/plugincollection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/plugincollection
 */


/**
 * Manages a list of CKEditor plugins, including loading, resolving dependencies and initialization.
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class PluginCollection extends Emitter {
    /**
     * Creates an instance of the plugin collection class.
     * Allows loading and initializing plugins and their dependencies.
     * Allows providing a list of already loaded plugins. These plugins will not be destroyed along with this collection.
     *
     * @param {module:core/editor/editor~Editor|module:core/context~Context} context
     * @param {Array.<Function>} [availablePlugins] Plugins (constructors) which the collection will be able to use
     * when {@link module:core/plugincollection~PluginCollection#init} is used with the plugin names (strings, instead of constructors).
     * Usually, the editor will pass its built-in plugins to the collection so they can later be
     * used in `config.plugins` or `config.removePlugins` by names.
     * @param {Iterable.<Array>} contextPlugins A list of already initialized plugins represented by a
     * `[ PluginConstructor, pluginInstance ]` pair.
     */
    constructor(context, availablePlugins = [], contextPlugins = []) {
        super();
        /**
         * @protected
         * @type {module:core/editor/editor~Editor|module:core/context~Context}
         */
        this._context = context;
        /**
         * @protected
         * @type {Map}
         */
        this._plugins = new Map();
        /**
         * A map of plugin constructors that can be retrieved by their names.
         *
         * @protected
         * @type {Map.<String|Function,Function>}
         */
        this._availablePlugins = new Map();
        for (const PluginConstructor of availablePlugins) {
            if (PluginConstructor.pluginName) {
                this._availablePlugins.set(PluginConstructor.pluginName, PluginConstructor);
            }
        }
        /**
         * Map of {@link module:core/contextplugin~ContextPlugin context plugins} which can be retrieved by their constructors or instances.
         *
         * @protected
         * @type {Map<Function,Function>}
         */
        this._contextPlugins = new Map();
        for (const [PluginConstructor, pluginInstance] of contextPlugins) {
            this._contextPlugins.set(PluginConstructor, pluginInstance);
            this._contextPlugins.set(pluginInstance, PluginConstructor);
            // To make it possible to require a plugin by its name.
            if (PluginConstructor.pluginName) {
                this._availablePlugins.set(PluginConstructor.pluginName, PluginConstructor);
            }
        }
    }
    /**
     * Iterable interface.
     *
     * Returns `[ PluginConstructor, pluginInstance ]` pairs.
     *
     * @returns {Iterable.<Array>}
     */
    *[Symbol.iterator]() {
        for (const entry of this._plugins) {
            if (typeof entry[0] == 'function') {
                yield entry;
            }
        }
    }
    get(key) {
        const plugin = this._plugins.get(key);
        if (!plugin) {
            let pluginName = key;
            if (typeof key == 'function') {
                pluginName = key.pluginName || key.name;
            }
            /**
             * The plugin is not loaded and could not be obtained.
             *
             * Plugin classes (constructors) need to be provided to the editor and must be loaded before they can be obtained from
             * the plugin collection.
             * This is usually done in CKEditor 5 builds by setting the {@link module:core/editor/editor~Editor.builtinPlugins}
             * property.
             *
             * **Note**: You can use `{@link module:core/plugincollection~PluginCollection#has editor.plugins.has()}`
             * to check if a plugin was loaded.
             *
             * @error plugincollection-plugin-not-loaded
             * @param {String} plugin The name of the plugin which is not loaded.
             */
            throw new CKEditorError('plugincollection-plugin-not-loaded', this._context, { plugin: pluginName });
        }
        return plugin;
    }
    /**
     * Checks if a plugin is loaded.
     *
     *		// Check if the 'Clipboard' plugin was loaded.
     *		if ( editor.plugins.has( 'ClipboardPipeline' ) ) {
     *			// Now use the clipboard plugin instance:
     *			const clipboard = editor.plugins.get( 'ClipboardPipeline' );
     *
     *			// ...
     *		}
     *
     * @param {Function|String} key The plugin constructor or {@link module:core/plugin~PluginInterface.pluginName name}.
     * @returns {Boolean}
     */
    has(key) {
        return this._plugins.has(key);
    }
    /**
     * Initializes a set of plugins and adds them to the collection.
     *
     * @param {Array.<Function|String>} plugins An array of {@link module:core/plugin~PluginInterface plugin constructors}
     * or {@link module:core/plugin~PluginInterface.pluginName plugin names}.
     * @param {Array.<String|Function>} [pluginsToRemove] Names of the plugins or plugin constructors
     * that should not be loaded (despite being specified in the `plugins` array).
     * @param {Array.<Function>} [pluginsSubstitutions] An array of {@link module:core/plugin~PluginInterface plugin constructors}
     * that will be used to replace plugins of the same names that were passed in `plugins` or that are in their dependency tree.
     * A useful option for replacing built-in plugins while creating tests (for mocking their APIs). Plugins that will be replaced
     * must follow these rules:
     *   * The new plugin must be a class.
     *   * The new plugin must be named.
     *   * Both plugins must not depend on other plugins.
     * @returns {Promise.<module:core/plugin~LoadedPlugins>} A promise which gets resolved once all plugins are loaded
     * and available in the collection.
     */
    init(plugins, pluginsToRemove = [], pluginsSubstitutions = []) {
        // Plugin initialization procedure consists of 2 main steps:
        // 1) collecting all available plugin constructors,
        // 2) verification whether all required plugins can be instantiated.
        //
        // In the first step, all plugin constructors, available in the provided `plugins` array and inside
        // plugin's dependencies (from the `Plugin.requires` array), are recursively collected and added to the existing
        // `this._availablePlugins` map, but without any verification at the given moment. Performing the verification
        // at this point (during the plugin constructor searching) would cause false errors to occur, that some plugin
        // is missing but in fact it may be defined further in the array as the dependency of other plugin. After
        // traversing the entire dependency tree, it will be checked if all required "top level" plugins are available.
        //
        // In the second step, the list of plugins that have not been explicitly removed is traversed to get all the
        // plugin constructors to be instantiated in the correct order and to validate against some rules. Finally, if
        // no plugin is missing and no other error has been found, they all will be instantiated.
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const that = this;
        const context = this._context;
        findAvailablePluginConstructors(plugins);
        validatePlugins(plugins);
        const pluginsToLoad = plugins.filter(plugin => !isPluginRemoved(plugin, pluginsToRemove));
        const pluginConstructors = [...getPluginConstructors(pluginsToLoad)];
        substitutePlugins(pluginConstructors, pluginsSubstitutions);
        const pluginInstances = loadPlugins(pluginConstructors);
        return initPlugins(pluginInstances, 'init')
            .then(() => initPlugins(pluginInstances, 'afterInit'))
            .then(() => pluginInstances);
        function isPluginConstructor(plugin) {
            return typeof plugin === 'function';
        }
        function isContextPlugin(plugin) {
            return isPluginConstructor(plugin) && plugin.isContextPlugin;
        }
        function isPluginRemoved(plugin, pluginsToRemove) {
            return pluginsToRemove.some(removedPlugin => {
                if (removedPlugin === plugin) {
                    return true;
                }
                if (getPluginName(plugin) === removedPlugin) {
                    return true;
                }
                if (getPluginName(removedPlugin) === plugin) {
                    return true;
                }
                return false;
            });
        }
        function getPluginName(plugin) {
            return isPluginConstructor(plugin) ?
                plugin.pluginName || plugin.name :
                plugin;
        }
        function findAvailablePluginConstructors(plugins, processed = new Set()) {
            plugins.forEach(plugin => {
                if (!isPluginConstructor(plugin)) {
                    return;
                }
                if (processed.has(plugin)) {
                    return;
                }
                processed.add(plugin);
                if (plugin.pluginName && !that._availablePlugins.has(plugin.pluginName)) {
                    that._availablePlugins.set(plugin.pluginName, plugin);
                }
                if (plugin.requires) {
                    findAvailablePluginConstructors(plugin.requires, processed);
                }
            });
        }
        function getPluginConstructors(plugins, processed = new Set()) {
            return plugins
                .map(plugin => {
                return isPluginConstructor(plugin) ?
                    plugin :
                    that._availablePlugins.get(plugin);
            })
                .reduce((result, plugin) => {
                if (processed.has(plugin)) {
                    return result;
                }
                processed.add(plugin);
                if (plugin.requires) {
                    validatePlugins(plugin.requires, plugin);
                    getPluginConstructors(plugin.requires, processed).forEach(plugin => result.add(plugin));
                }
                return result.add(plugin);
            }, new Set());
        }
        function validatePlugins(plugins, parentPluginConstructor = null) {
            plugins
                .map(plugin => {
                return isPluginConstructor(plugin) ?
                    plugin :
                    that._availablePlugins.get(plugin) || plugin;
            })
                .forEach(plugin => {
                checkMissingPlugin(plugin, parentPluginConstructor);
                checkContextPlugin(plugin, parentPluginConstructor);
                checkRemovedPlugin(plugin, parentPluginConstructor);
            });
        }
        function checkMissingPlugin(plugin, parentPluginConstructor) {
            if (isPluginConstructor(plugin)) {
                return;
            }
            if (parentPluginConstructor) {
                /**
                 * A required "soft" dependency was not found on the plugin list.
                 *
                 * When configuring the editor, either prior to building (via
                 * {@link module:core/editor/editor~Editor.builtinPlugins `Editor.builtinPlugins`}) or when
                 * creating a new instance of the editor (e.g. via
                 * {@link module:core/editor/editorconfig~EditorConfig#plugins `config.plugins`}), you need to provide
                 * some of the dependencies for other plugins that you used.
                 *
                 * This error is thrown when one of these dependencies was not provided. The name of the missing plugin
                 * can be found in `missingPlugin` and the plugin that required it in `requiredBy`.
                 *
                 * In order to resolve it, you need to import the missing plugin and add it to the
                 * current list of plugins (`Editor.builtinPlugins` or `config.plugins`/`config.extraPlugins`).
                 *
                 * Soft requirements were introduced in version 26.0.0. If you happen to stumble upon this error
                 * when upgrading to version 26.0.0, read also the
                 * {@glink updating/migration-to-26 Migration to 26.0.0} guide.
                 *
                 * @error plugincollection-soft-required
                 * @param {String} missingPlugin The name of the required plugin.
                 * @param {String} requiredBy The name of the plugin that requires the other plugin.
                 */
                throw new CKEditorError('plugincollection-soft-required', context, { missingPlugin: plugin, requiredBy: getPluginName(parentPluginConstructor) });
            }
            /**
             * A plugin is not available and could not be loaded.
             *
             * Plugin classes (constructors) need to be provided to the editor before they can be loaded by name.
             * This is usually done in CKEditor 5 builds by setting the {@link module:core/editor/editor~Editor.builtinPlugins}
             * property.
             *
             * **If you see this warning when using one of the {@glink installation/getting-started/predefined-builds
             * CKEditor 5 Builds}**,
             * it means that you try to enable a plugin which was not included in that build. This may be due to a typo
             * in the plugin name or simply because that plugin is not a part of this build. In the latter scenario,
             * read more about {@glink installation/getting-started/quick-start custom builds}.
             *
             * **If you see this warning when using one of the editor creators directly** (not a build), then it means
             * that you tried loading plugins by name. However, unlike CKEditor 4, CKEditor 5 does not implement a "plugin loader".
             * This means that CKEditor 5 does not know where to load the plugin modules from. Therefore, you need to
             * provide each plugin through a reference (as a constructor function). Check out the examples in
             * {@glink installation/advanced/alternative-setups/integrating-from-source "Building from source"}.
             *
             * @error plugincollection-plugin-not-found
             * @param {String} plugin The name of the plugin which could not be loaded.
             */
            throw new CKEditorError('plugincollection-plugin-not-found', context, { plugin });
        }
        function checkContextPlugin(plugin, parentPluginConstructor) {
            if (!isContextPlugin(parentPluginConstructor)) {
                return;
            }
            if (isContextPlugin(plugin)) {
                return;
            }
            /**
             * If a plugin is a context plugin, all plugins it requires should also be context plugins
             * instead of plugins. In other words, if one plugin can be used in the context,
             * all its requirements should also be ready to be used in the context. Note that the context
             * provides only a part of the API provided by the editor. If one plugin needs a full
             * editor API, all plugins which require it are considered as plugins that need a full
             * editor API.
             *
             * @error plugincollection-context-required
             * @param {String} plugin The name of the required plugin.
             * @param {String} requiredBy The name of the parent plugin.
             */
            throw new CKEditorError('plugincollection-context-required', context, { plugin: getPluginName(plugin), requiredBy: getPluginName(parentPluginConstructor) });
        }
        function checkRemovedPlugin(plugin, parentPluginConstructor) {
            if (!parentPluginConstructor) {
                return;
            }
            if (!isPluginRemoved(plugin, pluginsToRemove)) {
                return;
            }
            /**
             * Cannot load a plugin because one of its dependencies is listed in the `removePlugins` option.
             *
             * @error plugincollection-required
             * @param {String} plugin The name of the required plugin.
             * @param {String} requiredBy The name of the parent plugin.
             */
            throw new CKEditorError('plugincollection-required', context, { plugin: getPluginName(plugin), requiredBy: getPluginName(parentPluginConstructor) });
        }
        function loadPlugins(pluginConstructors) {
            return pluginConstructors.map(PluginConstructor => {
                let pluginInstance = that._contextPlugins.get(PluginConstructor);
                pluginInstance = pluginInstance || new PluginConstructor(context);
                that._add(PluginConstructor, pluginInstance);
                return pluginInstance;
            });
        }
        function initPlugins(pluginInstances, method) {
            return pluginInstances.reduce((promise, plugin) => {
                if (!plugin[method]) {
                    return promise;
                }
                if (that._contextPlugins.has(plugin)) {
                    return promise;
                }
                return promise.then(plugin[method].bind(plugin));
            }, Promise.resolve());
        }
        // Replaces plugin constructors with the specified set of plugins.
        //
        // @param {Array.<Function>} pluginConstructors
        // @param {Array.<Function>} pluginsSubstitutions
        function substitutePlugins(pluginConstructors, pluginsSubstitutions) {
            for (const pluginItem of pluginsSubstitutions) {
                if (typeof pluginItem != 'function') {
                    /**
                     * The plugin replacing an existing plugin must be a function.
                     *
                     * @error plugincollection-replace-plugin-invalid-type
                     */
                    throw new CKEditorError('plugincollection-replace-plugin-invalid-type', null, { pluginItem });
                }
                const pluginName = pluginItem.pluginName;
                if (!pluginName) {
                    /**
                     * The plugin replacing an existing plugin must have a name.
                     *
                     * @error plugincollection-replace-plugin-missing-name
                     */
                    throw new CKEditorError('plugincollection-replace-plugin-missing-name', null, { pluginItem });
                }
                if (pluginItem.requires && pluginItem.requires.length) {
                    /**
                     * The plugin replacing an existing plugin cannot depend on other plugins.
                     *
                     * @error plugincollection-plugin-for-replacing-cannot-have-dependencies
                     */
                    throw new CKEditorError('plugincollection-plugin-for-replacing-cannot-have-dependencies', null, { pluginName });
                }
                const pluginToReplace = that._availablePlugins.get(pluginName);
                if (!pluginToReplace) {
                    /**
                     * The replaced plugin does not exist in the
                     * {@link module:core/plugincollection~PluginCollection available plugins} collection.
                     *
                     * @error plugincollection-plugin-for-replacing-not-exist
                     */
                    throw new CKEditorError('plugincollection-plugin-for-replacing-not-exist', null, { pluginName });
                }
                const indexInPluginConstructors = pluginConstructors.indexOf(pluginToReplace);
                if (indexInPluginConstructors === -1) {
                    // The Context feature can substitute plugins as well.
                    // It may happen that the editor will be created with the given context, where the plugin for substitute
                    // was already replaced. In such a case, we don't want to do it again.
                    if (that._contextPlugins.has(pluginToReplace)) {
                        return;
                    }
                    /**
                     * The replaced plugin will not be loaded so it cannot be replaced.
                     *
                     * @error plugincollection-plugin-for-replacing-not-loaded
                     */
                    throw new CKEditorError('plugincollection-plugin-for-replacing-not-loaded', null, { pluginName });
                }
                if (pluginToReplace.requires && pluginToReplace.requires.length) {
                    /**
                     * The replaced plugin cannot depend on other plugins.
                     *
                     * @error plugincollection-replaced-plugin-cannot-have-dependencies
                     */
                    throw new CKEditorError('plugincollection-replaced-plugin-cannot-have-dependencies', null, { pluginName });
                }
                pluginConstructors.splice(indexInPluginConstructors, 1, pluginItem);
                that._availablePlugins.set(pluginName, pluginItem);
            }
        }
    }
    /**
     * Destroys all loaded plugins.
     *
     * @returns {Promise}
     */
    destroy() {
        const promises = [];
        for (const [, pluginInstance] of this) {
            if (typeof pluginInstance.destroy == 'function' && !this._contextPlugins.has(pluginInstance)) {
                promises.push(pluginInstance.destroy());
            }
        }
        return Promise.all(promises);
    }
    /**
     * Adds the plugin to the collection. Exposed mainly for testing purposes.
     *
     * @protected
     * @param {Function} PluginConstructor The plugin constructor.
     * @param {module:core/plugin~PluginInterface} plugin The instance of the plugin.
     */
    _add(PluginConstructor, plugin) {
        this._plugins.set(PluginConstructor, plugin);
        const pluginName = PluginConstructor.pluginName;
        if (!pluginName) {
            return;
        }
        if (this._plugins.has(pluginName)) {
            /**
             * Two plugins with the same {@link module:core/plugin~PluginInterface.pluginName} were loaded.
             * This will lead to runtime conflicts between these plugins.
             *
             * In practice, this warning usually means that new plugins were added to an existing CKEditor 5 build.
             * Plugins should always be added to a source version of the editor (`@ckeditor/ckeditor5-editor-*`),
             * not to an editor imported from one of the `@ckeditor/ckeditor5-build-*` packages.
             *
             * Check your import paths and the list of plugins passed to
             * {@link module:core/editor/editor~Editor.create `Editor.create()`}
             * or specified in {@link module:core/editor/editor~Editor.builtinPlugins `Editor.builtinPlugins`}.
             *
             * The second option is that your `node_modules/` directory contains duplicated versions of the same
             * CKEditor 5 packages. Normally, on clean installations, npm deduplicates packages in `node_modules/`, so
             * it may be enough to call `rm -rf node_modules && npm i`. However, if you installed conflicting versions
             * of some packages, their dependencies may need to be installed in more than one version which may lead to this
             * warning.
             *
             * Technically speaking, this error occurs because after adding a plugin to an existing editor build
             * the dependencies of this plugin are being duplicated.
             * They are already built into that editor build and now get added for the second time as dependencies
             * of the plugin you are installing.
             *
             * Read more about {@glink installation/getting-started/installing-plugins installing plugins}.
             *
             * @error plugincollection-plugin-name-conflict
             * @param {String} pluginName The duplicated plugin name.
             * @param {Function} plugin1 The first plugin constructor.
             * @param {Function} plugin2 The second plugin constructor.
             */
            throw new CKEditorError('plugincollection-plugin-name-conflict', null, { pluginName, plugin1: this._plugins.get(pluginName).constructor, plugin2: PluginConstructor });
        }
        this._plugins.set(pluginName, plugin);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/toarray.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
function toArray(data) {
    return Array.isArray(data) ? data : [data];
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/global.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* globals window, document */
/**
 * @module utils/dom/global
 */
/**
 * A helper (module) giving an access to the global DOM objects such as `window` and
 * `document`. Accessing these objects using this helper allows easy and bulletproof
 * testing, i.e. stubbing native properties:
 *
 *		import global from 'ckeditor5/utils/dom/global.js';
 *
 *		// This stub will work for any code using global module.
 *		testUtils.sinon.stub( global, 'window', {
 *			innerWidth: 10000
 *		} );
 *
 *		console.log( global.window.innerWidth );
 */
let global_global;
// In some environments window and document API might not be available.
try {
    global_global = { window, document };
}
catch (e) {
    // It's not possible to mock a window object to simulate lack of a window object without writing extremely convoluted code.
    /* istanbul ignore next */
    // Let's cast it to not change module's API.
    // We only handle this so loading editor in environments without window and document doesn't fail.
    // For better DX we shouldn't introduce mixed types and require developers to check the type manually.
    // This module should not be used on purpose in any environment outside browser.
    global_global = { window: {}, document: {} };
}
/* harmony default export */ const dom_global = (global_global);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/translation-service.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable no-var */
/**
 * @module utils/translation-service
 */


/* istanbul ignore else */
if (!dom_global.window.CKEDITOR_TRANSLATIONS) {
    dom_global.window.CKEDITOR_TRANSLATIONS = {};
}
/**
 * Adds translations to existing ones or overrides the existing translations. These translations will later
 * be available for the {@link module:utils/locale~Locale#t `t()`} function.
 *
 * The `translations` is an object which consists of `messageId: translation` pairs. Note that the message ID can be
 * either constructed from the message string or from the message ID if it was passed
 * (this happens rarely and mostly for short messages or messages with placeholders).
 * Since the editor displays only the message string, the message ID can be found either in the source code or in the
 * built translations for another language.
 *
 *		add( 'pl', {
 *			'Cancel': 'Anuluj',
 *			'IMAGE': 'obraz', // Note that the `IMAGE` comes from the message ID, while the string can be `image`.
 *		} );
 *
 * If the message is supposed to support various plural forms, make sure to provide an array with the singular form and all plural forms:
 *
 *		add( 'pl', {
 *	 		'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
 * 		} );
 *
 * You should also specify the third argument (the `getPluralForm()` function) that will be used to determine the plural form if no
 * language file was loaded for that language. All language files coming from CKEditor 5 sources will have this option set, so
 * these plural form rules will be reused by other translations added to the registered languages. The `getPluralForm()` function
 * can return either a Boolean or a number.
 *
 * 		add( 'en', {
 *	 		// ... Translations.
 * 		}, n => n !== 1 );
 * 		add( 'pl', {
 *	 		// ... Translations.
 * 		}, n => n == 1 ? 0 : n % 10 >= 2 && n % 10 <= 4 && ( n % 100 < 10 || n % 100 >= 20 ) ? 1 : 2 );
 *
 * All translations extend the global `window.CKEDITOR_TRANSLATIONS` object. An example of this object can be found below:
 *
 * 		{
 * 			pl: {
 *				dictionary: {
 *					'Cancel': 'Anuluj',
 *					'Add space': [ 'Dodaj spację', 'Dodaj %0 spacje', 'Dodaj %0 spacji' ]
 *				},
 *				// A function that returns the plural form index.
 *				getPluralForm: n => n !==1
 *			}
 *			// Other languages.
 *		}
 *
 * If you cannot import this function from this module (e.g. because you use a CKEditor 5 build), you can
 * still add translations by extending the global `window.CKEDITOR_TRANSLATIONS` object by using a function like
 * the one below:
 *
 *		function addTranslations( language, translations, getPluralForm ) {
 *			if ( !global.window.CKEDITOR_TRANSLATIONS ) {
 *				global.window.CKEDITOR_TRANSLATIONS = {};
 *			}

 *			if ( !global.window.CKEDITOR_TRANSLATIONS[ language ] ) {
 *				global.window.CKEDITOR_TRANSLATIONS[ language ] = {};
 *			}
 *
 *			const languageTranslations = global.window.CKEDITOR_TRANSLATIONS[ language ];
 *
 * 			languageTranslations.dictionary = languageTranslations.dictionary || {};
 * 			languageTranslations.getPluralForm = getPluralForm || languageTranslations.getPluralForm;
 *
 *			// Extend the dictionary for the given language.
 *			Object.assign( languageTranslations.dictionary, translations );
 *		}
 *
 * @param {String} language Target language.
 * @param {Object.<String,*>} translations An object with translations which will be added to the dictionary.
 * For each message ID the value should be either a translation or an array of translations if the message
 * should support plural forms.
 * @param {Function} [getPluralForm] A function that returns the plural form index (a number).
 */
function add(language, translations, getPluralForm) {
    if (!global.window.CKEDITOR_TRANSLATIONS[language]) {
        global.window.CKEDITOR_TRANSLATIONS[language] = {};
    }
    const languageTranslations = global.window.CKEDITOR_TRANSLATIONS[language];
    languageTranslations.dictionary = languageTranslations.dictionary || {};
    languageTranslations.getPluralForm = getPluralForm || languageTranslations.getPluralForm;
    Object.assign(languageTranslations.dictionary, translations);
}
/**
 * **Note:** This method is internal, use {@link module:utils/locale~Locale#t the `t()` function} instead to translate
 * the editor UI parts.
 *
 * This function is responsible for translating messages to the specified language. It uses translations added perviously
 * by {@link module:utils/translation-service~add} (a translations dictionary and the `getPluralForm()` function
 * to provide accurate translations of plural forms).
 *
 * When no translation is defined in the dictionary or the dictionary does not exist, this function returns
 * the original message string or the message plural depending on the number of elements.
 *
 *		translate( 'pl', { string: 'Cancel' } ); // 'Cancel'
 *
 * The third optional argument is the number of elements, based on which the single form or one of the plural forms
 * should be picked when the message is supposed to support various plural forms.
 *
 * 		translate( 'en', { string: 'Add a space', plural: 'Add %0 spaces' }, 1 ); // 'Add a space'
 * 		translate( 'en', { string: 'Add a space', plural: 'Add %0 spaces' }, 3 ); // 'Add %0 spaces'
 *
 * The message should provide an ID using the `id` property when the message strings are not unique and their
 * translations should be different.
 *
 *		translate( 'en', { string: 'image', id: 'ADD_IMAGE' } );
 *		translate( 'en', { string: 'image', id: 'AN_IMAGE' } );
 *
 * @protected
 * @param {String} language Target language.
 * @param {module:utils/translation-service~Message} message A message that will be translated.
 * @param {Number} [quantity] The number of elements for which a plural form should be picked from the target language dictionary.
 * @returns {String} Translated sentence.
 */
function _translate(language, message, quantity = 1) {
    if (typeof quantity !== 'number') {
        /**
         * An incorrect value was passed to the translation function. This was probably caused
         * by an incorrect message interpolation of a plural form. Note that for messages supporting plural forms
         * the second argument of the `t()` function should always be a number or an array with a number as the first element.
         *
         * @error translation-service-quantity-not-a-number
         */
        throw new CKEditorError('translation-service-quantity-not-a-number', null, { quantity });
    }
    const numberOfLanguages = getNumberOfLanguages();
    if (numberOfLanguages === 1) {
        // Override the language to the only supported one.
        // This can't be done in the `Locale` class, because the translations comes after the `Locale` class initialization.
        language = Object.keys(dom_global.window.CKEDITOR_TRANSLATIONS)[0];
    }
    const messageId = message.id || message.string;
    if (numberOfLanguages === 0 || !hasTranslation(language, messageId)) {
        if (quantity !== 1) {
            // Return the default plural form that was passed in the `message.plural` parameter.
            return message.plural;
        }
        return message.string;
    }
    const dictionary = dom_global.window.CKEDITOR_TRANSLATIONS[language].dictionary;
    const getPluralForm = dom_global.window.CKEDITOR_TRANSLATIONS[language].getPluralForm || (n => n === 1 ? 0 : 1);
    const translation = dictionary[messageId];
    if (typeof translation === 'string') {
        return translation;
    }
    const pluralFormIndex = Number(getPluralForm(quantity));
    // Note: The `translate` function is not responsible for replacing `%0, %1, ...` with values.
    return translation[pluralFormIndex];
}
/**
 * Clears dictionaries for test purposes.
 *
 * @protected
 */
function _clear() {
    global.window.CKEDITOR_TRANSLATIONS = {};
}
// Checks whether the dictionary exists and translation in that dictionary exists.
function hasTranslation(language, messageId) {
    return (!!dom_global.window.CKEDITOR_TRANSLATIONS[language] &&
        !!dom_global.window.CKEDITOR_TRANSLATIONS[language].dictionary[messageId]);
}
function getNumberOfLanguages() {
    return Object.keys(dom_global.window.CKEDITOR_TRANSLATIONS).length;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/language.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
const RTL_LANGUAGE_CODES = [
    'ar', 'ara',
    'fa', 'per', 'fas',
    'he', 'heb',
    'ku', 'kur',
    'ug', 'uig' // Uighur, Uyghur
];
/**
 * Helps determine whether a language text direction is LTR or RTL.
 *
 * @param {String} languageCode The ISO 639-1 or ISO 639-2 language code.
 * @returns {module:utils/language~LanguageDirection}
 */
function getLanguageDirection(languageCode) {
    return RTL_LANGUAGE_CODES.includes(languageCode) ? 'rtl' : 'ltr';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/locale.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/locale
 */
/* globals console */



/**
 * Represents the localization services.
 */
class Locale {
    /**
     * Creates a new instance of the locale class. Learn more about
     * {@glink features/ui-language configuring the language of the editor}.
     *
     * @param {Object} [options] Locale configuration.
     * @param {String} [options.uiLanguage='en'] The editor UI language code in the
     * [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. See {@link #uiLanguage}.
     * @param {String} [options.contentLanguage] The editor content language code in the
     * [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format. If not specified, the same as `options.language`.
     * See {@link #contentLanguage}.
     */
    constructor(options = {}) {
        this.uiLanguage = options.uiLanguage || 'en';
        this.contentLanguage = options.contentLanguage || this.uiLanguage;
        this.uiLanguageDirection = getLanguageDirection(this.uiLanguage);
        this.contentLanguageDirection = getLanguageDirection(this.contentLanguage);
        this.t = (message, values) => this._t(message, values);
    }
    /**
     * The editor UI language code in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
     *
     * **Note**: This property was deprecated. Please use {@link #uiLanguage} and {@link #contentLanguage}
     * properties instead.
     *
     * @deprecated
     * @member {String}
     */
    get language() {
        /**
         * The {@link module:utils/locale~Locale#language `Locale#language`} property was deprecated and will
         * be removed in the near future. Please use the {@link #uiLanguage} and {@link #contentLanguage} properties instead.
         *
         * @error locale-deprecated-language-property
         */
        console.warn('locale-deprecated-language-property: ' +
            'The Locale#language property has been deprecated and will be removed in the near future. ' +
            'Please use #uiLanguage and #contentLanguage properties instead.');
        return this.uiLanguage;
    }
    /**
     * An unbound version of the {@link #t} method.
     *
     * @private
     * @param {String|module:utils/translation-service~Message} message
     * @param {Number|String|Array.<Number|String>} [values]
     * @returns {String}
     */
    _t(message, values = []) {
        values = toArray(values);
        if (typeof message === 'string') {
            message = { string: message };
        }
        const hasPluralForm = !!message.plural;
        const quantity = hasPluralForm ? values[0] : 1;
        const translatedString = _translate(this.uiLanguage, message, quantity);
        return interpolateString(translatedString, values);
    }
}
// Fills the `%0, %1, ...` string placeholders with values.
function interpolateString(string, values) {
    return string.replace(/%(\d+)/g, (match, index) => {
        return (index < values.length) ? values[index] : match;
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/context.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/context
 */





/**
 * Provides a common, higher-level environment for solutions that use multiple {@link module:core/editor/editor~Editor editors}
 * or plugins that work outside the editor. Use it instead of {@link module:core/editor/editor~Editor.create `Editor.create()`}
 * in advanced application integrations.
 *
 * All configuration options passed to a context will be used as default options for editor instances initialized in that context.
 *
 * {@link module:core/contextplugin~ContextPlugin Context plugins} passed to a context instance will be shared among all
 * editor instances initialized in this context. These will be the same plugin instances for all the editors.
 *
 * **Note:** The context can only be initialized with {@link module:core/contextplugin~ContextPlugin context plugins}
 * (e.g. [comments](https://ckeditor.com/collaboration/comments/)). Regular {@link module:core/plugin~Plugin plugins} require an
 * editor instance to work and cannot be added to a context.
 *
 * **Note:** You can add a context plugin to an editor instance, though.
 *
 * If you are using multiple editor instances on one page and use any context plugins, create a context to share the configuration and
 * plugins among these editors. Some plugins will use the information about all existing editors to better integrate between them.
 *
 * If you are using plugins that do not require an editor to work (e.g. [comments](https://ckeditor.com/collaboration/comments/)),
 * enable and configure them using the context.
 *
 * If you are using only a single editor on each page, use {@link module:core/editor/editor~Editor.create `Editor.create()`} instead.
 * In such case, a context instance will be created by the editor instance in a transparent way.
 *
 * See {@link module:core/context~Context.create `Context.create()`} for usage examples.
 */
class Context {
    /**
     * Creates a context instance with a given configuration.
     *
     * Usually not to be used directly. See the static {@link module:core/context~Context.create `create()`} method.
     *
     * @param {Object} [config={}] The context configuration.
     */
    constructor(config) {
        /**
         * Stores all the configurations specific to this context instance.
         *
         * @readonly
         * @type {module:utils/config~Config}
         */
        this.config = new Config(config, this.constructor.defaultConfig);
        const availablePlugins = this.constructor.builtinPlugins;
        this.config.define('plugins', availablePlugins);
        /**
         * The plugins loaded and in use by this context instance.
         *
         * @readonly
         * @type {module:core/plugincollection~PluginCollection}
         */
        this.plugins = new PluginCollection(this, availablePlugins);
        const languageConfig = this.config.get('language') || {};
        /**
         * @readonly
         * @type {module:utils/locale~Locale}
         */
        this.locale = new Locale({
            uiLanguage: typeof languageConfig === 'string' ? languageConfig : languageConfig.ui,
            contentLanguage: this.config.get('language.content')
        });
        /**
         * Shorthand for {@link module:utils/locale~Locale#t}.
         *
         * @see module:utils/locale~Locale#t
         * @method #t
         */
        this.t = this.locale.t;
        /**
         * A list of editors that this context instance is injected to.
         *
         * @readonly
         * @type {module:utils/collection~Collection}
         */
        this.editors = new Collection();
        /**
         * Reference to the editor which created the context.
         * Null when the context was created outside of the editor.
         *
         * It is used to destroy the context when removing the editor that has created the context.
         *
         * @private
         * @type {module:core/editor/editor~Editor|null}
         */
        this._contextOwner = null;
    }
    /**
     * Loads and initializes plugins specified in the configuration.
     *
     * @returns {Promise.<module:core/plugin~LoadedPlugins>} A promise which resolves
     * once the initialization is completed, providing an array of loaded plugins.
     */
    initPlugins() {
        const plugins = this.config.get('plugins') || [];
        const substitutePlugins = this.config.get('substitutePlugins') || [];
        // Plugins for substitution should be checked as well.
        for (const Plugin of plugins.concat(substitutePlugins)) {
            if (typeof Plugin != 'function') {
                /**
                 * Only a constructor function is allowed as a {@link module:core/contextplugin~ContextPlugin context plugin}.
                 *
                 * @error context-initplugins-constructor-only
                 */
                throw new CKEditorError('context-initplugins-constructor-only', null, { Plugin });
            }
            if (Plugin.isContextPlugin !== true) {
                /**
                 * Only a plugin marked as a {@link module:core/contextplugin~ContextPlugin.isContextPlugin context plugin}
                 * is allowed to be used with a context.
                 *
                 * @error context-initplugins-invalid-plugin
                 */
                throw new CKEditorError('context-initplugins-invalid-plugin', null, { Plugin });
            }
        }
        return this.plugins.init(plugins, [], substitutePlugins);
    }
    /**
     * Destroys the context instance and all editors used with the context,
     * releasing all resources used by the context.
     *
     * @returns {Promise} A promise that resolves once the context instance is fully destroyed.
     */
    destroy() {
        return Promise.all(Array.from(this.editors, editor => editor.destroy()))
            .then(() => this.plugins.destroy());
    }
    /**
     * Adds a reference to the editor which is used with this context.
     *
     * When the given editor has created the context, the reference to this editor will be stored
     * as a {@link ~Context#_contextOwner}.
     *
     * This method should only be used by the editor.
     *
     * @protected
     * @param {module:core/editor/editor~Editor} editor
     * @param {Boolean} isContextOwner Stores the given editor as a context owner.
     */
    _addEditor(editor, isContextOwner) {
        if (this._contextOwner) {
            /**
             * Cannot add multiple editors to the context which is created by the editor.
             *
             * @error context-addeditor-private-context
             */
            throw new CKEditorError('context-addeditor-private-context');
        }
        this.editors.add(editor);
        if (isContextOwner) {
            this._contextOwner = editor;
        }
    }
    /**
     * Removes a reference to the editor which was used with this context.
     * When the context was created by the given editor, the context will be destroyed.
     *
     * This method should only be used by the editor.
     *
     * @protected
     * @param {module:core/editor/editor~Editor} editor
     * @return {Promise} A promise that resolves once the editor is removed from the context or when the context was destroyed.
     */
    _removeEditor(editor) {
        if (this.editors.has(editor)) {
            this.editors.remove(editor);
        }
        if (this._contextOwner === editor) {
            return this.destroy();
        }
        return Promise.resolve();
    }
    /**
     * Returns the context configuration which will be copied to the editors created using this context.
     *
     * The configuration returned by this method has the plugins configuration removed &mdash; plugins are shared with all editors
     * through another mechanism.
     *
     * This method should only be used by the editor.
     *
     * @protected
     * @returns {Object} Configuration as a plain object.
     */
    _getEditorConfig() {
        const result = {};
        for (const name of this.config.names()) {
            if (!['plugins', 'removePlugins', 'extraPlugins'].includes(name)) {
                result[name] = this.config.get(name);
            }
        }
        return result;
    }
    /**
     * Creates and initializes a new context instance.
     *
     *		const commonConfig = { ... }; // Configuration for all the plugins and editors.
     *		const editorPlugins = [ ... ]; // Regular plugins here.
     *
     *		Context
     *			.create( {
     *				// Only context plugins here.
     *				plugins: [ ... ],
     *
     *				// Configure the language for all the editors (it cannot be overwritten).
     *				language: { ... },
     *
     *				// Configuration for context plugins.
     *				comments: { ... },
     *				...
     *
     *				// Default configuration for editor plugins.
     *				toolbar: { ... },
     *				image: { ... },
     *				...
     *			} )
     *			.then( context => {
     *				const promises = [];
     *
     *				promises.push( ClassicEditor.create(
     *					document.getElementById( 'editor1' ),
     *					{
     *						editorPlugins,
     *						context
     *					}
     *				) );
     *
     *				promises.push( ClassicEditor.create(
     *					document.getElementById( 'editor2' ),
     *					{
     *						editorPlugins,
     *						context,
     *						toolbar: { ... } // You can overwrite the configuration of the context.
     *					}
     *				) );
     *
     *				return Promise.all( promises );
     *			} );
     *
     * @param {Object} [config] The context configuration.
     * @returns {Promise} A promise resolved once the context is ready. The promise resolves with the created context instance.
     */
    static create(config) {
        return new Promise(resolve => {
            const context = new this(config);
            resolve(context.initPlugins().then(() => context));
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/contextplugin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/contextplugin
 */

/**
 * The base class for {@link module:core/context~Context} plugin classes.
 *
 * A context plugin can either be initialized for an {@link module:core/editor/editor~Editor editor} or for
 * a {@link module:core/context~Context context}. In other words, it can either
 * work within one editor instance or with one or more editor instances that use a single context.
 * It is the context plugin's role to implement handling for both modes.
 *
 * There are a few rules for interaction between the editor plugins and context plugins:
 *
 * * A context plugin can require another context plugin.
 * * An {@link module:core/plugin~Plugin editor plugin} can require a context plugin.
 * * A context plugin MUST NOT require an {@link module:core/plugin~Plugin editor plugin}.
 *
 * @implements module:core/plugin~PluginInterface
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class ContextPlugin extends Observable {
    /**
     * Creates a new plugin instance.
     *
     * @param {module:core/context~Context|module:core/editor/editor~Editor} context
     */
    constructor(context) {
        super();
        /**
         * The context instance.
         *
         * @readonly
         * @type {module:core/context~Context|module:core/editor/editor~Editor}
         */
        this.context = context;
    }
    /**
     * @inheritDoc
     */
    destroy() {
        this.stopListening();
    }
    /**
     * @inheritDoc
     */
    static get isContextPlugin() {
        return true;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/typecheckable.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/typecheckable
 */
class TypeCheckable {
    /* istanbul ignore next */
    is() {
        // There are a lot of overloads above.
        // Overriding method in derived classes remove them and only `is( type: string ): boolean` is visible which we don't want.
        // One option would be to copy them all to all classes, but that's ugly.
        // It's best when TypeScript compiler doesn't see those overloads, except the one in the top base class.
        // To overload a method, but not let the compiler see it, do after class definition:
        // `MyClass.prototype.is = function( type: string ) {...}`
        throw new Error('is() method is abstract');
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/comparearrays.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/comparearrays
 */
/**
 * Compares how given arrays relate to each other. One array can be: same as another array, prefix of another array
 * or completely different. If arrays are different, first index at which they differ is returned. Otherwise,
 * a flag specifying the relation is returned. Flags are negative numbers, so whenever a number >= 0 is returned
 * it means that arrays differ.
 *
 *		compareArrays( [ 0, 2 ], [ 0, 2 ] );		// 'same'
 *		compareArrays( [ 0, 2 ], [ 0, 2, 1 ] );		// 'prefix'
 *		compareArrays( [ 0, 2 ], [ 0 ] );			// 'extension'
 *		compareArrays( [ 0, 2 ], [ 1, 2 ] );		// 0
 *		compareArrays( [ 0, 2 ], [ 0, 1 ] );		// 1
 *
 * @param {Array} a Array that is compared.
 * @param {Array} b Array to compare with.
 * @returns {module:utils/comparearrays~ArrayRelation|Number} How array `a` is related to `b`.
 */
function compareArrays(a, b) {
    const minLen = Math.min(a.length, b.length);
    for (let i = 0; i < minLen; i++) {
        if (a[i] != b[i]) {
            // The arrays are different.
            return i;
        }
    }
    // Both arrays were same at all points.
    if (a.length == b.length) {
        // If their length is also same, they are the same.
        return 'same';
    }
    else if (a.length < b.length) {
        // Compared array is shorter so it is a prefix of the other array.
        return 'prefix';
    }
    else {
        // Compared array is longer so it is an extension of the other array.
        return 'extension';
    }
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/clone.js


/** Used to compose bitmasks for cloning. */
var clone_CLONE_SYMBOLS_FLAG = 4;

/**
 * Creates a shallow clone of `value`.
 *
 * **Note:** This method is loosely based on the
 * [structured clone algorithm](https://mdn.io/Structured_clone_algorithm)
 * and supports cloning arrays, array buffers, booleans, date objects, maps,
 * numbers, `Object` objects, regexes, sets, strings, symbols, and typed
 * arrays. The own enumerable properties of `arguments` objects are cloned
 * as plain objects. An empty object is returned for uncloneable values such
 * as error objects, functions, DOM nodes, and WeakMaps.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to clone.
 * @returns {*} Returns the cloned value.
 * @see _.cloneDeep
 * @example
 *
 * var objects = [{ 'a': 1 }, { 'b': 2 }];
 *
 * var shallow = _.clone(objects);
 * console.log(shallow[0] === objects[0]);
 * // => true
 */
function clone(value) {
  return _baseClone(value, clone_CLONE_SYMBOLS_FLAG);
}

/* harmony default export */ const lodash_es_clone = (clone);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/node.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/view/node
 */





// To check if component is loaded more than once.

/**
 * Abstract view node class.
 *
 * This is an abstract class. Its constructor should not be used directly.
 * Use the {@link module:engine/view/downcastwriter~DowncastWriter} or {@link module:engine/view/upcastwriter~UpcastWriter}
 * to create new instances of view nodes.
 *
 * @abstract
 */
class node_Node extends EmitterMixin(TypeCheckable) {
    /**
     * Creates a tree view node.
     *
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this node belongs.
     */
    constructor(document) {
        super();
        /**
         * The document instance to which this node belongs.
         *
         * @readonly
         * @member {module:engine/view/document~Document}
         */
        this.document = document;
        /**
         * Parent element. Null by default. Set by {@link module:engine/view/element~Element#_insertChild}.
         *
         * @readonly
         * @member {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment|null}
         */
        this.parent = null;
    }
    /**
     * Index of the node in the parent element or null if the node has no parent.
     *
     * Accessing this property throws an error if this node's parent element does not contain it.
     * This means that view tree got broken.
     *
     * @readonly
     * @type {Number|null}
     */
    get index() {
        let pos;
        if (!this.parent) {
            return null;
        }
        // No parent or child doesn't exist in parent's children.
        if ((pos = this.parent.getChildIndex(this)) == -1) {
            /**
             * The node's parent does not contain this node. It means that the document tree is corrupted.
             *
             * @error view-node-not-found-in-parent
             */
            throw new CKEditorError('view-node-not-found-in-parent', this);
        }
        return pos;
    }
    /**
     * Node's next sibling, or `null` if it is the last child.
     *
     * @readonly
     * @type {module:engine/view/node~Node|null}
     */
    get nextSibling() {
        const index = this.index;
        return (index !== null && this.parent.getChild(index + 1)) || null;
    }
    /**
     * Node's previous sibling, or `null` if it is the first child.
     *
     * @readonly
     * @type {module:engine/view/node~Node|null}
     */
    get previousSibling() {
        const index = this.index;
        return (index !== null && this.parent.getChild(index - 1)) || null;
    }
    /**
     * Top-most ancestor of the node. If the node has no parent it is the root itself.
     *
     * @readonly
     * @type {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
     */
    get root() {
        // eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
        let root = this;
        while (root.parent) {
            root = root.parent;
        }
        return root;
    }
    /**
     * Returns true if the node is in a tree rooted in the document (is a descendant of one of its roots).
     *
     * @returns {Boolean}
     */
    isAttached() {
        return this.root.is('rootElement');
    }
    /**
     * Gets a path to the node. The path is an array containing indices of consecutive ancestors of this node,
     * beginning from {@link module:engine/view/node~Node#root root}, down to this node's index.
     *
     *		const abc = downcastWriter.createText( 'abc' );
     *		const foo = downcastWriter.createText( 'foo' );
     *		const h1 = downcastWriter.createElement( 'h1', null, downcastWriter.createText( 'header' ) );
     *		const p = downcastWriter.createElement( 'p', null, [ abc, foo ] );
     *		const div = downcastWriter.createElement( 'div', null, [ h1, p ] );
     *		foo.getPath(); // Returns [ 1, 3 ]. `foo` is in `p` which is in `div`. `p` starts at offset 1, while `foo` at 3.
     *		h1.getPath(); // Returns [ 0 ].
     *		div.getPath(); // Returns [].
     *
     * @returns {Array.<Number>} The path.
     */
    getPath() {
        const path = [];
        // eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
        let node = this;
        while (node.parent) {
            path.unshift(node.index);
            node = node.parent;
        }
        return path;
    }
    /**
     * Returns ancestors array of this node.
     *
     * @param {Object} options Options object.
     * @param {Boolean} [options.includeSelf=false] When set to `true` this node will be also included in parent's array.
     * @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from node's parent to root element,
     * otherwise root element will be the first item in the array.
     * @returns {Array} Array with ancestors.
     */
    getAncestors(options = {}) {
        const ancestors = [];
        let parent = options.includeSelf ? this : this.parent;
        while (parent) {
            ancestors[options.parentFirst ? 'push' : 'unshift'](parent);
            parent = parent.parent;
        }
        return ancestors;
    }
    /**
     * Returns a {@link module:engine/view/element~Element} or {@link module:engine/view/documentfragment~DocumentFragment}
     * which is a common ancestor of both nodes.
     *
     * @param {module:engine/view/node~Node} node The second node.
     * @param {Object} options Options object.
     * @param {Boolean} [options.includeSelf=false] When set to `true` both nodes will be considered "ancestors" too.
     * Which means that if e.g. node A is inside B, then their common ancestor will be B.
     * @returns {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment|null}
     */
    getCommonAncestor(node, options = {}) {
        const ancestorsA = this.getAncestors(options);
        const ancestorsB = node.getAncestors(options);
        let i = 0;
        while (ancestorsA[i] == ancestorsB[i] && ancestorsA[i]) {
            i++;
        }
        return i === 0 ? null : ancestorsA[i - 1];
    }
    /**
     * Returns whether this node is before given node. `false` is returned if nodes are in different trees (for example,
     * in different {@link module:engine/view/documentfragment~DocumentFragment}s).
     *
     * @param {module:engine/view/node~Node} node Node to compare with.
     * @returns {Boolean}
     */
    isBefore(node) {
        // Given node is not before this node if they are same.
        if (this == node) {
            return false;
        }
        // Return `false` if it is impossible to compare nodes.
        if (this.root !== node.root) {
            return false;
        }
        const thisPath = this.getPath();
        const nodePath = node.getPath();
        const result = compareArrays(thisPath, nodePath);
        switch (result) {
            case 'prefix':
                return true;
            case 'extension':
                return false;
            default:
                return thisPath[result] < nodePath[result];
        }
    }
    /**
     * Returns whether this node is after given node. `false` is returned if nodes are in different trees (for example,
     * in different {@link module:engine/view/documentfragment~DocumentFragment}s).
     *
     * @param {module:engine/view/node~Node} node Node to compare with.
     * @returns {Boolean}
     */
    isAfter(node) {
        // Given node is not before this node if they are same.
        if (this == node) {
            return false;
        }
        // Return `false` if it is impossible to compare nodes.
        if (this.root !== node.root) {
            return false;
        }
        // In other cases, just check if the `node` is before, and return the opposite.
        return !this.isBefore(node);
    }
    /**
     * Removes node from parent.
     *
     * @internal
     * @protected
     */
    _remove() {
        this.parent._removeChildren(this.index);
    }
    /**
     * @internal
     * @protected
     * @param {module:engine/view/document~ChangeType} type Type of the change.
     * @param {module:engine/view/node~Node} node Changed node.
     * @fires change
     */
    _fireChange(type, node) {
        this.fire(`change:${type}`, node);
        if (this.parent) {
            this.parent._fireChange(type, node);
        }
    }
    /**
     * Custom toJSON method to solve child-parent circular dependencies.
     *
     * @returns {Object} Clone of this object with the parent property removed.
     */
    toJSON() {
        const json = lodash_es_clone(this);
        // Due to circular references we need to remove parent reference.
        delete json.parent;
        return json;
    }
}
/**
 * Checks whether this object is of the given type.
 *
 * This method is useful when processing view objects that are of unknown type. For example, a function
 * may return a {@link module:engine/view/documentfragment~DocumentFragment} or a {@link module:engine/view/node~Node}
 * that can be either a text node or an element. This method can be used to check what kind of object is returned.
 *
 *		someObject.is( 'element' ); // -> true if this is an element
 *		someObject.is( 'node' ); // -> true if this is a node (a text node or an element)
 *		someObject.is( 'documentFragment' ); // -> true if this is a document fragment
 *
 * Since this method is also available on a range of model objects, you can prefix the type of the object with
 * `model:` or `view:` to check, for example, if this is the model's or view's element:
 *
 *		viewElement.is( 'view:element' ); // -> true
 *		viewElement.is( 'model:element' ); // -> false
 *
 * By using this method it is also possible to check a name of an element:
 *
 *		imgElement.is( 'element', 'img' ); // -> true
 *		imgElement.is( 'view:element', 'img' ); // -> same as above, but more precise
 *
 * The list of view objects which implement the `is()` method:
 *
 * * {@link module:engine/view/attributeelement~AttributeElement#is `AttributeElement#is()`}
 * * {@link module:engine/view/containerelement~ContainerElement#is `ContainerElement#is()`}
 * * {@link module:engine/view/documentfragment~DocumentFragment#is `DocumentFragment#is()`}
 * * {@link module:engine/view/documentselection~DocumentSelection#is `DocumentSelection#is()`}
 * * {@link module:engine/view/editableelement~EditableElement#is `EditableElement#is()`}
 * * {@link module:engine/view/element~Element#is `Element#is()`}
 * * {@link module:engine/view/emptyelement~EmptyElement#is `EmptyElement#is()`}
 * * {@link module:engine/view/node~Node#is `Node#is()`}
 * * {@link module:engine/view/position~Position#is `Position#is()`}
 * * {@link module:engine/view/range~Range#is `Range#is()`}
 * * {@link module:engine/view/rooteditableelement~RootEditableElement#is `RootEditableElement#is()`}
 * * {@link module:engine/view/selection~Selection#is `Selection#is()`}
 * * {@link module:engine/view/text~Text#is `Text#is()`}
 * * {@link module:engine/view/textproxy~TextProxy#is `TextProxy#is()`}
 * * {@link module:engine/view/uielement~UIElement#is `UIElement#is()`}
 *
 * @method #is
 * @param {String} type Type to check.
 * @returns {Boolean}
 */
node_Node.prototype.is = function (type) {
    return type === 'node' || type === 'view:node';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/text.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/text
 */

/**
 * Tree view text node.
 *
 * The constructor of this class should not be used directly. To create a new text node instance
 * use the {@link module:engine/view/downcastwriter~DowncastWriter#createText `DowncastWriter#createText()`}
 * method when working on data downcasted from the model or the
 * {@link module:engine/view/upcastwriter~UpcastWriter#createText `UpcastWriter#createText()`}
 * method when working on non-semantic views.
 *
 * @extends module:engine/view/node~Node
 */
class text_Text extends node_Node {
    /**
     * Creates a tree view text node.
     *
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this text node belongs.
     * @param {String} data The text's data.
     */
    constructor(document, data) {
        super(document);
        /**
         * The text content.
         *
         * Setting the data fires the {@link module:engine/view/node~Node#event:change:text change event}.
         *
         * @protected
         * @member {String} module:engine/view/text~Text#_textData
         */
        this._textData = data;
    }
    /**
     * The text content.
     *
     * @readonly
     * @type {String}
     */
    get data() {
        return this._textData;
    }
    /**
     * The `_data` property is controlled by a getter and a setter.
     *
     * The getter is required when using the addition assignment operator on protected property:
     *
     *		const foo = downcastWriter.createText( 'foo' );
     *		const bar = downcastWriter.createText( 'bar' );
     *
     *		foo._data += bar.data;   // executes: `foo._data = foo._data + bar.data`
     *		console.log( foo.data ); // prints: 'foobar'
     *
     * If the protected getter didn't exist, `foo._data` will return `undefined` and result of the merge will be invalid.
     *
     * The setter sets data and fires the {@link module:engine/view/node~Node#event:change:text change event}.
     *
     * @protected
     * @type {String}
     */
    get _data() {
        return this.data;
    }
    set _data(data) {
        this._fireChange('text', this);
        this._textData = data;
    }
    /**
     * Checks if this text node is similar to other text node.
     * Both nodes should have the same data to be considered as similar.
     *
     * @param {module:engine/view/node~Node} otherNode Node to check if it is same as this node.
     * @returns {Boolean}
     */
    isSimilar(otherNode) {
        if (!(otherNode instanceof text_Text)) {
            return false;
        }
        return this === otherNode || this.data === otherNode.data;
    }
    /**
     * Clones this node.
     *
     * @protected
     * @returns {module:engine/view/text~Text} Text node that is a clone of this node.
     */
    _clone() {
        return new text_Text(this.document, this.data);
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		text.is( '$text' ); // -> true
 *		text.is( 'node' ); // -> true
 *		text.is( 'view:$text' ); // -> true
 *		text.is( 'view:node' ); // -> true
 *
 *		text.is( 'model:$text' ); // -> false
 *		text.is( 'element' ); // -> false
 *		text.is( 'range' ); // -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * **Note:** Until version 20.0.0 this method wasn't accepting `'$text'` type. The legacy `'text'` type is still
 * accepted for backward compatibility.
 *
 * @param {String} type Type to check.
 * @returns {Boolean}
 */
text_Text.prototype.is = function (type) {
    return type === '$text' || type === 'view:$text' ||
        // This are legacy values kept for backward compatibility.
        type === 'text' || type === 'view:text' ||
        // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
        type === 'node' || type === 'view:node';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/textproxy.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/textproxy
 */


/**
 * TextProxy is a wrapper for substring of {@link module:engine/view/text~Text}. Instance of this class is created by
 * {@link module:engine/view/treewalker~TreeWalker} when only a part of {@link module:engine/view/text~Text} needs to be returned.
 *
 * `TextProxy` has an API similar to {@link module:engine/view/text~Text Text} and allows to do most of the common tasks performed
 * on view nodes.
 *
 * **Note:** Some `TextProxy` instances may represent whole text node, not just a part of it.
 * See {@link module:engine/view/textproxy~TextProxy#isPartial}.
 *
 * **Note:** `TextProxy` is a readonly interface.
 *
 * **Note:** `TextProxy` instances are created on the fly basing on the current state of parent {@link module:engine/view/text~Text}.
 * Because of this it is highly unrecommended to store references to `TextProxy instances because they might get
 * invalidated due to operations on Document. Also TextProxy is not a {@link module:engine/view/node~Node} so it can not be
 * inserted as a child of {@link module:engine/view/element~Element}.
 *
 * `TextProxy` instances are created by {@link module:engine/view/treewalker~TreeWalker view tree walker}. You should not need to create
 * an instance of this class by your own.
 */
class TextProxy extends TypeCheckable {
    /**
     * Creates a text proxy.
     *
     * @protected
     * @param {module:engine/view/text~Text} textNode Text node which part is represented by this text proxy.
     * @param {Number} offsetInText Offset in {@link module:engine/view/textproxy~TextProxy#textNode text node}
     * from which the text proxy starts.
     * @param {Number} length Text proxy length, that is how many text node's characters, starting from `offsetInText` it represents.
     * @constructor
     */
    constructor(textNode, offsetInText, length) {
        super();
        /**
         * Reference to the {@link module:engine/view/text~Text} element which TextProxy is a substring.
         *
         * @readonly
         * @member {module:engine/view/text~Text} module:engine/view/textproxy~TextProxy#textNode
         */
        this.textNode = textNode;
        if (offsetInText < 0 || offsetInText > textNode.data.length) {
            /**
             * Given offsetInText value is incorrect.
             *
             * @error view-textproxy-wrong-offsetintext
             */
            throw new CKEditorError('view-textproxy-wrong-offsetintext', this);
        }
        if (length < 0 || offsetInText + length > textNode.data.length) {
            /**
             * Given length value is incorrect.
             *
             * @error view-textproxy-wrong-length
             */
            throw new CKEditorError('view-textproxy-wrong-length', this);
        }
        /**
         * Text data represented by this text proxy.
         *
         * @readonly
         * @member {String} module:engine/view/textproxy~TextProxy#data
         */
        this.data = textNode.data.substring(offsetInText, offsetInText + length);
        /**
         * Offset in the `textNode` where this `TextProxy` instance starts.
         *
         * @readonly
         * @member {Number} module:engine/view/textproxy~TextProxy#offsetInText
         */
        this.offsetInText = offsetInText;
    }
    /**
     * Offset size of this node.
     *
     * @readonly
     * @type {Number}
     */
    get offsetSize() {
        return this.data.length;
    }
    /**
     * Flag indicating whether `TextProxy` instance covers only part of the original {@link module:engine/view/text~Text text node}
     * (`true`) or the whole text node (`false`).
     *
     * This is `false` when text proxy starts at the very beginning of {@link module:engine/view/textproxy~TextProxy#textNode textNode}
     * ({@link module:engine/view/textproxy~TextProxy#offsetInText offsetInText} equals `0`) and text proxy sizes is equal to
     * text node size.
     *
     * @readonly
     * @type {Boolean}
     */
    get isPartial() {
        return this.data.length !== this.textNode.data.length;
    }
    /**
     * Parent of this text proxy, which is same as parent of text node represented by this text proxy.
     *
     * @readonly
     * @type {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment|null}
     */
    get parent() {
        return this.textNode.parent;
    }
    /**
     * Root of this text proxy, which is same as root of text node represented by this text proxy.
     *
     * @readonly
     * @type {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
     */
    get root() {
        return this.textNode.root;
    }
    /**
     * {@link module:engine/view/document~Document View document} that owns this text proxy, or `null` if the text proxy is inside
     * {@link module:engine/view/documentfragment~DocumentFragment document fragment}.
     *
     * @readonly
     * @type {module:engine/view/document~Document|null}
     */
    get document() {
        return this.textNode.document;
    }
    /**
     * Returns ancestors array of this text proxy.
     *
     * @param {Object} options Options object.
     * @param {Boolean} [options.includeSelf=false] When set to `true` {#textNode} will be also included in parent's array.
     * @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from text proxy parent to
     * root element, otherwise root element will be the first item in the array.
     * @returns {Array} Array with ancestors.
     */
    getAncestors(options = {}) {
        const ancestors = [];
        let parent = options.includeSelf ? this.textNode : this.parent;
        while (parent !== null) {
            ancestors[options.parentFirst ? 'push' : 'unshift'](parent);
            parent = parent.parent;
        }
        return ancestors;
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		textProxy.is( '$textProxy' ); // -> true
 *		textProxy.is( 'view:$textProxy' ); // -> true
 *
 *		textProxy.is( 'model:$textProxy' ); // -> false
 *		textProxy.is( 'element' ); // -> false
 *		textProxy.is( 'range' ); // -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * **Note:** Until version 20.0.0 this method wasn't accepting `'$textProxy'` type. The legacy `'textProxy'` type is still
 * accepted for backward compatibility.
 *
 * @param {String} type Type to check.
 * @returns {Boolean}
 */
TextProxy.prototype.is = function (type) {
    return type === '$textProxy' || type === 'view:$textProxy' ||
        // This are legacy values kept for backward compatibility.
        type === 'textProxy' || type === 'view:textProxy';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/objecttomap.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/objecttomap
 */
/**
 * Transforms object to map.
 *
 *		const map = objectToMap( { 'foo': 1, 'bar': 2 } );
 *		map.get( 'foo' ); // 1
 *
 * **Note**: For mixed data (`Object` or `Iterable`) there's a dedicated {@link module:utils/tomap~toMap} function.
 *
 * @param {Object|null} obj Object to transform.
 * @returns {Map} Map created from object.
 */
function objectToMap(obj) {
    const map = new Map();
    for (const key in obj) {
        map.set(key, obj[key]);
    }
    return map;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/tomap.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/tomap
 */


/**
 * Transforms object or iterable to map. Iterable needs to be in the format acceptable by the `Map` constructor.
 *
 *		map = toMap( { 'foo': 1, 'bar': 2 } );
 *		map = toMap( [ [ 'foo', 1 ], [ 'bar', 2 ] ] );
 *		map = toMap( anotherMap );
 *
 * @param {Object|Iterable|null} data Object or iterable to transform.
 * @returns {Map} Map created from data.
 */
function toMap(data) {
    if (isIterable(data)) {
        return new Map(data);
    }
    else {
        return objectToMap(data);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/matcher.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */


/**
 * View matcher class.
 * Instance of this class can be used to find {@link module:engine/view/element~Element elements} that match given pattern.
 */
class Matcher {
    /**
     * Creates new instance of Matcher.
     *
     * @param {String|RegExp|Object|Function} [pattern] Match patterns. See {@link module:engine/view/matcher~Matcher#add add method} for
     * more information.
     */
    constructor(...pattern) {
        /**
         * @private
         * @type {Array<Object|Function>}
         */
        this._patterns = [];
        this.add(...pattern);
    }
    /**
     * Adds pattern or patterns to matcher instance.
     *
     *		// String.
     *		matcher.add( 'div' );
     *
     *		// Regular expression.
     *		matcher.add( /^\w/ );
     *
     *		// Single class.
     *		matcher.add( {
     *			classes: 'foobar'
     *		} );
     *
     * See {@link module:engine/view/matcher~MatcherPattern} for more examples.
     *
     * Multiple patterns can be added in one call:
     *
     * 		matcher.add( 'div', { classes: 'foobar' } );
     *
     * @param {Object|String|RegExp|Function} pattern Object describing pattern details. If string or regular expression
     * is provided it will be used to match element's name. Pattern can be also provided in a form
     * of a function - then this function will be called with each {@link module:engine/view/element~Element element} as a parameter.
     * Function's return value will be stored under `match` key of the object returned from
     * {@link module:engine/view/matcher~Matcher#match match} or {@link module:engine/view/matcher~Matcher#matchAll matchAll} methods.
     * @param {String|RegExp} [pattern.name] Name or regular expression to match element's name.
     * @param {Object} [pattern.attributes] Object with key-value pairs representing attributes to match. Each object key
     * represents attribute name. Value under that key can be either:
     * * `true` - then attribute is just required (can be empty),
     * * a string - then attribute has to be equal, or
     * * a regular expression - then attribute has to match the expression.
     * @param {String|RegExp|Array} [pattern.classes] Class name or array of class names to match. Each name can be
     * provided in a form of string or regular expression.
     * @param {Object} [pattern.styles] Object with key-value pairs representing styles to match. Each object key
     * represents style name. Value under that key can be either a string or a regular expression and it will be used
     * to match style value.
     */
    add(...pattern) {
        for (let item of pattern) {
            // String or RegExp pattern is used as element's name.
            if (typeof item == 'string' || item instanceof RegExp) {
                item = { name: item };
            }
            this._patterns.push(item);
        }
    }
    /**
     * Matches elements for currently stored patterns. Returns match information about first found
     * {@link module:engine/view/element~Element element}, otherwise returns `null`.
     *
     * Example of returned object:
     *
     *		{
     *			element: <instance of found element>,
     *			pattern: <pattern used to match found element>,
     *			match: {
     *				name: true,
     *				attributes: [ 'title', 'href' ],
     *				classes: [ 'foo' ],
     *				styles: [ 'color', 'position' ]
     *			}
     *		}
     *
     * @see module:engine/view/matcher~Matcher#add
     * @see module:engine/view/matcher~Matcher#matchAll
     * @param {...module:engine/view/element~Element} element View element to match against stored patterns.
     * @returns {Object|null} result
     * @returns {module:engine/view/element~Element} result.element Matched view element.
     * @returns {Object|String|RegExp|Function} result.pattern Pattern that was used to find matched element.
     * @returns {Object} result.match Object representing matched element parts.
     * @returns {Boolean} [result.match.name] True if name of the element was matched.
     * @returns {Array} [result.match.attributes] Array with matched attribute names.
     * @returns {Array} [result.match.classes] Array with matched class names.
     * @returns {Array} [result.match.styles] Array with matched style names.
     */
    match(...element) {
        for (const singleElement of element) {
            for (const pattern of this._patterns) {
                const match = isElementMatching(singleElement, pattern);
                if (match) {
                    return {
                        element: singleElement,
                        pattern,
                        match
                    };
                }
            }
        }
        return null;
    }
    /**
     * Matches elements for currently stored patterns. Returns array of match information with all found
     * {@link module:engine/view/element~Element elements}. If no element is found - returns `null`.
     *
     * @see module:engine/view/matcher~Matcher#add
     * @see module:engine/view/matcher~Matcher#match
     * @param {...module:engine/view/element~Element} element View element to match against stored patterns.
     * @returns {Array.<Object>|null} Array with match information about found elements or `null`. For more information
     * see {@link module:engine/view/matcher~Matcher#match match method} description.
     */
    matchAll(...element) {
        const results = [];
        for (const singleElement of element) {
            for (const pattern of this._patterns) {
                const match = isElementMatching(singleElement, pattern);
                if (match) {
                    results.push({
                        element: singleElement,
                        pattern,
                        match
                    });
                }
            }
        }
        return results.length > 0 ? results : null;
    }
    /**
     * Returns the name of the element to match if there is exactly one pattern added to the matcher instance
     * and it matches element name defined by `string` (not `RegExp`). Otherwise, returns `null`.
     *
     * @returns {String|null} Element name trying to match.
     */
    getElementName() {
        if (this._patterns.length !== 1) {
            return null;
        }
        const pattern = this._patterns[0];
        const name = pattern.name;
        return (typeof pattern != 'function' && name && !(name instanceof RegExp)) ? name : null;
    }
}
// Returns match information if {@link module:engine/view/element~Element element} is matching provided pattern.
// If element cannot be matched to provided pattern - returns `null`.
//
// @param {module:engine/view/element~Element} element
// @param {Object|String|RegExp|Function} pattern
// @returns {Object|null} Returns object with match information or null if element is not matching.
function isElementMatching(element, pattern) {
    // If pattern is provided as function - return result of that function;
    if (typeof pattern == 'function') {
        return pattern(element);
    }
    const match = {};
    // Check element's name.
    if (pattern.name) {
        match.name = matchName(pattern.name, element.name);
        if (!match.name) {
            return null;
        }
    }
    // Check element's attributes.
    if (pattern.attributes) {
        match.attributes = matchAttributes(pattern.attributes, element);
        if (!match.attributes) {
            return null;
        }
    }
    // Check element's classes.
    if (pattern.classes) {
        match.classes = matchClasses(pattern.classes, element);
        if (!match.classes) {
            return null;
        }
    }
    // Check element's styles.
    if (pattern.styles) {
        match.styles = matchStyles(pattern.styles, element);
        if (!match.styles) {
            return null;
        }
    }
    return match;
}
// Checks if name can be matched by provided pattern.
//
// @param {String|RegExp} pattern
// @param {String} name
// @returns {Boolean} Returns `true` if name can be matched, `false` otherwise.
function matchName(pattern, name) {
    // If pattern is provided as RegExp - test against this regexp.
    if (pattern instanceof RegExp) {
        return !!name.match(pattern);
    }
    return pattern === name;
}
// Checks if an array of key/value pairs can be matched against provided patterns.
//
// Patterns can be provided in a following ways:
// 	- a boolean value matches any attribute with any value (or no value):
//
//			pattern: true
//
//	- a RegExp expression or object matches any attribute name:
//
//			pattern: /h[1-6]/
//
//	- an object matches any attribute that has the same name as the object item's key, where object item's value is:
//		- equal to `true`, which matches any attribute value:
//
//			pattern: {
//				required: true
//			}
//
//		- a string that is equal to attribute value:
//
//			pattern: {
//				rel: 'nofollow'
//			}
//
//		- a regular expression that matches attribute value,
//
//			pattern: {
//				src: /https.*/
//			}
//
//	- an array with items, where the item is:
//		- a string that is equal to attribute value:
//
//			pattern: [ 'data-property-1', 'data-property-2' ],
//
//		- an object with `key` and `value` property, where `key` is a regular expression matching attribute name and
//		  `value` is either regular expression matching attribute value or a string equal to attribute value:
//
//			pattern: [
//				{ key: /data-property-.*/, value: true },
//				// or:
//				{ key: /data-property-.*/, value: 'foobar' },
//				// or:
//				{ key: /data-property-.*/, value: /foo.*/ }
//			]
//
// @param {Object} patterns Object with information about attributes to match.
// @param {Iterable.<String>} keys Attribute, style or class keys.
// @param {Function} valueGetter A function providing value for a given item key.
// @returns {Array|null} Returns array with matched attribute names or `null` if no attributes were matched.
function matchPatterns(patterns, keys, valueGetter) {
    const normalizedPatterns = normalizePatterns(patterns);
    const normalizedItems = Array.from(keys);
    const match = [];
    normalizedPatterns.forEach(([patternKey, patternValue]) => {
        normalizedItems.forEach(itemKey => {
            if (isKeyMatched(patternKey, itemKey) &&
                isValueMatched(patternValue, itemKey, valueGetter)) {
                match.push(itemKey);
            }
        });
    });
    // Return matches only if there are at least as many of them as there are patterns.
    // The RegExp pattern can match more than one item.
    if (!normalizedPatterns.length || match.length < normalizedPatterns.length) {
        return undefined;
    }
    return match;
}
// Bring all the possible pattern forms to an array of arrays where first item is a key and second is a value.
//
// Examples:
//
// Boolean pattern value:
//
//		true
//
// to
//
//		[ [ true, true ] ]
//
// Textual pattern value:
//
//		'attribute-name-or-class-or-style'
//
// to
//
//		[ [ 'attribute-name-or-class-or-style', true ] ]
//
// Regular expression:
//
//		/^data-.*$/
//
// to
//
//		[ [ /^data-.*$/, true ] ]
//
// Objects (plain or with `key` and `value` specified explicitly):
//
//		{
//			src: /^https:.*$/
//		}
//
// or
//
//		[ {
//			key: 'src',
//			value: /^https:.*$/
//		} ]
//
// to:
//
//		[ [ 'src', /^https:.*$/ ] ]
//
// @param {Object|Array} patterns
// @returns {Array|null} Returns an array of objects or null if provided patterns were not in an expected form.
function normalizePatterns(patterns) {
    if (Array.isArray(patterns)) {
        return patterns.map((pattern) => {
            if (lodash_es_isPlainObject(pattern)) {
                if (pattern.key === undefined || pattern.value === undefined) {
                    // Documented at the end of matcher.js.
                    logWarning('matcher-pattern-missing-key-or-value', pattern);
                }
                return [pattern.key, pattern.value];
            }
            // Assume the pattern is either String or RegExp.
            return [pattern, true];
        });
    }
    if (lodash_es_isPlainObject(patterns)) {
        return Object.entries(patterns);
    }
    // Other cases (true, string or regexp).
    return [[patterns, true]];
}
// @param {String|RegExp} patternKey A pattern representing a key we want to match.
// @param {String} itemKey An actual item key (e.g. `'src'`, `'background-color'`, `'ck-widget'`) we're testing against pattern.
// @returns {Boolean}
function isKeyMatched(patternKey, itemKey) {
    return patternKey === true ||
        patternKey === itemKey ||
        patternKey instanceof RegExp && itemKey.match(patternKey);
}
// @param {String|RegExp} patternValue A pattern representing a value we want to match.
// @param {String} itemKey An item key, e.g. `background`, `href`, 'rel', etc.
// @param {Function} valueGetter A function used to provide a value for a given `itemKey`.
// @returns {Boolean}
function isValueMatched(patternValue, itemKey, valueGetter) {
    if (patternValue === true) {
        return true;
    }
    const itemValue = valueGetter(itemKey);
    // For now, the reducers are not returning the full tree of properties.
    // Casting to string preserves the old behavior until the root cause is fixed.
    // More can be found in https://github.com/ckeditor/ckeditor5/issues/10399.
    return patternValue === itemValue ||
        patternValue instanceof RegExp && !!String(itemValue).match(patternValue);
}
// Checks if attributes of provided element can be matched against provided patterns.
//
// @param {Object} patterns Object with information about attributes to match. Each key of the object will be
// used as attribute name. Value of each key can be a string or regular expression to match against attribute value.
// @param {module:engine/view/element~Element} element Element which attributes will be tested.
// @returns {Array|null} Returns array with matched attribute names or `null` if no attributes were matched.
function matchAttributes(patterns, element) {
    const attributeKeys = new Set(element.getAttributeKeys());
    // `style` and `class` attribute keys are deprecated. Only allow them in object pattern
    // for backward compatibility.
    if (lodash_es_isPlainObject(patterns)) {
        if (patterns.style !== undefined) {
            // Documented at the end of matcher.js.
            logWarning('matcher-pattern-deprecated-attributes-style-key', patterns);
        }
        if (patterns.class !== undefined) {
            // Documented at the end of matcher.js.
            logWarning('matcher-pattern-deprecated-attributes-class-key', patterns);
        }
    }
    else {
        attributeKeys.delete('style');
        attributeKeys.delete('class');
    }
    return matchPatterns(patterns, attributeKeys, key => element.getAttribute(key));
}
// Checks if classes of provided element can be matched against provided patterns.
//
// @param {Array.<String|RegExp>} patterns Array of strings or regular expressions to match against element's classes.
// @param {module:engine/view/element~Element} element Element which classes will be tested.
// @returns {Array|null} Returns array with matched class names or `null` if no classes were matched.
function matchClasses(patterns, element) {
    // We don't need `getter` here because patterns for classes are always normalized to `[ className, true ]`.
    return matchPatterns(patterns, element.getClassNames(), /* istanbul ignore next */ () => { });
}
// Checks if styles of provided element can be matched against provided patterns.
//
// @param {Object} patterns Object with information about styles to match. Each key of the object will be
// used as style name. Value of each key can be a string or regular expression to match against style value.
// @param {module:engine/view/element~Element} element Element which styles will be tested.
// @returns {Array|null} Returns array with matched style names or `null` if no styles were matched.
function matchStyles(patterns, element) {
    return matchPatterns(patterns, element.getStyleNames(true), key => element.getStyle(key));
}
/**
 * The key-value matcher pattern is missing key or value. Both must be present.
 * Refer the documentation: {@link module:engine/view/matcher~MatcherPattern}.
 *
 * @param {Object} pattern Pattern with missing properties.
 * @error matcher-pattern-missing-key-or-value
 */
/**
 * The key-value matcher pattern for `attributes` option is using deprecated `style` key.
 *
 * Use `styles` matcher pattern option instead:
 *
 * 		// Instead of:
 * 		const pattern = {
 * 			attributes: {
 * 				key1: 'value1',
 * 				key2: 'value2',
 * 				style: /^border.*$/
 * 			}
 * 		}
 *
 * 		// Use:
 * 		const pattern = {
 * 			attributes: {
 * 				key1: 'value1',
 * 				key2: 'value2'
 * 			},
 * 			styles: /^border.*$/
 * 		}
 *
 * Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
 * and {@link module:engine/view/matcher~MatcherPattern} documentation.
 *
 * @param {Object} pattern Pattern with missing properties.
 * @error matcher-pattern-deprecated-attributes-style-key
 */
/**
 * The key-value matcher pattern for `attributes` option is using deprecated `class` key.
 *
 * Use `classes` matcher pattern option instead:
 *
 * 		// Instead of:
 * 		const pattern = {
 * 			attributes: {
 * 				key1: 'value1',
 * 				key2: 'value2',
 * 				class: 'foobar'
 * 			}
 * 		}
 *
 * 		// Use:
 * 		const pattern = {
 * 			attributes: {
 * 				key1: 'value1',
 * 				key2: 'value2'
 * 			},
 * 			classes: 'foobar'
 * 		}
 *
 * Refer to the {@glink updating/migration-to-29##migration-to-ckeditor-5-v2910 Migration to v29.1.0} guide
 * and the {@link module:engine/view/matcher~MatcherPattern} documentation.
 *
 * @param {Object} pattern Pattern with missing properties.
 * @error matcher-pattern-deprecated-attributes-class-key
 */

;// CONCATENATED MODULE: ./node_modules/lodash-es/isSymbol.js



/** `Object#toString` result references. */
var isSymbol_symbolTag = '[object Symbol]';

/**
 * Checks if `value` is classified as a `Symbol` primitive or object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a symbol, else `false`.
 * @example
 *
 * _.isSymbol(Symbol.iterator);
 * // => true
 *
 * _.isSymbol('abc');
 * // => false
 */
function isSymbol(value) {
  return typeof value == 'symbol' ||
    (lodash_es_isObjectLike(value) && _baseGetTag(value) == isSymbol_symbolTag);
}

/* harmony default export */ const lodash_es_isSymbol = (isSymbol);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_isKey.js



/** Used to match property names within property paths. */
var reIsDeepProp = /\.|\[(?:[^[\]]*|(["'])(?:(?!\1)[^\\]|\\.)*?\1)\]/,
    reIsPlainProp = /^\w*$/;

/**
 * Checks if `value` is a property name and not a property path.
 *
 * @private
 * @param {*} value The value to check.
 * @param {Object} [object] The object to query keys on.
 * @returns {boolean} Returns `true` if `value` is a property name, else `false`.
 */
function isKey(value, object) {
  if (lodash_es_isArray(value)) {
    return false;
  }
  var type = typeof value;
  if (type == 'number' || type == 'symbol' || type == 'boolean' ||
      value == null || lodash_es_isSymbol(value)) {
    return true;
  }
  return reIsPlainProp.test(value) || !reIsDeepProp.test(value) ||
    (object != null && value in Object(object));
}

/* harmony default export */ const _isKey = (isKey);

;// CONCATENATED MODULE: ./node_modules/lodash-es/memoize.js


/** Error message constants. */
var FUNC_ERROR_TEXT = 'Expected a function';

/**
 * Creates a function that memoizes the result of `func`. If `resolver` is
 * provided, it determines the cache key for storing the result based on the
 * arguments provided to the memoized function. By default, the first argument
 * provided to the memoized function is used as the map cache key. The `func`
 * is invoked with the `this` binding of the memoized function.
 *
 * **Note:** The cache is exposed as the `cache` property on the memoized
 * function. Its creation may be customized by replacing the `_.memoize.Cache`
 * constructor with one whose instances implement the
 * [`Map`](http://ecma-international.org/ecma-262/7.0/#sec-properties-of-the-map-prototype-object)
 * method interface of `clear`, `delete`, `get`, `has`, and `set`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to have its output memoized.
 * @param {Function} [resolver] The function to resolve the cache key.
 * @returns {Function} Returns the new memoized function.
 * @example
 *
 * var object = { 'a': 1, 'b': 2 };
 * var other = { 'c': 3, 'd': 4 };
 *
 * var values = _.memoize(_.values);
 * values(object);
 * // => [1, 2]
 *
 * values(other);
 * // => [3, 4]
 *
 * object.a = 2;
 * values(object);
 * // => [1, 2]
 *
 * // Modify the result cache.
 * values.cache.set(object, ['a', 'b']);
 * values(object);
 * // => ['a', 'b']
 *
 * // Replace `_.memoize.Cache`.
 * _.memoize.Cache = WeakMap;
 */
function memoize(func, resolver) {
  if (typeof func != 'function' || (resolver != null && typeof resolver != 'function')) {
    throw new TypeError(FUNC_ERROR_TEXT);
  }
  var memoized = function() {
    var args = arguments,
        key = resolver ? resolver.apply(this, args) : args[0],
        cache = memoized.cache;

    if (cache.has(key)) {
      return cache.get(key);
    }
    var result = func.apply(this, args);
    memoized.cache = cache.set(key, result) || cache;
    return result;
  };
  memoized.cache = new (memoize.Cache || _MapCache);
  return memoized;
}

// Expose `MapCache`.
memoize.Cache = _MapCache;

/* harmony default export */ const lodash_es_memoize = (memoize);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_memoizeCapped.js


/** Used as the maximum memoize cache size. */
var MAX_MEMOIZE_SIZE = 500;

/**
 * A specialized version of `_.memoize` which clears the memoized function's
 * cache when it exceeds `MAX_MEMOIZE_SIZE`.
 *
 * @private
 * @param {Function} func The function to have its output memoized.
 * @returns {Function} Returns the new memoized function.
 */
function memoizeCapped(func) {
  var result = lodash_es_memoize(func, function(key) {
    if (cache.size === MAX_MEMOIZE_SIZE) {
      cache.clear();
    }
    return key;
  });

  var cache = result.cache;
  return result;
}

/* harmony default export */ const _memoizeCapped = (memoizeCapped);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_stringToPath.js


/** Used to match property names within property paths. */
var rePropName = /[^.[\]]+|\[(?:(-?\d+(?:\.\d+)?)|(["'])((?:(?!\2)[^\\]|\\.)*?)\2)\]|(?=(?:\.|\[\])(?:\.|\[\]|$))/g;

/** Used to match backslashes in property paths. */
var reEscapeChar = /\\(\\)?/g;

/**
 * Converts `string` to a property path array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the property path array.
 */
var stringToPath = _memoizeCapped(function(string) {
  var result = [];
  if (string.charCodeAt(0) === 46 /* . */) {
    result.push('');
  }
  string.replace(rePropName, function(match, number, quote, subString) {
    result.push(quote ? subString.replace(reEscapeChar, '$1') : (number || match));
  });
  return result;
});

/* harmony default export */ const _stringToPath = (stringToPath);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_arrayMap.js
/**
 * A specialized version of `_.map` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @returns {Array} Returns the new mapped array.
 */
function arrayMap(array, iteratee) {
  var index = -1,
      length = array == null ? 0 : array.length,
      result = Array(length);

  while (++index < length) {
    result[index] = iteratee(array[index], index, array);
  }
  return result;
}

/* harmony default export */ const _arrayMap = (arrayMap);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseToString.js





/** Used as references for various `Number` constants. */
var INFINITY = 1 / 0;

/** Used to convert symbols to primitives and strings. */
var _baseToString_symbolProto = _Symbol ? _Symbol.prototype : undefined,
    symbolToString = _baseToString_symbolProto ? _baseToString_symbolProto.toString : undefined;

/**
 * The base implementation of `_.toString` which doesn't convert nullish
 * values to empty strings.
 *
 * @private
 * @param {*} value The value to process.
 * @returns {string} Returns the string.
 */
function baseToString(value) {
  // Exit early for strings to avoid a performance hit in some environments.
  if (typeof value == 'string') {
    return value;
  }
  if (lodash_es_isArray(value)) {
    // Recursively convert values (susceptible to call stack limits).
    return _arrayMap(value, baseToString) + '';
  }
  if (lodash_es_isSymbol(value)) {
    return symbolToString ? symbolToString.call(value) : '';
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -INFINITY) ? '-0' : result;
}

/* harmony default export */ const _baseToString = (baseToString);

;// CONCATENATED MODULE: ./node_modules/lodash-es/toString.js


/**
 * Converts `value` to a string. An empty string is returned for `null`
 * and `undefined` values. The sign of `-0` is preserved.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.toString(null);
 * // => ''
 *
 * _.toString(-0);
 * // => '-0'
 *
 * _.toString([1, 2, 3]);
 * // => '1,2,3'
 */
function toString_toString(value) {
  return value == null ? '' : _baseToString(value);
}

/* harmony default export */ const lodash_es_toString = (toString_toString);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_castPath.js





/**
 * Casts `value` to a path array if it's not one.
 *
 * @private
 * @param {*} value The value to inspect.
 * @param {Object} [object] The object to query keys on.
 * @returns {Array} Returns the cast property path array.
 */
function castPath(value, object) {
  if (lodash_es_isArray(value)) {
    return value;
  }
  return _isKey(value, object) ? [value] : _stringToPath(lodash_es_toString(value));
}

/* harmony default export */ const _castPath = (castPath);

;// CONCATENATED MODULE: ./node_modules/lodash-es/last.js
/**
 * Gets the last element of `array`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Array
 * @param {Array} array The array to query.
 * @returns {*} Returns the last element of `array`.
 * @example
 *
 * _.last([1, 2, 3]);
 * // => 3
 */
function last(array) {
  var length = array == null ? 0 : array.length;
  return length ? array[length - 1] : undefined;
}

/* harmony default export */ const lodash_es_last = (last);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_toKey.js


/** Used as references for various `Number` constants. */
var _toKey_INFINITY = 1 / 0;

/**
 * Converts `value` to a string key if it's not a string or symbol.
 *
 * @private
 * @param {*} value The value to inspect.
 * @returns {string|symbol} Returns the key.
 */
function toKey(value) {
  if (typeof value == 'string' || lodash_es_isSymbol(value)) {
    return value;
  }
  var result = (value + '');
  return (result == '0' && (1 / value) == -_toKey_INFINITY) ? '-0' : result;
}

/* harmony default export */ const _toKey = (toKey);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseGet.js



/**
 * The base implementation of `_.get` without support for default values.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Array|string} path The path of the property to get.
 * @returns {*} Returns the resolved value.
 */
function baseGet(object, path) {
  path = _castPath(path, object);

  var index = 0,
      length = path.length;

  while (object != null && index < length) {
    object = object[_toKey(path[index++])];
  }
  return (index && index == length) ? object : undefined;
}

/* harmony default export */ const _baseGet = (baseGet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseSlice.js
/**
 * The base implementation of `_.slice` without an iteratee call guard.
 *
 * @private
 * @param {Array} array The array to slice.
 * @param {number} [start=0] The start position.
 * @param {number} [end=array.length] The end position.
 * @returns {Array} Returns the slice of `array`.
 */
function baseSlice(array, start, end) {
  var index = -1,
      length = array.length;

  if (start < 0) {
    start = -start > length ? 0 : (length + start);
  }
  end = end > length ? length : end;
  if (end < 0) {
    end += length;
  }
  length = start > end ? 0 : ((end - start) >>> 0);
  start >>>= 0;

  var result = Array(length);
  while (++index < length) {
    result[index] = array[index + start];
  }
  return result;
}

/* harmony default export */ const _baseSlice = (baseSlice);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_parent.js



/**
 * Gets the parent value at `path` of `object`.
 *
 * @private
 * @param {Object} object The object to query.
 * @param {Array} path The path to get the parent value of.
 * @returns {*} Returns the parent value.
 */
function _parent_parent(object, path) {
  return path.length < 2 ? object : _baseGet(object, _baseSlice(path, 0, -1));
}

/* harmony default export */ const _parent = (_parent_parent);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseUnset.js





/**
 * The base implementation of `_.unset`.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {Array|string} path The property path to unset.
 * @returns {boolean} Returns `true` if the property is deleted, else `false`.
 */
function baseUnset(object, path) {
  path = _castPath(path, object);
  object = _parent(object, path);
  return object == null || delete object[_toKey(lodash_es_last(path))];
}

/* harmony default export */ const _baseUnset = (baseUnset);

;// CONCATENATED MODULE: ./node_modules/lodash-es/unset.js


/**
 * Removes the property at `path` of `object`.
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Object
 * @param {Object} object The object to modify.
 * @param {Array|string} path The path of the property to unset.
 * @returns {boolean} Returns `true` if the property is deleted, else `false`.
 * @example
 *
 * var object = { 'a': [{ 'b': { 'c': 7 } }] };
 * _.unset(object, 'a[0].b.c');
 * // => true
 *
 * console.log(object);
 * // => { 'a': [{ 'b': {} }] };
 *
 * _.unset(object, ['a', '0', 'b', 'c']);
 * // => true
 *
 * console.log(object);
 * // => { 'a': [{ 'b': {} }] };
 */
function unset(object, path) {
  return object == null ? true : _baseUnset(object, path);
}

/* harmony default export */ const lodash_es_unset = (unset);

;// CONCATENATED MODULE: ./node_modules/lodash-es/get.js


/**
 * Gets the value at `path` of `object`. If the resolved value is
 * `undefined`, the `defaultValue` is returned in its place.
 *
 * @static
 * @memberOf _
 * @since 3.7.0
 * @category Object
 * @param {Object} object The object to query.
 * @param {Array|string} path The path of the property to get.
 * @param {*} [defaultValue] The value returned for `undefined` resolved values.
 * @returns {*} Returns the resolved value.
 * @example
 *
 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
 *
 * _.get(object, 'a[0].b.c');
 * // => 3
 *
 * _.get(object, ['a', '0', 'b', 'c']);
 * // => 3
 *
 * _.get(object, 'a.b.c', 'default');
 * // => 'default'
 */
function get(object, path, defaultValue) {
  var result = object == null ? undefined : _baseGet(object, path);
  return result === undefined ? defaultValue : result;
}

/* harmony default export */ const lodash_es_get = (get);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_assignMergeValue.js



/**
 * This function is like `assignValue` except that it doesn't assign
 * `undefined` values.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {string} key The key of the property to assign.
 * @param {*} value The value to assign.
 */
function assignMergeValue(object, key, value) {
  if ((value !== undefined && !lodash_es_eq(object[key], value)) ||
      (value === undefined && !(key in object))) {
    _baseAssignValue(object, key, value);
  }
}

/* harmony default export */ const _assignMergeValue = (assignMergeValue);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_createBaseFor.js
/**
 * Creates a base function for methods like `_.forIn` and `_.forOwn`.
 *
 * @private
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {Function} Returns the new base function.
 */
function createBaseFor(fromRight) {
  return function(object, iteratee, keysFunc) {
    var index = -1,
        iterable = Object(object),
        props = keysFunc(object),
        length = props.length;

    while (length--) {
      var key = props[fromRight ? length : ++index];
      if (iteratee(iterable[key], key, iterable) === false) {
        break;
      }
    }
    return object;
  };
}

/* harmony default export */ const _createBaseFor = (createBaseFor);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseFor.js


/**
 * The base implementation of `baseForOwn` which iterates over `object`
 * properties returned by `keysFunc` and invokes `iteratee` for each property.
 * Iteratee functions may exit iteration early by explicitly returning `false`.
 *
 * @private
 * @param {Object} object The object to iterate over.
 * @param {Function} iteratee The function invoked per iteration.
 * @param {Function} keysFunc The function to get the keys of `object`.
 * @returns {Object} Returns `object`.
 */
var baseFor = _createBaseFor();

/* harmony default export */ const _baseFor = (baseFor);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isArrayLikeObject.js



/**
 * This method is like `_.isArrayLike` except that it also checks if `value`
 * is an object.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is an array-like object,
 *  else `false`.
 * @example
 *
 * _.isArrayLikeObject([1, 2, 3]);
 * // => true
 *
 * _.isArrayLikeObject(document.body.children);
 * // => true
 *
 * _.isArrayLikeObject('abc');
 * // => false
 *
 * _.isArrayLikeObject(_.noop);
 * // => false
 */
function isArrayLikeObject(value) {
  return lodash_es_isObjectLike(value) && lodash_es_isArrayLike(value);
}

/* harmony default export */ const lodash_es_isArrayLikeObject = (isArrayLikeObject);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_safeGet.js
/**
 * Gets the value at `key`, unless `key` is "__proto__" or "constructor".
 *
 * @private
 * @param {Object} object The object to query.
 * @param {string} key The key of the property to get.
 * @returns {*} Returns the property value.
 */
function safeGet(object, key) {
  if (key === 'constructor' && typeof object[key] === 'function') {
    return;
  }

  if (key == '__proto__') {
    return;
  }

  return object[key];
}

/* harmony default export */ const _safeGet = (safeGet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/toPlainObject.js



/**
 * Converts `value` to a plain object flattening inherited enumerable string
 * keyed properties of `value` to own properties of the plain object.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category Lang
 * @param {*} value The value to convert.
 * @returns {Object} Returns the converted plain object.
 * @example
 *
 * function Foo() {
 *   this.b = 2;
 * }
 *
 * Foo.prototype.c = 3;
 *
 * _.assign({ 'a': 1 }, new Foo);
 * // => { 'a': 1, 'b': 2 }
 *
 * _.assign({ 'a': 1 }, _.toPlainObject(new Foo));
 * // => { 'a': 1, 'b': 2, 'c': 3 }
 */
function toPlainObject(value) {
  return _copyObject(value, lodash_es_keysIn(value));
}

/* harmony default export */ const lodash_es_toPlainObject = (toPlainObject);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseMergeDeep.js
















/**
 * A specialized version of `baseMerge` for arrays and objects which performs
 * deep merges and tracks traversed objects enabling objects with circular
 * references to be merged.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {string} key The key of the value to merge.
 * @param {number} srcIndex The index of `source`.
 * @param {Function} mergeFunc The function to merge values.
 * @param {Function} [customizer] The function to customize assigned values.
 * @param {Object} [stack] Tracks traversed source values and their merged
 *  counterparts.
 */
function baseMergeDeep(object, source, key, srcIndex, mergeFunc, customizer, stack) {
  var objValue = _safeGet(object, key),
      srcValue = _safeGet(source, key),
      stacked = stack.get(srcValue);

  if (stacked) {
    _assignMergeValue(object, key, stacked);
    return;
  }
  var newValue = customizer
    ? customizer(objValue, srcValue, (key + ''), object, source, stack)
    : undefined;

  var isCommon = newValue === undefined;

  if (isCommon) {
    var isArr = lodash_es_isArray(srcValue),
        isBuff = !isArr && lodash_es_isBuffer(srcValue),
        isTyped = !isArr && !isBuff && lodash_es_isTypedArray(srcValue);

    newValue = srcValue;
    if (isArr || isBuff || isTyped) {
      if (lodash_es_isArray(objValue)) {
        newValue = objValue;
      }
      else if (lodash_es_isArrayLikeObject(objValue)) {
        newValue = _copyArray(objValue);
      }
      else if (isBuff) {
        isCommon = false;
        newValue = _cloneBuffer(srcValue, true);
      }
      else if (isTyped) {
        isCommon = false;
        newValue = _cloneTypedArray(srcValue, true);
      }
      else {
        newValue = [];
      }
    }
    else if (lodash_es_isPlainObject(srcValue) || lodash_es_isArguments(srcValue)) {
      newValue = objValue;
      if (lodash_es_isArguments(objValue)) {
        newValue = lodash_es_toPlainObject(objValue);
      }
      else if (!lodash_es_isObject(objValue) || lodash_es_isFunction(objValue)) {
        newValue = _initCloneObject(srcValue);
      }
    }
    else {
      isCommon = false;
    }
  }
  if (isCommon) {
    // Recursively merge objects and arrays (susceptible to call stack limits).
    stack.set(srcValue, newValue);
    mergeFunc(newValue, srcValue, srcIndex, customizer, stack);
    stack['delete'](srcValue);
  }
  _assignMergeValue(object, key, newValue);
}

/* harmony default export */ const _baseMergeDeep = (baseMergeDeep);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseMerge.js








/**
 * The base implementation of `_.merge` without support for multiple sources.
 *
 * @private
 * @param {Object} object The destination object.
 * @param {Object} source The source object.
 * @param {number} srcIndex The index of `source`.
 * @param {Function} [customizer] The function to customize merged values.
 * @param {Object} [stack] Tracks traversed source values and their merged
 *  counterparts.
 */
function baseMerge(object, source, srcIndex, customizer, stack) {
  if (object === source) {
    return;
  }
  _baseFor(source, function(srcValue, key) {
    stack || (stack = new _Stack);
    if (lodash_es_isObject(srcValue)) {
      _baseMergeDeep(object, source, key, srcIndex, baseMerge, customizer, stack);
    }
    else {
      var newValue = customizer
        ? customizer(_safeGet(object, key), srcValue, (key + ''), object, source, stack)
        : undefined;

      if (newValue === undefined) {
        newValue = srcValue;
      }
      _assignMergeValue(object, key, newValue);
    }
  }, lodash_es_keysIn);
}

/* harmony default export */ const _baseMerge = (baseMerge);

;// CONCATENATED MODULE: ./node_modules/lodash-es/identity.js
/**
 * This method returns the first argument it receives.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Util
 * @param {*} value Any value.
 * @returns {*} Returns `value`.
 * @example
 *
 * var object = { 'a': 1 };
 *
 * console.log(_.identity(object) === object);
 * // => true
 */
function identity(value) {
  return value;
}

/* harmony default export */ const lodash_es_identity = (identity);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_apply.js
/**
 * A faster alternative to `Function#apply`, this function invokes `func`
 * with the `this` binding of `thisArg` and the arguments of `args`.
 *
 * @private
 * @param {Function} func The function to invoke.
 * @param {*} thisArg The `this` binding of `func`.
 * @param {Array} args The arguments to invoke `func` with.
 * @returns {*} Returns the result of `func`.
 */
function apply(func, thisArg, args) {
  switch (args.length) {
    case 0: return func.call(thisArg);
    case 1: return func.call(thisArg, args[0]);
    case 2: return func.call(thisArg, args[0], args[1]);
    case 3: return func.call(thisArg, args[0], args[1], args[2]);
  }
  return func.apply(thisArg, args);
}

/* harmony default export */ const _apply = (apply);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_overRest.js


/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeMax = Math.max;

/**
 * A specialized version of `baseRest` which transforms the rest array.
 *
 * @private
 * @param {Function} func The function to apply a rest parameter to.
 * @param {number} [start=func.length-1] The start position of the rest parameter.
 * @param {Function} transform The rest array transform.
 * @returns {Function} Returns the new function.
 */
function overRest(func, start, transform) {
  start = nativeMax(start === undefined ? (func.length - 1) : start, 0);
  return function() {
    var args = arguments,
        index = -1,
        length = nativeMax(args.length - start, 0),
        array = Array(length);

    while (++index < length) {
      array[index] = args[start + index];
    }
    index = -1;
    var otherArgs = Array(start + 1);
    while (++index < start) {
      otherArgs[index] = args[index];
    }
    otherArgs[start] = transform(array);
    return _apply(func, this, otherArgs);
  };
}

/* harmony default export */ const _overRest = (overRest);

;// CONCATENATED MODULE: ./node_modules/lodash-es/constant.js
/**
 * Creates a function that returns `value`.
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Util
 * @param {*} value The value to return from the new function.
 * @returns {Function} Returns the new constant function.
 * @example
 *
 * var objects = _.times(2, _.constant({ 'a': 1 }));
 *
 * console.log(objects);
 * // => [{ 'a': 1 }, { 'a': 1 }]
 *
 * console.log(objects[0] === objects[1]);
 * // => true
 */
function constant(value) {
  return function() {
    return value;
  };
}

/* harmony default export */ const lodash_es_constant = (constant);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseSetToString.js




/**
 * The base implementation of `setToString` without support for hot loop shorting.
 *
 * @private
 * @param {Function} func The function to modify.
 * @param {Function} string The `toString` result.
 * @returns {Function} Returns `func`.
 */
var baseSetToString = !_defineProperty ? lodash_es_identity : function(func, string) {
  return _defineProperty(func, 'toString', {
    'configurable': true,
    'enumerable': false,
    'value': lodash_es_constant(string),
    'writable': true
  });
};

/* harmony default export */ const _baseSetToString = (baseSetToString);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_shortOut.js
/** Used to detect hot functions by number of calls within a span of milliseconds. */
var HOT_COUNT = 800,
    HOT_SPAN = 16;

/* Built-in method references for those with the same name as other `lodash` methods. */
var nativeNow = Date.now;

/**
 * Creates a function that'll short out and invoke `identity` instead
 * of `func` when it's called `HOT_COUNT` or more times in `HOT_SPAN`
 * milliseconds.
 *
 * @private
 * @param {Function} func The function to restrict.
 * @returns {Function} Returns the new shortable function.
 */
function shortOut(func) {
  var count = 0,
      lastCalled = 0;

  return function() {
    var stamp = nativeNow(),
        remaining = HOT_SPAN - (stamp - lastCalled);

    lastCalled = stamp;
    if (remaining > 0) {
      if (++count >= HOT_COUNT) {
        return arguments[0];
      }
    } else {
      count = 0;
    }
    return func.apply(undefined, arguments);
  };
}

/* harmony default export */ const _shortOut = (shortOut);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_setToString.js



/**
 * Sets the `toString` method of `func` to return `string`.
 *
 * @private
 * @param {Function} func The function to modify.
 * @param {Function} string The `toString` result.
 * @returns {Function} Returns `func`.
 */
var setToString = _shortOut(_baseSetToString);

/* harmony default export */ const _setToString = (setToString);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseRest.js




/**
 * The base implementation of `_.rest` which doesn't validate or coerce arguments.
 *
 * @private
 * @param {Function} func The function to apply a rest parameter to.
 * @param {number} [start=func.length-1] The start position of the rest parameter.
 * @returns {Function} Returns the new function.
 */
function baseRest(func, start) {
  return _setToString(_overRest(func, start, lodash_es_identity), func + '');
}

/* harmony default export */ const _baseRest = (baseRest);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_isIterateeCall.js





/**
 * Checks if the given arguments are from an iteratee call.
 *
 * @private
 * @param {*} value The potential iteratee value argument.
 * @param {*} index The potential iteratee index or key argument.
 * @param {*} object The potential iteratee object argument.
 * @returns {boolean} Returns `true` if the arguments are from an iteratee call,
 *  else `false`.
 */
function isIterateeCall(value, index, object) {
  if (!lodash_es_isObject(object)) {
    return false;
  }
  var type = typeof index;
  if (type == 'number'
        ? (lodash_es_isArrayLike(object) && _isIndex(index, object.length))
        : (type == 'string' && index in object)
      ) {
    return lodash_es_eq(object[index], value);
  }
  return false;
}

/* harmony default export */ const _isIterateeCall = (isIterateeCall);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_createAssigner.js



/**
 * Creates a function like `_.assign`.
 *
 * @private
 * @param {Function} assigner The function to assign values.
 * @returns {Function} Returns the new assigner function.
 */
function createAssigner(assigner) {
  return _baseRest(function(object, sources) {
    var index = -1,
        length = sources.length,
        customizer = length > 1 ? sources[length - 1] : undefined,
        guard = length > 2 ? sources[2] : undefined;

    customizer = (assigner.length > 3 && typeof customizer == 'function')
      ? (length--, customizer)
      : undefined;

    if (guard && _isIterateeCall(sources[0], sources[1], guard)) {
      customizer = length < 3 ? undefined : customizer;
      length = 1;
    }
    object = Object(object);
    while (++index < length) {
      var source = sources[index];
      if (source) {
        assigner(object, source, index, customizer);
      }
    }
    return object;
  });
}

/* harmony default export */ const _createAssigner = (createAssigner);

;// CONCATENATED MODULE: ./node_modules/lodash-es/merge.js



/**
 * This method is like `_.assign` except that it recursively merges own and
 * inherited enumerable string keyed properties of source objects into the
 * destination object. Source properties that resolve to `undefined` are
 * skipped if a destination value exists. Array and plain object properties
 * are merged recursively. Other objects and value types are overridden by
 * assignment. Source objects are applied from left to right. Subsequent
 * sources overwrite property assignments of previous sources.
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 0.5.0
 * @category Object
 * @param {Object} object The destination object.
 * @param {...Object} [sources] The source objects.
 * @returns {Object} Returns `object`.
 * @example
 *
 * var object = {
 *   'a': [{ 'b': 2 }, { 'd': 4 }]
 * };
 *
 * var other = {
 *   'a': [{ 'c': 3 }, { 'e': 5 }]
 * };
 *
 * _.merge(object, other);
 * // => { 'a': [{ 'b': 2, 'c': 3 }, { 'd': 4, 'e': 5 }] }
 */
var merge = _createAssigner(function(object, source, srcIndex) {
  _baseMerge(object, source, srcIndex);
});

/* harmony default export */ const lodash_es_merge = (merge);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseSet.js






/**
 * The base implementation of `_.set`.
 *
 * @private
 * @param {Object} object The object to modify.
 * @param {Array|string} path The path of the property to set.
 * @param {*} value The value to set.
 * @param {Function} [customizer] The function to customize path creation.
 * @returns {Object} Returns `object`.
 */
function baseSet(object, path, value, customizer) {
  if (!lodash_es_isObject(object)) {
    return object;
  }
  path = _castPath(path, object);

  var index = -1,
      length = path.length,
      lastIndex = length - 1,
      nested = object;

  while (nested != null && ++index < length) {
    var key = _toKey(path[index]),
        newValue = value;

    if (key === '__proto__' || key === 'constructor' || key === 'prototype') {
      return object;
    }

    if (index != lastIndex) {
      var objValue = nested[key];
      newValue = customizer ? customizer(objValue, key, nested) : undefined;
      if (newValue === undefined) {
        newValue = lodash_es_isObject(objValue)
          ? objValue
          : (_isIndex(path[index + 1]) ? [] : {});
      }
    }
    _assignValue(nested, key, newValue);
    nested = nested[key];
  }
  return object;
}

/* harmony default export */ const _baseSet = (baseSet);

;// CONCATENATED MODULE: ./node_modules/lodash-es/set.js


/**
 * Sets the value at `path` of `object`. If a portion of `path` doesn't exist,
 * it's created. Arrays are created for missing index properties while objects
 * are created for all other missing properties. Use `_.setWith` to customize
 * `path` creation.
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 3.7.0
 * @category Object
 * @param {Object} object The object to modify.
 * @param {Array|string} path The path of the property to set.
 * @param {*} value The value to set.
 * @returns {Object} Returns `object`.
 * @example
 *
 * var object = { 'a': [{ 'b': { 'c': 3 } }] };
 *
 * _.set(object, 'a[0].b.c', 4);
 * console.log(object.a[0].b.c);
 * // => 4
 *
 * _.set(object, ['x', '0', 'y', 'z'], 5);
 * console.log(object.x[0].y.z);
 * // => 5
 */
function set(object, path, value) {
  return object == null ? object : _baseSet(object, path, value);
}

/* harmony default export */ const lodash_es_set = (set);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/stylesmap.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/stylesmap
 */

/**
 * Styles map. Allows handling (adding, removing, retrieving) a set of style rules (usually, of an element).
 *
 * The styles map is capable of normalizing style names so e.g. the following operations are possible:
 */
class StylesMap {
    /**
     * Creates Styles instance.
     *
     * @param {module:engine/view/stylesmap~StylesProcessor} styleProcessor
     */
    constructor(styleProcessor) {
        /**
         * Keeps an internal representation of styles map. Normalized styles are kept as object tree to allow unified modification and
         * value access model using lodash's get, set, unset, etc methods.
         *
         * When no style processor rules are defined it acts as simple key-value storage.
         *
         * @private
         * @type {Object}
         */
        this._styles = {};
        /**
         * An instance of the {@link module:engine/view/stylesmap~StylesProcessor}.
         *
         * @private
         * @member {module:engine/view/stylesmap~StylesProcessor}
         */
        this._styleProcessor = styleProcessor;
    }
    /**
     * Returns true if style map has no styles set.
     *
     * @type {Boolean}
     */
    get isEmpty() {
        const entries = Object.entries(this._styles);
        const from = Array.from(entries);
        return !from.length;
    }
    /**
     * Number of styles defined.
     *
     * @type {Number}
     */
    get size() {
        if (this.isEmpty) {
            return 0;
        }
        return this.getStyleNames().length;
    }
    /**
     * Set styles map to a new value.
     *
     *		styles.setTo( 'border:1px solid blue;margin-top:1px;' );
     *
     * @param {String} inlineStyle
     */
    setTo(inlineStyle) {
        this.clear();
        const parsedStyles = Array.from(parseInlineStyles(inlineStyle).entries());
        for (const [key, value] of parsedStyles) {
            this._styleProcessor.toNormalizedForm(key, value, this._styles);
        }
    }
    /**
     * Checks if a given style is set.
     *
     *		styles.setTo( 'margin-left:1px;' );
     *
     *		styles.has( 'margin-left' );    // -> true
     *		styles.has( 'padding' );        // -> false
     *
     * **Note**: This check supports normalized style names.
     *
     *		// Enable 'margin' shorthand processing:
     *		editor.data.addStyleProcessorRules( addMarginRules );
     *
     *		styles.setTo( 'margin:2px;' );
     *
     *		styles.has( 'margin' );         // -> true
     *		styles.has( 'margin-top' );     // -> true
     *		styles.has( 'margin-left' );    // -> true
     *
     *		styles.remove( 'margin-top' );
     *
     *		styles.has( 'margin' );         // -> false
     *		styles.has( 'margin-top' );     // -> false
     *		styles.has( 'margin-left' );    // -> true
     *
     * @param {String} name Style name.
     * @returns {Boolean}
     */
    has(name) {
        if (this.isEmpty) {
            return false;
        }
        const styles = this._styleProcessor.getReducedForm(name, this._styles);
        const propertyDescriptor = styles.find(([property]) => property === name);
        // Only return a value if it is set;
        return Array.isArray(propertyDescriptor);
    }
    set(nameOrObject, valueOrObject) {
        if (lodash_es_isObject(nameOrObject)) {
            for (const [key, value] of Object.entries(nameOrObject)) {
                this._styleProcessor.toNormalizedForm(key, value, this._styles);
            }
        }
        else {
            this._styleProcessor.toNormalizedForm(nameOrObject, valueOrObject, this._styles);
        }
    }
    /**
     * Removes given style.
     *
     *		styles.setTo( 'background:#f00;margin-right:2px;' );
     *
     *		styles.remove( 'background' );
     *
     *		styles.toString();   // -> 'margin-right:2px;'
     *
     * ***Note**:* This method uses {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules
     * enabled style processor rules} to normalize passed values.
     *
     *		// Enable 'margin' shorthand processing:
     *		editor.data.addStyleProcessorRules( addMarginRules );
     *
     *		styles.setTo( 'margin:1px' );
     *
     *		styles.remove( 'margin-top' );
     *		styles.remove( 'margin-right' );
     *
     *		styles.toString(); // -> 'margin-bottom:1px;margin-left:1px;'
     *
     * @param {String} name Style name.
     */
    remove(name) {
        const path = toPath(name);
        lodash_es_unset(this._styles, path);
        delete this._styles[name];
        this._cleanEmptyObjectsOnPath(path);
    }
    /**
     * Returns a normalized style object or a single value.
     *
     *		// Enable 'margin' shorthand processing:
     *		editor.data.addStyleProcessorRules( addMarginRules );
     *
     *		const styles = new Styles();
     *		styles.setTo( 'margin:1px 2px 3em;' );
     *
     *		styles.getNormalized( 'margin' );
     *		// will log:
     *		// {
     *		//     top: '1px',
     *		//     right: '2px',
     *		//     bottom: '3em',
     *		//     left: '2px'     // normalized value from margin shorthand
     *		// }
     *
     *		styles.getNormalized( 'margin-left' ); // -> '2px'
     *
     * **Note**: This method will only return normalized styles if a style processor was defined.
     *
     * @param {String} name Style name.
     * @returns {Object|String|undefined}
     */
    getNormalized(name) {
        return this._styleProcessor.getNormalized(name, this._styles);
    }
    /**
     * Returns a normalized style string. Styles are sorted by name.
     *
     *		styles.set( 'margin' , '1px' );
     *		styles.set( 'background', '#f00' );
     *
     *		styles.toString(); // -> 'background:#f00;margin:1px;'
     *
     * **Note**: This method supports normalized styles if defined.
     *
     *		// Enable 'margin' shorthand processing:
     *		editor.data.addStyleProcessorRules( addMarginRules );
     *
     *		styles.set( 'margin' , '1px' );
     *		styles.set( 'background', '#f00' );
     *		styles.remove( 'margin-top' );
     *		styles.remove( 'margin-right' );
     *
     *		styles.toString(); // -> 'background:#f00;margin-bottom:1px;margin-left:1px;'
     *
     * @returns {String}
     */
    toString() {
        if (this.isEmpty) {
            return '';
        }
        return this._getStylesEntries()
            .map(arr => arr.join(':'))
            .sort()
            .join(';') + ';';
    }
    /**
     * Returns property as a value string or undefined if property is not set.
     *
     *		// Enable 'margin' shorthand processing:
     *		editor.data.addStyleProcessorRules( addMarginRules );
     *
     *		const styles = new Styles();
     *		styles.setTo( 'margin:1px;' );
     *		styles.set( 'margin-bottom', '3em' );
     *
     *		styles.getAsString( 'margin' ); // -> 'margin: 1px 1px 3em;'
     *
     * Note, however, that all sub-values must be set for the longhand property name to return a value:
     *
     *		const styles = new Styles();
     *		styles.setTo( 'margin:1px;' );
     *		styles.remove( 'margin-bottom' );
     *
     *		styles.getAsString( 'margin' ); // -> undefined
     *
     * In the above scenario, it is not possible to return a `margin` value, so `undefined` is returned.
     * Instead, you should use:
     *
     *		const styles = new Styles();
     *		styles.setTo( 'margin:1px;' );
     *		styles.remove( 'margin-bottom' );
     *
     *		for ( const styleName of styles.getStyleNames() ) {
     *			console.log( styleName, styles.getAsString( styleName ) );
     *		}
     *		// 'margin-top', '1px'
     *		// 'margin-right', '1px'
     *		// 'margin-left', '1px'
     *
     * In general, it is recommend to iterate over style names like in the example above. This way, you will always get all
     * the currently set style values. So, if all the 4 margin values would be set
     * the for-of loop above would yield only `'margin'`, `'1px'`:
     *
     *		const styles = new Styles();
     *		styles.setTo( 'margin:1px;' );
     *
     *		for ( const styleName of styles.getStyleNames() ) {
     *			console.log( styleName, styles.getAsString( styleName ) );
     *		}
     *		// 'margin', '1px'
     *
     * **Note**: To get a normalized version of a longhand property use the {@link #getNormalized `#getNormalized()`} method.
     *
     * @param {String} propertyName
     * @returns {String|undefined}
     */
    getAsString(propertyName) {
        if (this.isEmpty) {
            return;
        }
        if (this._styles[propertyName] && !lodash_es_isObject(this._styles[propertyName])) {
            // Try return styles set directly - values that are not parsed.
            return this._styles[propertyName];
        }
        const styles = this._styleProcessor.getReducedForm(propertyName, this._styles);
        const propertyDescriptor = styles.find(([property]) => property === propertyName);
        // Only return a value if it is set;
        if (Array.isArray(propertyDescriptor)) {
            return propertyDescriptor[1];
        }
    }
    /**
     * Returns all style properties names as they would appear when using {@link #toString `#toString()`}.
     *
     * When `expand` is set to true and there's a shorthand style property set, it will also return all equivalent styles:
     *
     * 		stylesMap.setTo( 'margin: 1em' )
     *
     * will be expanded to:
     *
     * 		[ 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ]
     *
     * @param {Boolean} [expand=false] Expand shorthand style properties and all return equivalent style representations.
     * @returns {Array.<String>}
     */
    getStyleNames(expand = false) {
        if (this.isEmpty) {
            return [];
        }
        if (expand) {
            return this._styleProcessor.getStyleNames(this._styles);
        }
        const entries = this._getStylesEntries();
        return entries.map(([key]) => key);
    }
    /**
     * Removes all styles.
     */
    clear() {
        this._styles = {};
    }
    /**
     * Returns normalized styles entries for further processing.
     *
     * @private
     * @returns {Array.<module:engine/view/stylesmap~PropertyDescriptor>}
     */
    _getStylesEntries() {
        const parsed = [];
        const keys = Object.keys(this._styles);
        for (const key of keys) {
            parsed.push(...this._styleProcessor.getReducedForm(key, this._styles));
        }
        return parsed;
    }
    /**
     * Removes empty objects upon removing an entry from internal object.
     *
     * @param {String} path
     * @private
     */
    _cleanEmptyObjectsOnPath(path) {
        const pathParts = path.split('.');
        const isChildPath = pathParts.length > 1;
        if (!isChildPath) {
            return;
        }
        const parentPath = pathParts.splice(0, pathParts.length - 1).join('.');
        const parentObject = lodash_es_get(this._styles, parentPath);
        if (!parentObject) {
            return;
        }
        const isParentEmpty = !Array.from(Object.keys(parentObject)).length;
        if (isParentEmpty) {
            this.remove(parentPath);
        }
    }
}
/**
 * Style processor is responsible for writing and reading a normalized styles object.
 */
class StylesProcessor {
    /**
     * Creates StylesProcessor instance.
     *
     * @private
     */
    constructor() {
        this._normalizers = new Map();
        this._extractors = new Map();
        this._reducers = new Map();
        this._consumables = new Map();
    }
    /**
     * Parse style string value to a normalized object and appends it to styles object.
     *
     *		const styles = {};
     *
     *		stylesProcessor.toNormalizedForm( 'margin', '1px', styles );
     *
     *		// styles will consist: { margin: { top: '1px', right: '1px', bottom: '1px', left: '1px; } }
     *
     * **Note**: To define normalizer callbacks use {@link #setNormalizer}.
     *
     * @param {String} name Name of style property.
     * @param {String} propertyValue Value of style property.
     * @param {Object} styles Object holding normalized styles.
     */
    toNormalizedForm(name, propertyValue, styles) {
        if (lodash_es_isObject(propertyValue)) {
            appendStyleValue(styles, toPath(name), propertyValue);
            return;
        }
        if (this._normalizers.has(name)) {
            const normalizer = this._normalizers.get(name);
            const { path, value } = normalizer(propertyValue);
            appendStyleValue(styles, path, value);
        }
        else {
            appendStyleValue(styles, name, propertyValue);
        }
    }
    /**
     * Returns a normalized version of a style property.
     *		const styles = {
     *			margin: { top: '1px', right: '1px', bottom: '1px', left: '1px; },
     *			background: { color: '#f00' }
     *		};
     *
     *		stylesProcessor.getNormalized( 'background' );
     *		// will return: { color: '#f00' }
     *
     *		stylesProcessor.getNormalized( 'margin-top' );
     *		// will return: '1px'
     *
     * **Note**: In some cases extracting single value requires defining an extractor callback {@link #setExtractor}.
     *
     * @param {String} name Name of style property.
     * @param {Object} styles Object holding normalized styles.
     * @returns {*}
     */
    getNormalized(name, styles) {
        if (!name) {
            return lodash_es_merge({}, styles);
        }
        // Might be empty string.
        if (styles[name] !== undefined) {
            return styles[name];
        }
        if (this._extractors.has(name)) {
            const extractor = this._extractors.get(name);
            if (typeof extractor === 'string') {
                return lodash_es_get(styles, extractor);
            }
            const value = extractor(name, styles);
            if (value) {
                return value;
            }
        }
        return lodash_es_get(styles, toPath(name));
    }
    /**
     * Returns a reduced form of style property form normalized object.
     *
     * For default margin reducer, the below code:
     *
     *		stylesProcessor.getReducedForm( 'margin', {
     *			margin: { top: '1px', right: '1px', bottom: '2px', left: '1px; }
     *		} );
     *
     * will return:
     *
     *		[
     *			[ 'margin', '1px 1px 2px' ]
     *		]
     *
     * because it might be represented as a shorthand 'margin' value. However if one of margin long hand values is missing it should return:
     *
     *		[
     *			[ 'margin-top', '1px' ],
     *			[ 'margin-right', '1px' ],
     *			[ 'margin-bottom', '2px' ]
     *			// the 'left' value is missing - cannot use 'margin' shorthand.
     *		]
     *
     * **Note**: To define reducer callbacks use {@link #setReducer}.
     *
     * @param {String} name Name of style property.
     * @param {Object} styles Object holding normalized styles.
     * @returns {Array.<module:engine/view/stylesmap~PropertyDescriptor>}
     */
    getReducedForm(name, styles) {
        const normalizedValue = this.getNormalized(name, styles);
        // Might be empty string.
        if (normalizedValue === undefined) {
            return [];
        }
        if (this._reducers.has(name)) {
            const reducer = this._reducers.get(name);
            return reducer(normalizedValue);
        }
        return [[name, normalizedValue]];
    }
    /**
     * Return all style properties. Also expand shorthand properties (e.g. `margin`, `background`) if respective extractor is available.
     *
     * @param {Object} styles Object holding normalized styles.
     * @returns {Array.<String>}
     */
    getStyleNames(styles) {
        // Find all extractable styles that have a value.
        const expandedStyleNames = Array.from(this._consumables.keys()).filter(name => {
            const style = this.getNormalized(name, styles);
            if (style && typeof style == 'object') {
                return Object.keys(style).length;
            }
            return style;
        });
        // For simple styles (for example `color`) we don't have a map of those styles
        // but they are 1 to 1 with normalized object keys.
        const styleNamesKeysSet = new Set([
            ...expandedStyleNames,
            ...Object.keys(styles)
        ]);
        return Array.from(styleNamesKeysSet.values());
    }
    /**
     * Returns related style names.
     *
     *		stylesProcessor.getRelatedStyles( 'margin' );
     *		// will return: [ 'margin-top', 'margin-right', 'margin-bottom', 'margin-left' ];
     *
     *		stylesProcessor.getRelatedStyles( 'margin-top' );
     *		// will return: [ 'margin' ];
     *
     * **Note**: To define new style relations load an existing style processor or use
     * {@link module:engine/view/stylesmap~StylesProcessor#setStyleRelation `StylesProcessor.setStyleRelation()`}.
     *
     * @param {String} name
     * @returns {Array.<String>}
     */
    getRelatedStyles(name) {
        return this._consumables.get(name) || [];
    }
    /**
     * Adds a normalizer method for a style property.
     *
     * A normalizer returns describing how the value should be normalized.
     *
     * For instance 'margin' style is a shorthand for four margin values:
     *
     * - 'margin-top'
     * - 'margin-right'
     * - 'margin-bottom'
     * - 'margin-left'
     *
     * and can be written in various ways if some values are equal to others. For instance `'margin: 1px 2em;'` is a shorthand for
     * `'margin-top: 1px;margin-right: 2em;margin-bottom: 1px;margin-left: 2em'`.
     *
     * A normalizer should parse various margin notations as a single object:
     *
     *		const styles = {
     *			margin: {
     *				top: '1px',
     *				right: '2em',
     *				bottom: '1px',
     *				left: '2em'
     *			}
     *		};
     *
     * Thus a normalizer for 'margin' style should return an object defining style path and value to store:
     *
     *		const returnValue = {
     *			path: 'margin',
     *			value: {
     *				top: '1px',
     *				right: '2em',
     *				bottom: '1px',
     *				left: '2em'
     *			}
     *		};
     *
     * Additionally to fully support all margin notations there should be also defined 4 normalizers for longhand margin notations. Below
     * is an example for 'margin-top' style property normalizer:
     *
     *		stylesProcessor.setNormalizer( 'margin-top', valueString => {
     *			return {
     *				path: 'margin.top',
     *				value: valueString
     *			}
     *		} );
     *
     * @param {String} name
     * @param {Function} callback
     */
    setNormalizer(name, callback) {
        this._normalizers.set(name, callback);
    }
    /**
     * Adds a extractor callback for a style property.
     *
     * Most normalized style values are stored as one level objects. It is assumed that `'margin-top'` style will be stored as:
     *
     *		const styles = {
     *			margin: {
     *				top: 'value'
     *			}
     *		}
     *
     * However, some styles can have conflicting notations and thus it might be harder to extract a style value from shorthand. For instance
     * the 'border-top-style' can be defined using `'border-top:solid'`, `'border-style:solid none none none'` or by `'border:solid'`
     * shorthands. The default border styles processors stores styles as:
     *
     *		const styles = {
     *			border: {
     *				style: {
     *					top: 'solid'
     *				}
     *			}
     *		}
     *
     * as it is better to modify border style independently from other values. On the other part the output of the border might be
     * desired as `border-top`, `border-left`, etc notation.
     *
     * In the above example a reducer should return a side border value that combines style, color and width:
     *
     *		styleProcessor.setExtractor( 'border-top', styles => {
     *			return {
     *				color: styles.border.color.top,
     *				style: styles.border.style.top,
     *				width: styles.border.width.top
     *			}
     *		} );
     *
     * @param {String} name
     * @param {Function|String} callbackOrPath Callback that return a requested value or path string for single values.
     */
    setExtractor(name, callbackOrPath) {
        this._extractors.set(name, callbackOrPath);
    }
    /**
     * Adds a reducer callback for a style property.
     *
     * Reducer returns a minimal notation for given style name. For longhand properties it is not required to write a reducer as
     * by default the direct value from style path is taken.
     *
     * For shorthand styles a reducer should return minimal style notation either by returning single name-value tuple or multiple tuples
     * if a shorthand cannot be used. For instance for a margin shorthand a reducer might return:
     *
     *		const marginShortHandTuple = [
     *			[ 'margin', '1px 1px 2px' ]
     *		];
     *
     * or a longhand tuples for defined values:
     *
     *		// Considering margin.bottom and margin.left are undefined.
     *		const marginLonghandsTuples = [
     *			[ 'margin-top', '1px' ],
     *			[ 'margin-right', '1px' ]
     *		];
     *
     * A reducer obtains a normalized style value:
     *
     *		// Simplified reducer that always outputs 4 values which are always present:
     *		stylesProcessor.setReducer( 'margin', margin => {
     *			return [
     *				[ 'margin', `${ margin.top } ${ margin.right } ${ margin.bottom } ${ margin.left }` ]
     *			]
     *		} );
     *
     * @param {String} name
     * @param {Function} callback
     */
    setReducer(name, callback) {
        this._reducers.set(name, callback);
    }
    /**
     * Defines a style shorthand relation to other style notations.
     *
     *		stylesProcessor.setStyleRelation( 'margin', [
     *			'margin-top',
     *			'margin-right',
     *			'margin-bottom',
     *			'margin-left'
     *		] );
     *
     * This enables expanding of style names for shorthands. For instance, if defined,
     * {@link module:engine/conversion/viewconsumable~ViewConsumable view consumable} items are automatically created
     * for long-hand margin style notation alongside the `'margin'` item.
     *
     * This means that when an element being converted has a style `margin`, a converter for `margin-left` will work just
     * fine since the view consumable will contain a consumable `margin-left` item (thanks to the relation) and
     * `element.getStyle( 'margin-left' )` will work as well assuming that the style processor was correctly configured.
     * However, once `margin-left` is consumed, `margin` will not be consumable anymore.
     *
     * @param {String} shorthandName
     * @param {Array.<String>} styleNames
     */
    setStyleRelation(shorthandName, styleNames) {
        this._mapStyleNames(shorthandName, styleNames);
        for (const alsoName of styleNames) {
            this._mapStyleNames(alsoName, [shorthandName]);
        }
    }
    /**
     * Set two-way binding of style names.
     *
     * @param {String} name
     * @param {Array.<String>} styleNames
     * @private
     */
    _mapStyleNames(name, styleNames) {
        if (!this._consumables.has(name)) {
            this._consumables.set(name, []);
        }
        this._consumables.get(name).push(...styleNames);
    }
}
// Parses inline styles and puts property - value pairs into styles map.
//
// @param {String} stylesString Styles to parse.
// @returns {Map.<String, String>} stylesMap Map of parsed properties and values.
function parseInlineStyles(stylesString) {
    // `null` if no quote was found in input string or last found quote was a closing quote. See below.
    let quoteType = null;
    let propertyNameStart = 0;
    let propertyValueStart = 0;
    let propertyName = null;
    const stylesMap = new Map();
    // Do not set anything if input string is empty.
    if (stylesString === '') {
        return stylesMap;
    }
    // Fix inline styles that do not end with `;` so they are compatible with algorithm below.
    if (stylesString.charAt(stylesString.length - 1) != ';') {
        stylesString = stylesString + ';';
    }
    // Seek the whole string for "special characters".
    for (let i = 0; i < stylesString.length; i++) {
        const char = stylesString.charAt(i);
        if (quoteType === null) {
            // No quote found yet or last found quote was a closing quote.
            switch (char) {
                case ':':
                    // Most of time colon means that property name just ended.
                    // Sometimes however `:` is found inside property value (for example in background image url).
                    if (!propertyName) {
                        // Treat this as end of property only if property name is not already saved.
                        // Save property name.
                        propertyName = stylesString.substr(propertyNameStart, i - propertyNameStart);
                        // Save this point as the start of property value.
                        propertyValueStart = i + 1;
                    }
                    break;
                case '"':
                case '\'':
                    // Opening quote found (this is an opening quote, because `quoteType` is `null`).
                    quoteType = char;
                    break;
                case ';': {
                    // Property value just ended.
                    // Use previously stored property value start to obtain property value.
                    const propertyValue = stylesString.substr(propertyValueStart, i - propertyValueStart);
                    if (propertyName) {
                        // Save parsed part.
                        stylesMap.set(propertyName.trim(), propertyValue.trim());
                    }
                    propertyName = null;
                    // Save this point as property name start. Property name starts immediately after previous property value ends.
                    propertyNameStart = i + 1;
                    break;
                }
            }
        }
        else if (char === quoteType) {
            // If a quote char is found and it is a closing quote, mark this fact by `null`-ing `quoteType`.
            quoteType = null;
        }
    }
    return stylesMap;
}
// Return lodash compatible path from style name.
function toPath(name) {
    return name.replace('-', '.');
}
// Appends style definition to the styles object.
//
// @param {String} nameOrPath
// @param {String|Object} valueOrObject
// @private
function appendStyleValue(stylesObject, nameOrPath, valueOrObject) {
    let valueToSet = valueOrObject;
    if (lodash_es_isObject(valueOrObject)) {
        valueToSet = lodash_es_merge({}, lodash_es_get(stylesObject, nameOrPath), valueOrObject);
    }
    lodash_es_set(stylesObject, nameOrPath, valueToSet);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/element.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/element
 */









// @if CK_DEBUG_ENGINE // const { convertMapToTags } = require( '../dev-utils/utils' );
/**
 * View element.
 *
 * The editing engine does not define a fixed semantics of its elements (it is "DTD-free").
 * This is why the type of the {@link module:engine/view/element~Element} need to
 * be defined by the feature developer. When creating an element you should use one of the following methods:
 *
 * * {@link module:engine/view/downcastwriter~DowncastWriter#createContainerElement `downcastWriter#createContainerElement()`}
 * in order to create a {@link module:engine/view/containerelement~ContainerElement},
 * * {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement `downcastWriter#createAttributeElement()`}
 * in order to create a {@link module:engine/view/attributeelement~AttributeElement},
 * * {@link module:engine/view/downcastwriter~DowncastWriter#createEmptyElement `downcastWriter#createEmptyElement()`}
 * in order to create a {@link module:engine/view/emptyelement~EmptyElement}.
 * * {@link module:engine/view/downcastwriter~DowncastWriter#createUIElement `downcastWriter#createUIElement()`}
 * in order to create a {@link module:engine/view/uielement~UIElement}.
 * * {@link module:engine/view/downcastwriter~DowncastWriter#createEditableElement `downcastWriter#createEditableElement()`}
 * in order to create a {@link module:engine/view/editableelement~EditableElement}.
 *
 * Note that for view elements which are not created from the model, like elements from mutations, paste or
 * {@link module:engine/controller/datacontroller~DataController#set data.set} it is not possible to define the type of the element.
 * In such cases the {@link module:engine/view/upcastwriter~UpcastWriter#createElement `UpcastWriter#createElement()`} method
 * should be used to create generic view elements.
 *
 * @extends module:engine/view/node~Node
 */
class Element extends node_Node {
    /**
     * Creates a view element.
     *
     * Attributes can be passed in various formats:
     *
     *		new Element( viewDocument, 'div', { class: 'editor', contentEditable: 'true' } ); // object
     *		new Element( viewDocument, 'div', [ [ 'class', 'editor' ], [ 'contentEditable', 'true' ] ] ); // map-like iterator
     *		new Element( viewDocument, 'div', mapOfAttributes ); // map
     *
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
     * @param {String} name Node name.
     * @param {Object|Iterable} [attrs] Collection of attributes.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into created element.
     */
    constructor(document, name, attrs, children) {
        super(document);
        /**
         * Name of the element.
         *
         * @readonly
         * @member {String}
         */
        this.name = name;
        /**
         * Map of attributes, where attributes names are keys and attributes values are values.
         *
         * @protected
         * @member {Map} #_attrs
         */
        this._attrs = parseAttributes(attrs);
        /**
         * Array of child nodes.
         *
         * @protected
         * @member {Array.<module:engine/view/node~Node>}
         */
        this._children = [];
        if (children) {
            this._insertChild(0, children);
        }
        /**
         * Set of classes associated with element instance.
         *
         * @protected
         * @member {Set}
         */
        this._classes = new Set();
        if (this._attrs.has('class')) {
            // Remove class attribute and handle it by class set.
            const classString = this._attrs.get('class');
            parseClasses(this._classes, classString);
            this._attrs.delete('class');
        }
        /**
         * Normalized styles.
         *
         * @protected
         * @member {module:engine/view/stylesmap~StylesMap} module:engine/view/element~Element#_styles
         */
        this._styles = new StylesMap(this.document.stylesProcessor);
        if (this._attrs.has('style')) {
            // Remove style attribute and handle it by styles map.
            this._styles.setTo(this._attrs.get('style'));
            this._attrs.delete('style');
        }
        /**
         * Map of custom properties.
         * Custom properties can be added to element instance, will be cloned but not rendered into DOM.
         *
         * @protected
         * @member {Map}
         */
        this._customProperties = new Map();
        /**
         * A list of attribute names that should be rendered in the editing pipeline even though filtering mechanisms
         * implemented in the {@link module:engine/view/domconverter~DomConverter} (for instance,
         * {@link module:engine/view/domconverter~DomConverter#shouldRenderAttribute}) would filter them out.
         *
         * These attributes can be specified as an option when the element is created by
         * the {@link module:engine/view/downcastwriter~DowncastWriter}. To check whether an unsafe an attribute should
         * be permitted, use the {@link #shouldRenderUnsafeAttribute} method.
         *
         * @private
         * @readonly
         * @member {Array.<String>}
         */
        this._unsafeAttributesToRender = [];
    }
    /**
     * Number of element's children.
     *
     * @readonly
     * @type {Number}
     */
    get childCount() {
        return this._children.length;
    }
    /**
     * Is `true` if there are no nodes inside this element, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isEmpty() {
        return this._children.length === 0;
    }
    /**
     * Gets child at the given index.
     *
     * @param {Number} index Index of child.
     * @returns {module:engine/view/node~Node} Child node.
     */
    getChild(index) {
        return this._children[index];
    }
    /**
     * Gets index of the given child node. Returns `-1` if child node is not found.
     *
     * @param {module:engine/view/node~Node} node Child node.
     * @returns {Number} Index of the child node.
     */
    getChildIndex(node) {
        return this._children.indexOf(node);
    }
    /**
     * Gets child nodes iterator.
     *
     * @returns {Iterable.<module:engine/view/node~Node>} Child nodes iterator.
     */
    getChildren() {
        return this._children[Symbol.iterator]();
    }
    /**
     * Returns an iterator that contains the keys for attributes. Order of inserting attributes is not preserved.
     *
     * @returns {Iterable.<String>} Keys for attributes.
     */
    *getAttributeKeys() {
        if (this._classes.size > 0) {
            yield 'class';
        }
        if (!this._styles.isEmpty) {
            yield 'style';
        }
        yield* this._attrs.keys();
    }
    /**
     * Returns iterator that iterates over this element's attributes.
     *
     * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
     * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
     *
     * @returns {Iterable.<*>}
     */
    *getAttributes() {
        yield* this._attrs.entries();
        if (this._classes.size > 0) {
            yield ['class', this.getAttribute('class')];
        }
        if (!this._styles.isEmpty) {
            yield ['style', this.getAttribute('style')];
        }
    }
    /**
     * Gets attribute by key. If attribute is not present - returns undefined.
     *
     * @param {String} key Attribute key.
     * @returns {String|undefined} Attribute value.
     */
    getAttribute(key) {
        if (key == 'class') {
            if (this._classes.size > 0) {
                return [...this._classes].join(' ');
            }
            return undefined;
        }
        if (key == 'style') {
            const inlineStyle = this._styles.toString();
            return inlineStyle == '' ? undefined : inlineStyle;
        }
        return this._attrs.get(key);
    }
    /**
     * Returns a boolean indicating whether an attribute with the specified key exists in the element.
     *
     * @param {String} key Attribute key.
     * @returns {Boolean} `true` if attribute with the specified key exists in the element, false otherwise.
     */
    hasAttribute(key) {
        if (key == 'class') {
            return this._classes.size > 0;
        }
        if (key == 'style') {
            return !this._styles.isEmpty;
        }
        return this._attrs.has(key);
    }
    /**
     * Checks if this element is similar to other element.
     * Both elements should have the same name and attributes to be considered as similar. Two similar elements
     * can contain different set of children nodes.
     *
     * @param {module:engine/view/element~Element} otherElement
     * @returns {Boolean}
     */
    isSimilar(otherElement) {
        if (!(otherElement instanceof Element)) {
            return false;
        }
        // If exactly the same Element is provided - return true immediately.
        if (this === otherElement) {
            return true;
        }
        // Check element name.
        if (this.name != otherElement.name) {
            return false;
        }
        // Check number of attributes, classes and styles.
        if (this._attrs.size !== otherElement._attrs.size || this._classes.size !== otherElement._classes.size ||
            this._styles.size !== otherElement._styles.size) {
            return false;
        }
        // Check if attributes are the same.
        for (const [key, value] of this._attrs) {
            if (!otherElement._attrs.has(key) || otherElement._attrs.get(key) !== value) {
                return false;
            }
        }
        // Check if classes are the same.
        for (const className of this._classes) {
            if (!otherElement._classes.has(className)) {
                return false;
            }
        }
        // Check if styles are the same.
        for (const property of this._styles.getStyleNames()) {
            if (!otherElement._styles.has(property) ||
                otherElement._styles.getAsString(property) !== this._styles.getAsString(property)) {
                return false;
            }
        }
        return true;
    }
    /**
     * Returns true if class is present.
     * If more then one class is provided - returns true only when all classes are present.
     *
     *		element.hasClass( 'foo' ); // Returns true if 'foo' class is present.
     *		element.hasClass( 'foo', 'bar' ); // Returns true if 'foo' and 'bar' classes are both present.
     *
     * @param {...String} className
     */
    hasClass(...className) {
        for (const name of className) {
            if (!this._classes.has(name)) {
                return false;
            }
        }
        return true;
    }
    /**
     * Returns iterator that contains all class names.
     *
     * @returns {Iterable.<String>}
     */
    getClassNames() {
        return this._classes.keys();
    }
    /**
     * Returns style value for the given property mae.
     * If the style does not exist `undefined` is returned.
     *
     * **Note**: This method can work with normalized style names if
     * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
     * See {@link module:engine/view/stylesmap~StylesMap#getAsString `StylesMap#getAsString()`} for details.
     *
     * For an element with style set to `'margin:1px'`:
     *
     *		// Enable 'margin' shorthand processing:
     *		editor.data.addStyleProcessorRules( addMarginRules );
     *
     *		const element = view.change( writer => {
     *			const element = writer.createElement();
     *			writer.setStyle( 'margin', '1px' );
     *			writer.setStyle( 'margin-bottom', '3em' );
     *
     *			return element;
     *		} );
     *
     *		element.getStyle( 'margin' ); // -> 'margin: 1px 1px 3em;'
     *
     * @param {String} property
     * @returns {String|undefined}
     */
    getStyle(property) {
        return this._styles.getAsString(property);
    }
    /**
     * Returns a normalized style object or single style value.
     *
     * For an element with style set to: margin:1px 2px 3em;
     *
     *		element.getNormalizedStyle( 'margin' ) );
     *
     * will return:
     *
     *		{
     *			top: '1px',
     *			right: '2px',
     *			bottom: '3em',
     *			left: '2px'    // a normalized value from margin shorthand
     *		}
     *
     * and reading for single style value:
     *
     *		styles.getNormalizedStyle( 'margin-left' );
     *
     * Will return a `2px` string.
     *
     * **Note**: This method will return normalized values only if
     * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
     * See {@link module:engine/view/stylesmap~StylesMap#getNormalized `StylesMap#getNormalized()`} for details.
     *
     *
     * @param {String} property Name of CSS property
     * @returns {Object|String|undefined}
     */
    getNormalizedStyle(property) {
        return this._styles.getNormalized(property);
    }
    /**
     * Returns iterator that contains all style names.
     *
     * @param {Boolean} [expand=false] Expand shorthand style properties and return all equivalent style representations.
     * @returns {Iterable.<String>}
     */
    getStyleNames(expand) {
        return this._styles.getStyleNames(expand);
    }
    /**
     * Returns true if style keys are present.
     * If more then one style property is provided - returns true only when all properties are present.
     *
     *		element.hasStyle( 'color' ); // Returns true if 'border-top' style is present.
     *		element.hasStyle( 'color', 'border-top' ); // Returns true if 'color' and 'border-top' styles are both present.
     *
     * @param {...String} property
     */
    hasStyle(...property) {
        for (const name of property) {
            if (!this._styles.has(name)) {
                return false;
            }
        }
        return true;
    }
    /**
     * Returns ancestor element that match specified pattern.
     * Provided patterns should be compatible with {@link module:engine/view/matcher~Matcher Matcher} as it is used internally.
     *
     * @see module:engine/view/matcher~Matcher
     * @param {Object|String|RegExp|Function} patterns Patterns used to match correct ancestor.
     * See {@link module:engine/view/matcher~Matcher}.
     * @returns {module:engine/view/element~Element|null} Found element or `null` if no matching ancestor was found.
     */
    findAncestor(...patterns) {
        const matcher = new Matcher(...patterns);
        let parent = this.parent;
        while (parent && !parent.is('documentFragment')) {
            if (matcher.match(parent)) {
                return parent;
            }
            parent = parent.parent;
        }
        return null;
    }
    /**
     * Returns the custom property value for the given key.
     *
     * @param {String|Symbol} key
     * @returns {*}
     */
    getCustomProperty(key) {
        return this._customProperties.get(key);
    }
    /**
     * Returns an iterator which iterates over this element's custom properties.
     * Iterator provides `[ key, value ]` pairs for each stored property.
     *
     * @returns {Iterable.<*>}
     */
    *getCustomProperties() {
        yield* this._customProperties.entries();
    }
    /**
     * Returns identity string based on element's name, styles, classes and other attributes.
     * Two elements that {@link #isSimilar are similar} will have same identity string.
     * It has the following format:
     *
     *		'name class="class1,class2" style="style1:value1;style2:value2" attr1="val1" attr2="val2"'
     *
     * For example:
     *
     *		const element = writer.createContainerElement( 'foo', {
     *			banana: '10',
     *			apple: '20',
     *			style: 'color: red; border-color: white;',
     *			class: 'baz'
     *		} );
     *
     *		// returns 'foo class="baz" style="border-color:white;color:red" apple="20" banana="10"'
     *		element.getIdentity();
     *
     * **Note**: Classes, styles and other attributes are sorted alphabetically.
     *
     * @returns {String}
     */
    getIdentity() {
        const classes = Array.from(this._classes).sort().join(',');
        const styles = this._styles.toString();
        const attributes = Array.from(this._attrs).map(i => `${i[0]}="${i[1]}"`).sort().join(' ');
        return this.name +
            (classes == '' ? '' : ` class="${classes}"`) +
            (!styles ? '' : ` style="${styles}"`) +
            (attributes == '' ? '' : ` ${attributes}`);
    }
    /**
     * Decides whether an unsafe attribute is whitelisted and should be rendered in the editing pipeline even though filtering mechanisms
     * like {@link module:engine/view/domconverter~DomConverter#shouldRenderAttribute} say it should not.
     *
     * Unsafe attribute names can be specified when creating an element via {@link module:engine/view/downcastwriter~DowncastWriter}.
     *
     * @param {String} attributeName The name of the attribute to be checked.
     * @returns {Boolean}
     */
    shouldRenderUnsafeAttribute(attributeName) {
        return this._unsafeAttributesToRender.includes(attributeName);
    }
    /**
     * Clones provided element.
     *
     * @protected
     * @param {Boolean} [deep=false] If set to `true` clones element and all its children recursively. When set to `false`,
     * element will be cloned without any children.
     * @returns {module:engine/view/element~Element} Clone of this element.
     */
    _clone(deep = false) {
        const childrenClone = [];
        if (deep) {
            for (const child of this.getChildren()) {
                childrenClone.push(child._clone(deep));
            }
        }
        // ContainerElement and AttributeElement should be also cloned properly.
        const cloned = new this.constructor(this.document, this.name, this._attrs, childrenClone);
        // Classes and styles are cloned separately - this solution is faster than adding them back to attributes and
        // parse once again in constructor.
        cloned._classes = new Set(this._classes);
        cloned._styles.set(this._styles.getNormalized());
        // Clone custom properties.
        cloned._customProperties = new Map(this._customProperties);
        // Clone filler offset method.
        // We can't define this method in a prototype because it's behavior which
        // is changed by e.g. toWidget() function from ckeditor5-widget. Perhaps this should be one of custom props.
        cloned.getFillerOffset = this.getFillerOffset;
        // Clone unsafe attributes list.
        cloned._unsafeAttributesToRender = this._unsafeAttributesToRender;
        return cloned;
    }
    /**
     * {@link module:engine/view/element~Element#_insertChild Insert} a child node or a list of child nodes at the end of this node
     * and sets the parent of these nodes to this element.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#insert
     * @protected
     * @param {module:engine/view/item~Item|Iterable.<module:engine/view/item~Item>} items Items to be inserted.
     * @fires module:engine/view/node~Node#change
     * @returns {Number} Number of appended nodes.
     */
    _appendChild(items) {
        return this._insertChild(this.childCount, items);
    }
    /**
     * Inserts a child node or a list of child nodes on the given index and sets the parent of these nodes to
     * this element.
     *
     * @internal
     * @see module:engine/view/downcastwriter~DowncastWriter#insert
     * @protected
     * @param {Number} index Position where nodes should be inserted.
     * @param {module:engine/view/item~Item|Iterable.<module:engine/view/item~Item>} items Items to be inserted.
     * @fires module:engine/view/node~Node#change
     * @returns {Number} Number of inserted nodes.
     */
    _insertChild(index, items) {
        this._fireChange('children', this);
        let count = 0;
        const nodes = normalize(this.document, items);
        for (const node of nodes) {
            // If node that is being added to this element is already inside another element, first remove it from the old parent.
            if (node.parent !== null) {
                node._remove();
            }
            node.parent = this;
            node.document = this.document;
            this._children.splice(index, 0, node);
            index++;
            count++;
        }
        return count;
    }
    /**
     * Removes number of child nodes starting at the given index and set the parent of these nodes to `null`.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#remove
     * @protected
     * @param {Number} index Number of the first node to remove.
     * @param {Number} [howMany=1] Number of nodes to remove.
     * @fires module:engine/view/node~Node#change
     * @returns {Array.<module:engine/view/node~Node>} The array of removed nodes.
     */
    _removeChildren(index, howMany = 1) {
        this._fireChange('children', this);
        for (let i = index; i < index + howMany; i++) {
            this._children[i].parent = null;
        }
        return this._children.splice(index, howMany);
    }
    /**
     * Adds or overwrite attribute with a specified key and value.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#setAttribute
     * @protected
     * @param {String} key Attribute key.
     * @param {String} value Attribute value.
     * @fires module:engine/view/node~Node#change
     */
    _setAttribute(key, value) {
        value = String(value);
        this._fireChange('attributes', this);
        if (key == 'class') {
            parseClasses(this._classes, value);
        }
        else if (key == 'style') {
            this._styles.setTo(value);
        }
        else {
            this._attrs.set(key, value);
        }
    }
    /**
     * Removes attribute from the element.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#removeAttribute
     * @protected
     * @param {String} key Attribute key.
     * @returns {Boolean} Returns true if an attribute existed and has been removed.
     * @fires module:engine/view/node~Node#change
     */
    _removeAttribute(key) {
        this._fireChange('attributes', this);
        // Remove class attribute.
        if (key == 'class') {
            if (this._classes.size > 0) {
                this._classes.clear();
                return true;
            }
            return false;
        }
        // Remove style attribute.
        if (key == 'style') {
            if (!this._styles.isEmpty) {
                this._styles.clear();
                return true;
            }
            return false;
        }
        // Remove other attributes.
        return this._attrs.delete(key);
    }
    /**
     * Adds specified class.
     *
     *		element._addClass( 'foo' ); // Adds 'foo' class.
     *		element._addClass( [ 'foo', 'bar' ] ); // Adds 'foo' and 'bar' classes.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#addClass
     * @protected
     * @param {Array.<String>|String} className
     * @fires module:engine/view/node~Node#change
     */
    _addClass(className) {
        this._fireChange('attributes', this);
        for (const name of toArray(className)) {
            this._classes.add(name);
        }
    }
    /**
     * Removes specified class.
     *
     *		element._removeClass( 'foo' );  // Removes 'foo' class.
     *		element._removeClass( [ 'foo', 'bar' ] ); // Removes both 'foo' and 'bar' classes.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#removeClass
     * @protected
     * @param {Array.<String>|String} className
     * @fires module:engine/view/node~Node#change
     */
    _removeClass(className) {
        this._fireChange('attributes', this);
        for (const name of toArray(className)) {
            this._classes.delete(name);
        }
    }
    _setStyle(property, value) {
        this._fireChange('attributes', this);
        if (lodash_es_isPlainObject(property)) {
            this._styles.set(property);
        }
        else {
            this._styles.set(property, value);
        }
    }
    /**
     * Removes specified style.
     *
     *		element._removeStyle( 'color' );  // Removes 'color' style.
     *		element._removeStyle( [ 'color', 'border-top' ] ); // Removes both 'color' and 'border-top' styles.
     *
     * **Note**: This method can work with normalized style names if
     * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
     * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#removeStyle
     * @protected
     * @param {Array.<String>|String} property
     * @fires module:engine/view/node~Node#change
     */
    _removeStyle(property) {
        this._fireChange('attributes', this);
        for (const name of toArray(property)) {
            this._styles.remove(name);
        }
    }
    /**
     * Sets a custom property. Unlike attributes, custom properties are not rendered to the DOM,
     * so they can be used to add special data to elements.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#setCustomProperty
     * @protected
     * @param {String|Symbol} key
     * @param {*} value
     */
    _setCustomProperty(key, value) {
        this._customProperties.set(key, value);
    }
    /**
     * Removes the custom property stored under the given key.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#removeCustomProperty
     * @protected
     * @param {String|Symbol} key
     * @returns {Boolean} Returns true if property was removed.
     */
    _removeCustomProperty(key) {
        return this._customProperties.delete(key);
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		element.is( 'element' ); // -> true
 *		element.is( 'node' ); // -> true
 *		element.is( 'view:element' ); // -> true
 *		element.is( 'view:node' ); // -> true
 *
 *		element.is( 'model:element' ); // -> false
 *		element.is( 'documentSelection' ); // -> false
 *
 * Assuming that the object being checked is an element, you can also check its
 * {@link module:engine/view/element~Element#name name}:
 *
 *		element.is( 'element', 'img' ); // -> true if this is an <img> element
 *		text.is( 'element', 'img' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
Element.prototype.is = function (type, name) {
    if (!name) {
        return type === 'element' || type === 'view:element' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'element' || type === 'view:element');
    }
};
// Parses attributes provided to the element constructor before they are applied to an element. If attributes are passed
// as an object (instead of `Iterable`), the object is transformed to the map. Attributes with `null` value are removed.
// Attributes with non-`String` value are converted to `String`.
//
// @param {Object|Iterable} attrs Attributes to parse.
// @returns {Map} Parsed attributes.
function parseAttributes(attrs) {
    const attrsMap = toMap(attrs);
    for (const [key, value] of attrsMap) {
        if (value === null) {
            attrsMap.delete(key);
        }
        else if (typeof value != 'string') {
            attrsMap.set(key, String(value));
        }
    }
    return attrsMap;
}
// Parses class attribute and puts all classes into classes set.
// Classes set s cleared before insertion.
//
// @param {Set.<String>} classesSet Set to insert parsed classes.
// @param {String} classesString String with classes to parse.
function parseClasses(classesSet, classesString) {
    const classArray = classesString.split(/\s+/);
    classesSet.clear();
    classArray.forEach(name => classesSet.add(name));
}
// Converts strings to Text and non-iterables to arrays.
//
// @param {String|module:engine/view/item~Item|Iterable.<String|module:engine/view/item~Item>}
// @returns {Iterable.<module:engine/view/node~Node>}
function normalize(document, nodes) {
    // Separate condition because string is iterable.
    if (typeof nodes == 'string') {
        return [new text_Text(document, nodes)];
    }
    if (!isIterable(nodes)) {
        nodes = [nodes];
    }
    // Array.from to enable .map() on non-arrays.
    return Array.from(nodes)
        .map(node => {
        if (typeof node == 'string') {
            return new text_Text(document, node);
        }
        if (node instanceof TextProxy) {
            return new text_Text(document, node.data);
        }
        return node;
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/containerelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/containerelement
 */

/**
 * Containers are elements which define document structure. They define boundaries for
 * {@link module:engine/view/attributeelement~AttributeElement attributes}. They are mostly used for block elements like `<p>` or `<div>`.
 *
 * Editing engine does not define a fixed HTML DTD. This is why a feature developer needs to choose between various
 * types (container element, {@link module:engine/view/attributeelement~AttributeElement attribute element},
 * {@link module:engine/view/emptyelement~EmptyElement empty element}, etc) when developing a feature.
 *
 * The container element should be your default choice when writing a converter, unless:
 *
 * * this element represents a model text attribute (then use {@link module:engine/view/attributeelement~AttributeElement}),
 * * this is an empty element like `<img>` (then use {@link module:engine/view/emptyelement~EmptyElement}),
 * * this is a root element,
 * * this is a nested editable element (then use  {@link module:engine/view/editableelement~EditableElement}).
 *
 * To create a new container element instance use the
 * {@link module:engine/view/downcastwriter~DowncastWriter#createContainerElement `DowncastWriter#createContainerElement()`}
 * method.
 *
 * @extends module:engine/view/element~Element
 */
class ContainerElement extends Element {
    /**
     * Creates a container element.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#createContainerElement
     * @see module:engine/view/element~Element
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
     * @param {String} name Node name.
     * @param {Object|Iterable} [attrs] Collection of attributes.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into created element.
     */
    constructor(...args) {
        super(...args);
        /**
         * Returns block {@link module:engine/view/filler filler} offset or `null` if block filler is not needed.
         *
         * @method #getFillerOffset
         * @returns {Number|null} Block filler offset or `null` if block filler is not needed.
         */
        this.getFillerOffset = containerelement_getFillerOffset;
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		containerElement.is( 'containerElement' ); // -> true
 *		containerElement.is( 'element' ); // -> true
 *		containerElement.is( 'node' ); // -> true
 *		containerElement.is( 'view:containerElement' ); // -> true
 *		containerElement.is( 'view:element' ); // -> true
 *		containerElement.is( 'view:node' ); // -> true
 *
 *		containerElement.is( 'model:element' ); // -> false
 *		containerElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is a container element, you can also check its
 * {@link module:engine/view/containerelement~ContainerElement#name name}:
 *
 *		containerElement.is( 'element', 'div' ); // -> true if this is a div container element
 *		containerElement.is( 'contaienrElement', 'div' ); // -> same as above
 *		text.is( 'element', 'div' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
ContainerElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'containerElement' || type === 'view:containerElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'element' || type === 'view:element' ||
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'containerElement' || type === 'view:containerElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'element' || type === 'view:element');
    }
};
/**
 * Returns block {@link module:engine/view/filler filler} offset or `null` if block filler is not needed.
 *
 * @returns {Number|null} Block filler offset or `null` if block filler is not needed.
 */
function containerelement_getFillerOffset() {
    const children = [...this.getChildren()];
    const lastChild = children[this.childCount - 1];
    // Block filler is required after a `<br>` if it's the last element in its container. See #1422.
    if (lastChild && lastChild.is('element', 'br')) {
        return this.childCount;
    }
    for (const child of children) {
        // If there's any non-UI element – don't render the bogus.
        if (!child.is('uiElement')) {
            return null;
        }
    }
    // If there are only UI elements – render the bogus at the end of the element.
    return this.childCount;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/editableelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/view/editableelement
 */


/**
 * Editable element which can be a {@link module:engine/view/rooteditableelement~RootEditableElement root}
 * or nested editable area in the editor.
 *
 * Editable is automatically read-only when its {@link module:engine/view/document~Document Document} is read-only.
 *
 * The constructor of this class shouldn't be used directly. To create new `EditableElement` use the
 * {@link module:engine/view/downcastwriter~DowncastWriter#createEditableElement `downcastWriter#createEditableElement()`} method.
 *
 * @extends module:engine/view/containerelement~ContainerElement
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class EditableElement extends ObservableMixin(ContainerElement) {
    /**
     * Creates an editable element.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#createEditableElement
     * @protected
     */
    constructor(...args) {
        super(...args);
        const document = args[0];
        /**
         * Whether the editable is in read-write or read-only mode.
         *
         * @observable
         * @member {Boolean} module:engine/view/editableelement~EditableElement#isReadOnly
         */
        this.set('isReadOnly', false);
        /**
         * Whether the editable is focused.
         *
         * This property updates when {@link module:engine/view/document~Document#isFocused document.isFocused} or view
         * selection is changed.
         *
         * @readonly
         * @observable
         * @member {Boolean} module:engine/view/editableelement~EditableElement#isFocused
         */
        this.set('isFocused', false);
        this.bind('isReadOnly').to(document);
        this.bind('isFocused').to(document, 'isFocused', isFocused => isFocused && document.selection.editableElement == this);
        // Update focus state based on selection changes.
        this.listenTo(document.selection, 'change', () => {
            this.isFocused = document.isFocused && document.selection.editableElement == this;
        });
    }
    destroy() {
        this.stopListening();
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		editableElement.is( 'editableElement' ); // -> true
 *		editableElement.is( 'element' ); // -> true
 *		editableElement.is( 'node' ); // -> true
 *		editableElement.is( 'view:editableElement' ); // -> true
 *		editableElement.is( 'view:element' ); // -> true
 *		editableElement.is( 'view:node' ); // -> true
 *
 *		editableElement.is( 'model:element' ); // -> false
 *		editableElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is an editbale element, you can also check its
 * {@link module:engine/view/editableelement~EditableElement#name name}:
 *
 *		editableElement.is( 'element', 'div' ); // -> true if this is a div element
 *		editableElement.is( 'editableElement', 'div' ); // -> same as above
 *		text.is( 'element', 'div' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
EditableElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'editableElement' || type === 'view:editableElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'containerElement' || type === 'view:containerElement' ||
            type === 'element' || type === 'view:element' ||
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'editableElement' || type === 'view:editableElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'containerElement' || type === 'view:containerElement' ||
            type === 'element' || type === 'view:element');
    }
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/rooteditableelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/rooteditableelement
 */

const rootNameSymbol = Symbol('rootName');
/**
 * Class representing a single root in the data view. A root can be either {@link ~RootEditableElement#isReadOnly editable or read-only},
 * but in both cases it is called "an editable". Roots can contain other {@link module:engine/view/editableelement~EditableElement
 * editable elements} making them "nested editables".
 *
 * @extends module:engine/view/editableelement~EditableElement
 */
class RootEditableElement extends EditableElement {
    /**
     * Creates root editable element.
     *
     * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
     * @param {String} name Node name.
     */
    constructor(document, name) {
        super(document, name);
        /**
         * Name of this root inside {@link module:engine/view/document~Document} that is an owner of this root. If no
         * other name is set, `main` name is used.
         *
         * @readonly
         * @member {String}
         */
        this.rootName = 'main';
    }
    get rootName() {
        return this.getCustomProperty(rootNameSymbol);
    }
    set rootName(rootName) {
        this._setCustomProperty(rootNameSymbol, rootName);
    }
    /**
     * Overrides old element name and sets new one.
     * This is needed because view roots are created before they are attached to the DOM.
     * The name of the root element is temporary at this stage. It has to be changed when the
     * view root element is attached to the DOM element.
     *
     * @protected
     * @param {String} name The new name of element.
     */
    set _name(name) {
        this.name = name;
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		rootEditableElement.is( 'rootElement' ); // -> true
 *		rootEditableElement.is( 'editableElement' ); // -> true
 *		rootEditableElement.is( 'element' ); // -> true
 *		rootEditableElement.is( 'node' ); // -> true
 *		rootEditableElement.is( 'view:editableElement' ); // -> true
 *		rootEditableElement.is( 'view:element' ); // -> true
 *		rootEditableElement.is( 'view:node' ); // -> true
 *
 *		rootEditableElement.is( 'model:element' ); // -> false
 *		rootEditableElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is a root editable element, you can also check its
 * {@link module:engine/view/rooteditableelement~RootEditableElement#name name}:
 *
 *		rootEditableElement.is( 'element', 'div' ); // -> true if this is a div root editable element
 *		rootEditableElement.is( 'rootElement', 'div' ); // -> same as above
 *		text.is( 'element', 'div' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
RootEditableElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'rootElement' || type === 'view:rootElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'editableElement' || type === 'view:editableElement' ||
            type === 'containerElement' || type === 'view:containerElement' ||
            type === 'element' || type === 'view:element' ||
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'rootElement' || type === 'view:rootElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'editableElement' || type === 'view:editableElement' ||
            type === 'containerElement' || type === 'view:containerElement' ||
            type === 'element' || type === 'view:element');
    }
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/treewalker.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/treewalker
 */





/**
 * Position iterator class. It allows to iterate forward and backward over the document.
 */
class treewalker_TreeWalker {
    /**
     * Creates a range iterator. All parameters are optional, but you have to specify either `boundaries` or `startPosition`.
     *
     * @constructor
     * @param {TODO ~TreeWalkerOptions} options Object with configuration.
     */
    constructor(options = {}) {
        if (!options.boundaries && !options.startPosition) {
            /**
             * Neither boundaries nor starting position have been defined.
             *
             * @error view-tree-walker-no-start-position
             */
            throw new CKEditorError('view-tree-walker-no-start-position', null);
        }
        if (options.direction && options.direction != 'forward' && options.direction != 'backward') {
            /**
             * Only `backward` and `forward` direction allowed.
             *
             * @error view-tree-walker-unknown-direction
             */
            throw new CKEditorError('view-tree-walker-unknown-direction', options.startPosition, { direction: options.direction });
        }
        /**
         * Iterator boundaries.
         *
         * When the iterator is walking `'forward'` on the end of boundary or is walking `'backward'`
         * on the start of boundary, then `{ done: true }` is returned.
         *
         * If boundaries are not defined they are set before first and after last child of the root node.
         *
         * @readonly
         * @member {module:engine/view/range~Range} module:engine/view/treewalker~TreeWalker#boundaries
         */
        this.boundaries = options.boundaries || null;
        /**
         * Iterator position. If start position is not defined then position depends on {@link #direction}. If direction is
         * `'forward'` position starts form the beginning, when direction is `'backward'` position starts from the end.
         *
         * @readonly
         * @member {module:engine/view/position~Position} module:engine/view/treewalker~TreeWalker#position
         */
        if (options.startPosition) {
            this.position = Position._createAt(options.startPosition);
        }
        else {
            this.position = Position._createAt(options.boundaries[options.direction == 'backward' ? 'end' : 'start']);
        }
        /**
         * Walking direction. Defaults `'forward'`.
         *
         * @readonly
         * @member {'backward'|'forward'} module:engine/view/treewalker~TreeWalker#direction
         */
        this.direction = options.direction || 'forward';
        /**
         * Flag indicating whether all characters from {@link module:engine/view/text~Text} should be returned as one
         * {@link module:engine/view/text~Text} or one by one as {@link module:engine/view/textproxy~TextProxy}.
         *
         * @readonly
         * @member {Boolean} module:engine/view/treewalker~TreeWalker#singleCharacters
         */
        this.singleCharacters = !!options.singleCharacters;
        /**
         * Flag indicating whether iterator should enter elements or not. If the iterator is shallow child nodes of any
         * iterated node will not be returned along with `elementEnd` tag.
         *
         * @readonly
         * @member {Boolean} module:engine/view/treewalker~TreeWalker#shallow
         */
        this.shallow = !!options.shallow;
        /**
         * Flag indicating whether iterator should ignore `elementEnd` tags. If set to `true`, walker will not
         * return a parent node of the start position. Each {@link module:engine/view/element~Element} will be returned once.
         * When set to `false` each element might be returned twice: for `'elementStart'` and `'elementEnd'`.
         *
         * @readonly
         * @member {Boolean} module:engine/view/treewalker~TreeWalker#ignoreElementEnd
         */
        this.ignoreElementEnd = !!options.ignoreElementEnd;
        /**
         * Start boundary parent.
         *
         * @private
         * @member {module:engine/view/node~Node} module:engine/view/treewalker~TreeWalker#_boundaryStartParent
         */
        this._boundaryStartParent = this.boundaries ? this.boundaries.start.parent : null;
        /**
         * End boundary parent.
         *
         * @private
         * @member {module:engine/view/node~Node} module:engine/view/treewalker~TreeWalker#_boundaryEndParent
         */
        this._boundaryEndParent = this.boundaries ? this.boundaries.end.parent : null;
    }
    /**
     * Iterable interface.
     *
     * @returns {Iterable.<module:engine/view/treewalker~TreeWalkerValue>}
     */
    [Symbol.iterator]() {
        return this;
    }
    /**
     * Moves {@link #position} in the {@link #direction} skipping values as long as the callback function returns `true`.
     *
     * For example:
     *
     * 		walker.skip( value => value.type == 'text' ); // <p>{}foo</p> -> <p>foo[]</p>
     * 		walker.skip( value => true ); // Move the position to the end: <p>{}foo</p> -> <p>foo</p>[]
     * 		walker.skip( value => false ); // Do not move the position.
     *
     * @param {Function} skip Callback function. Gets {@link module:engine/view/treewalker~TreeWalkerValue} and should
     * return `true` if the value should be skipped or `false` if not.
     */
    skip(skip) {
        let done, value, prevPosition;
        do {
            prevPosition = this.position;
            ({ done, value } = this.next());
        } while (!done && skip(value));
        if (!done) {
            this.position = prevPosition;
        }
    }
    /**
     * Gets the next tree walker's value.
     *
     * @returns {module:engine/view/treewalker~TreeWalkerValue} Object implementing iterator interface, returning
     * information about taken step.
     */
    next() {
        if (this.direction == 'forward') {
            return this._next();
        }
        else {
            return this._previous();
        }
    }
    /**
     * Makes a step forward in view. Moves the {@link #position} to the next position and returns the encountered value.
     *
     * @private
     * @returns {Object}
     * @returns {Boolean} return.done `true` if iterator is done, `false` otherwise.
     * @returns {module:engine/view/treewalker~TreeWalkerValue} return.value Information about taken step.
     */
    _next() {
        let position = this.position.clone();
        const previousPosition = this.position;
        const parent = position.parent;
        // We are at the end of the root.
        if (parent.parent === null && position.offset === parent.childCount) {
            return { done: true, value: undefined };
        }
        // We reached the walker boundary.
        if (parent === this._boundaryEndParent && position.offset == this.boundaries.end.offset) {
            return { done: true, value: undefined };
        }
        // Get node just after current position.
        let node;
        // Text is a specific parent because it contains string instead of child nodes.
        if (parent instanceof text_Text) {
            if (position.isAtEnd) {
                // Prevent returning "elementEnd" for Text node. Skip that value and return the next walker step.
                this.position = Position._createAfter(parent);
                return this._next();
            }
            node = parent.data[position.offset];
        }
        else {
            node = parent.getChild(position.offset);
        }
        if (node instanceof Element) {
            if (!this.shallow) {
                position = new Position(node, 0);
            }
            else {
                position.offset++;
            }
            this.position = position;
            return this._formatReturnValue('elementStart', node, previousPosition, position, 1);
        }
        else if (node instanceof text_Text) {
            if (this.singleCharacters) {
                position = new Position(node, 0);
                this.position = position;
                return this._next();
            }
            else {
                let charactersCount = node.data.length;
                let item;
                // If text stick out of walker range, we need to cut it and wrap in TextProxy.
                if (node == this._boundaryEndParent) {
                    charactersCount = this.boundaries.end.offset;
                    item = new TextProxy(node, 0, charactersCount);
                    position = Position._createAfter(item);
                }
                else {
                    item = new TextProxy(node, 0, node.data.length);
                    // If not just keep moving forward.
                    position.offset++;
                }
                this.position = position;
                return this._formatReturnValue('text', item, previousPosition, position, charactersCount);
            }
        }
        else if (typeof node == 'string') {
            let textLength;
            if (this.singleCharacters) {
                textLength = 1;
            }
            else {
                // Check if text stick out of walker range.
                const endOffset = parent === this._boundaryEndParent ? this.boundaries.end.offset : parent.data.length;
                textLength = endOffset - position.offset;
            }
            const textProxy = new TextProxy(parent, position.offset, textLength);
            position.offset += textLength;
            this.position = position;
            return this._formatReturnValue('text', textProxy, previousPosition, position, textLength);
        }
        else {
            // `node` is not set, we reached the end of current `parent`.
            position = Position._createAfter(parent);
            this.position = position;
            if (this.ignoreElementEnd) {
                return this._next();
            }
            else {
                return this._formatReturnValue('elementEnd', parent, previousPosition, position);
            }
        }
    }
    /**
     * Makes a step backward in view. Moves the {@link #position} to the previous position and returns the encountered value.
     *
     * @private
     * @returns {Object}
     * @returns {Boolean} return.done True if iterator is done.
     * @returns {module:engine/view/treewalker~TreeWalkerValue} return.value Information about taken step.
     */
    _previous() {
        let position = this.position.clone();
        const previousPosition = this.position;
        const parent = position.parent;
        // We are at the beginning of the root.
        if (parent.parent === null && position.offset === 0) {
            return { done: true, value: undefined };
        }
        // We reached the walker boundary.
        if (parent == this._boundaryStartParent && position.offset == this.boundaries.start.offset) {
            return { done: true, value: undefined };
        }
        // Get node just before current position.
        let node;
        // Text {@link module:engine/view/text~Text} element is a specific parent because contains string instead of child nodes.
        if (parent instanceof text_Text) {
            if (position.isAtStart) {
                // Prevent returning "elementStart" for Text node. Skip that value and return the next walker step.
                this.position = Position._createBefore(parent);
                return this._previous();
            }
            node = parent.data[position.offset - 1];
        }
        else {
            node = parent.getChild(position.offset - 1);
        }
        if (node instanceof Element) {
            if (!this.shallow) {
                position = new Position(node, node.childCount);
                this.position = position;
                if (this.ignoreElementEnd) {
                    return this._previous();
                }
                else {
                    return this._formatReturnValue('elementEnd', node, previousPosition, position);
                }
            }
            else {
                position.offset--;
                this.position = position;
                return this._formatReturnValue('elementStart', node, previousPosition, position, 1);
            }
        }
        else if (node instanceof text_Text) {
            if (this.singleCharacters) {
                position = new Position(node, node.data.length);
                this.position = position;
                return this._previous();
            }
            else {
                let charactersCount = node.data.length;
                let item;
                // If text stick out of walker range, we need to cut it and wrap in TextProxy.
                if (node == this._boundaryStartParent) {
                    const offset = this.boundaries.start.offset;
                    item = new TextProxy(node, offset, node.data.length - offset);
                    charactersCount = item.data.length;
                    position = Position._createBefore(item);
                }
                else {
                    item = new TextProxy(node, 0, node.data.length);
                    // If not just keep moving backward.
                    position.offset--;
                }
                this.position = position;
                return this._formatReturnValue('text', item, previousPosition, position, charactersCount);
            }
        }
        else if (typeof node == 'string') {
            let textLength;
            if (!this.singleCharacters) {
                // Check if text stick out of walker range.
                const startOffset = parent === this._boundaryStartParent ? this.boundaries.start.offset : 0;
                textLength = position.offset - startOffset;
            }
            else {
                textLength = 1;
            }
            position.offset -= textLength;
            const textProxy = new TextProxy(parent, position.offset, textLength);
            this.position = position;
            return this._formatReturnValue('text', textProxy, previousPosition, position, textLength);
        }
        else {
            // `node` is not set, we reached the beginning of current `parent`.
            position = Position._createBefore(parent);
            this.position = position;
            return this._formatReturnValue('elementStart', parent, previousPosition, position, 1);
        }
    }
    /**
     * Format returned data and adjust `previousPosition` and `nextPosition` if reach the bound of the {@link module:engine/view/text~Text}.
     *
     * @private
     * @param {module:engine/view/treewalker~TreeWalkerValueType} type Type of step.
     * @param {module:engine/view/item~Item} item Item between old and new position.
     * @param {module:engine/view/position~Position} previousPosition Previous position of iterator.
     * @param {module:engine/view/position~Position} nextPosition Next position of iterator.
     * @param {Number} [length] Length of the item.
     * @returns {module:engine/view/treewalker~TreeWalkerValue}
     */
    _formatReturnValue(type, item, previousPosition, nextPosition, length) {
        // Text is a specific parent, because contains string instead of children.
        // Walker doesn't enter to the Text except situations when walker is iterating over every single character,
        // or the bound starts/ends inside the Text. So when the position is at the beginning or at the end of the Text
        // we move it just before or just after Text.
        if (item instanceof TextProxy) {
            // Position is at the end of Text.
            if (item.offsetInText + item.data.length == item.textNode.data.length) {
                if (this.direction == 'forward' && !(this.boundaries && this.boundaries.end.isEqual(this.position))) {
                    nextPosition = Position._createAfter(item.textNode);
                    // When we change nextPosition of returned value we need also update walker current position.
                    this.position = nextPosition;
                }
                else {
                    previousPosition = Position._createAfter(item.textNode);
                }
            }
            // Position is at the begining ot the text.
            if (item.offsetInText === 0) {
                if (this.direction == 'backward' && !(this.boundaries && this.boundaries.start.isEqual(this.position))) {
                    nextPosition = Position._createBefore(item.textNode);
                    // When we change nextPosition of returned value we need also update walker current position.
                    this.position = nextPosition;
                }
                else {
                    previousPosition = Position._createBefore(item.textNode);
                }
            }
        }
        return {
            done: false,
            value: {
                type,
                item,
                previousPosition,
                nextPosition,
                length
            }
        };
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/position.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/position
 */




// To check if component is loaded more than once.


/**
 * Position in the view tree. Position is represented by its parent node and an offset in this parent.
 *
 * In order to create a new position instance use the `createPosition*()` factory methods available in:
 *
 * * {@link module:engine/view/view~View}
 * * {@link module:engine/view/downcastwriter~DowncastWriter}
 * * {@link module:engine/view/upcastwriter~UpcastWriter}
 */
class Position extends TypeCheckable {
    /**
     * Creates a position.
     *
     * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} parent Position parent.
     * @param {Number} offset Position offset.
     */
    constructor(parent, offset) {
        super();
        /**
         * Position parent.
         *
         * @readonly
         * @member {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
         * module:engine/view/position~Position#parent
         */
        this.parent = parent;
        /**
         * Position offset.
         *
         * @readonly
         * @member {Number} module:engine/view/position~Position#offset
         */
        this.offset = offset;
    }
    /**
     * Node directly after the position. Equals `null` when there is no node after position or position is located
     * inside text node.
     *
     * @readonly
     * @type {module:engine/view/node~Node|null}
     */
    get nodeAfter() {
        if (this.parent.is('$text')) {
            return null;
        }
        return this.parent.getChild(this.offset) || null;
    }
    /**
     * Node directly before the position. Equals `null` when there is no node before position or position is located
     * inside text node.
     *
     * @readonly
     * @type {module:engine/view/node~Node|null}
     */
    get nodeBefore() {
        if (this.parent.is('$text')) {
            return null;
        }
        return this.parent.getChild(this.offset - 1) || null;
    }
    /**
     * Is `true` if position is at the beginning of its {@link module:engine/view/position~Position#parent parent}, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isAtStart() {
        return this.offset === 0;
    }
    /**
     * Is `true` if position is at the end of its {@link module:engine/view/position~Position#parent parent}, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isAtEnd() {
        const endOffset = this.parent.is('$text') ? this.parent.data.length : this.parent.childCount;
        return this.offset === endOffset;
    }
    /**
     * Position's root, that is the root of the position's parent element.
     *
     * @readonly
     * @type {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
     */
    get root() {
        return this.parent.root;
    }
    /**
     * {@link module:engine/view/editableelement~EditableElement EditableElement} instance that contains this position, or `null` if
     * position is not inside an editable element.
     *
     * @type {module:engine/view/editableelement~EditableElement|null}
     */
    get editableElement() {
        let editable = this.parent;
        while (!(editable instanceof EditableElement)) {
            if (editable.parent) {
                editable = editable.parent;
            }
            else {
                return null;
            }
        }
        return editable;
    }
    /**
     * Returns a new instance of Position with offset incremented by `shift` value.
     *
     * @param {Number} shift How position offset should get changed. Accepts negative values.
     * @returns {module:engine/view/position~Position} Shifted position.
     */
    getShiftedBy(shift) {
        const shifted = Position._createAt(this);
        const offset = shifted.offset + shift;
        shifted.offset = offset < 0 ? 0 : offset;
        return shifted;
    }
    /**
     * Gets the farthest position which matches the callback using
     * {@link module:engine/view/treewalker~TreeWalker TreeWalker}.
     *
     * For example:
     *
     * 		getLastMatchingPosition( value => value.type == 'text' ); // <p>{}foo</p> -> <p>foo[]</p>
     * 		getLastMatchingPosition( value => value.type == 'text', { direction: 'backward' } ); // <p>foo[]</p> -> <p>{}foo</p>
     * 		getLastMatchingPosition( value => false ); // Do not move the position.
     *
     * @param {Function} skip Callback function. Gets {@link module:engine/view/treewalker~type TreeWalkerValue} and should
     * return `true` if the value should be skipped or `false` if not.
     * @param {Object} options Object with configuration options. See {@link module:engine/view/treewalker~TreeWalker}.
     *
     * @returns {module:engine/view/position~Position} The position after the last item which matches the `skip` callback test.
     */
    getLastMatchingPosition(skip, options = {}) {
        options.startPosition = this;
        const treeWalker = new treewalker_TreeWalker(options);
        treeWalker.skip(skip);
        return treeWalker.position;
    }
    /**
     * Returns ancestors array of this position, that is this position's parent and it's ancestors.
     *
     * @returns {Array} Array with ancestors.
     */
    getAncestors() {
        if (this.parent.is('documentFragment')) {
            return [this.parent];
        }
        else {
            return this.parent.getAncestors({ includeSelf: true });
        }
    }
    /**
     * Returns a {@link module:engine/view/node~Node} or {@link module:engine/view/documentfragment~DocumentFragment}
     * which is a common ancestor of both positions.
     *
     * @param {module:engine/view/position~Position} position
     * @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment|null}
     */
    getCommonAncestor(position) {
        const ancestorsA = this.getAncestors();
        const ancestorsB = position.getAncestors();
        let i = 0;
        while (ancestorsA[i] == ancestorsB[i] && ancestorsA[i]) {
            i++;
        }
        return i === 0 ? null : ancestorsA[i - 1];
    }
    /**
     * Checks whether this position equals given position.
     *
     * @param {module:engine/view/position~Position} otherPosition Position to compare with.
     * @returns {Boolean} True if positions are same.
     */
    isEqual(otherPosition) {
        return (this.parent == otherPosition.parent && this.offset == otherPosition.offset);
    }
    /**
     * Checks whether this position is located before given position. When method returns `false` it does not mean that
     * this position is after give one. Two positions may be located inside separate roots and in that situation this
     * method will still return `false`.
     *
     * @see module:engine/view/position~Position#isAfter
     * @see module:engine/view/position~Position#compareWith
     * @param {module:engine/view/position~Position} otherPosition Position to compare with.
     * @returns {Boolean} Returns `true` if this position is before given position.
     */
    isBefore(otherPosition) {
        return this.compareWith(otherPosition) == 'before';
    }
    /**
     * Checks whether this position is located after given position. When method returns `false` it does not mean that
     * this position is before give one. Two positions may be located inside separate roots and in that situation this
     * method will still return `false`.
     *
     * @see module:engine/view/position~Position#isBefore
     * @see module:engine/view/position~Position#compareWith
     * @param {module:engine/view/position~Position} otherPosition Position to compare with.
     * @returns {Boolean} Returns `true` if this position is after given position.
     */
    isAfter(otherPosition) {
        return this.compareWith(otherPosition) == 'after';
    }
    /**
     * Checks whether this position is before, after or in same position that other position. Two positions may be also
     * different when they are located in separate roots.
     *
     * @param {module:engine/view/position~Position} otherPosition Position to compare with.
     * @returns {module:engine/view/position~PositionRelation}
     */
    compareWith(otherPosition) {
        if (this.root !== otherPosition.root) {
            return 'different';
        }
        if (this.isEqual(otherPosition)) {
            return 'same';
        }
        // Get path from root to position's parent element.
        const thisPath = this.parent.is('node') ? this.parent.getPath() : [];
        const otherPath = otherPosition.parent.is('node') ? otherPosition.parent.getPath() : [];
        // Add the positions' offsets to the parents offsets.
        thisPath.push(this.offset);
        otherPath.push(otherPosition.offset);
        // Compare both path arrays to find common ancestor.
        const result = compareArrays(thisPath, otherPath);
        switch (result) {
            case 'prefix':
                return 'before';
            case 'extension':
                return 'after';
            default:
                return thisPath[result] < otherPath[result] ? 'before' : 'after';
        }
    }
    /**
     * Creates a {@link module:engine/view/treewalker~TreeWalker TreeWalker} instance with this positions as a start position.
     *
     * @param {Object} options Object with configuration options. See {@link module:engine/view/treewalker~TreeWalker}
     * @param {module:engine/view/range~Range} [options.boundaries=null] Range to define boundaries of the iterator.
     * @param {Boolean} [options.singleCharacters=false]
     * @param {Boolean} [options.shallow=false]
     * @param {Boolean} [options.ignoreElementEnd=false]
     */
    getWalker(options = {}) {
        options.startPosition = this;
        return new treewalker_TreeWalker(options);
    }
    clone() {
        return new Position(this.parent, this.offset);
    }
    /**
     * Creates position at the given location. The location can be specified as:
     *
     * * a {@link module:engine/view/position~Position position},
     * * parent element and offset (offset defaults to `0`),
     * * parent element and `'end'` (sets position at the end of that element),
     * * {@link module:engine/view/item~Item view item} and `'before'` or `'after'` (sets position before or after given view item).
     *
     * This method is a shortcut to other constructors such as:
     *
     * * {@link module:engine/view/position~Position._createBefore},
     * * {@link module:engine/view/position~Position._createAfter}.
     *
     * @protected
     * @param {module:engine/view/item~Item|module:engine/view/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/view/item~Item view item}.
     */
    static _createAt(itemOrPosition, offset) {
        if (itemOrPosition instanceof Position) {
            return new this(itemOrPosition.parent, itemOrPosition.offset);
        }
        else {
            const node = itemOrPosition;
            if (offset == 'end') {
                offset = node.is('$text') ? node.data.length : node.childCount;
            }
            else if (offset == 'before') {
                return this._createBefore(node);
            }
            else if (offset == 'after') {
                return this._createAfter(node);
            }
            else if (offset !== 0 && !offset) {
                /**
                 * {@link module:engine/view/view~View#createPositionAt `View#createPositionAt()`}
                 * requires the offset to be specified when the first parameter is a view item.
                 *
                 * @error view-createpositionat-offset-required
                 */
                throw new CKEditorError('view-createpositionat-offset-required', node);
            }
            return new Position(node, offset);
        }
    }
    /**
     * Creates a new position after given view item.
     *
     * @protected
     * @param {module:engine/view/item~Item} item View item after which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    static _createAfter(item) {
        // TextProxy is not a instance of Node so we need do handle it in specific way.
        if (item.is('$textProxy')) {
            return new Position(item.textNode, item.offsetInText + item.data.length);
        }
        if (!item.parent) {
            /**
             * You can not make a position after a root.
             *
             * @error view-position-after-root
             * @param {module:engine/view/node~Node} root
             */
            throw new CKEditorError('view-position-after-root', item, { root: item });
        }
        return new Position(item.parent, item.index + 1);
    }
    /**
     * Creates a new position before given view item.
     *
     * @protected
     * @param {module:engine/view/item~Item} item View item before which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    static _createBefore(item) {
        // TextProxy is not a instance of Node so we need do handle it in specific way.
        if (item.is('$textProxy')) {
            return new Position(item.textNode, item.offsetInText);
        }
        if (!item.parent) {
            /**
             * You cannot make a position before a root.
             *
             * @error view-position-before-root
             * @param {module:engine/view/node~Node} root
             */
            throw new CKEditorError('view-position-before-root', item, { root: item });
        }
        return new Position(item.parent, item.index);
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		position.is( 'position' ); // -> true
 *		position.is( 'view:position' ); // -> true
 *
 *		position.is( 'model:position' ); // -> false
 *		position.is( 'element' ); // -> false
 *		position.is( 'range' ); // -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
Position.prototype.is = function (type) {
    return type === 'position' || type === 'view:position';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/range.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/range
 */



/**
 * Range in the view tree. A range is represented by its start and end {@link module:engine/view/position~Position positions}.
 *
 * In order to create a new position instance use the `createPosition*()` factory methods available in:
 *
 * * {@link module:engine/view/view~View}
 * * {@link module:engine/view/downcastwriter~DowncastWriter}
 * * {@link module:engine/view/upcastwriter~UpcastWriter}
 */
class Range extends TypeCheckable {
    /**
     * Creates a range spanning from `start` position to `end` position.
     *
     * **Note:** Constructor creates it's own {@link module:engine/view/position~Position} instances basing on passed values.
     *
     * @param {module:engine/view/position~Position} start Start position.
     * @param {module:engine/view/position~Position} [end] End position. If not set, range will be collapsed at the `start` position.
     */
    constructor(start, end = null) {
        super();
        /**
         * Start position.
         *
         * @readonly
         * @member {module:engine/view/position~Position}
         */
        this.start = start.clone();
        /**
         * End position.
         *
         * @readonly
         * @member {module:engine/view/position~Position}
         */
        this.end = end ? end.clone() : start.clone();
    }
    /**
     * Iterable interface.
     *
     * Iterates over all {@link module:engine/view/item~Item view items} that are in this range and returns
     * them together with additional information like length or {@link module:engine/view/position~Position positions},
     * grouped as {@link module:engine/view/treewalker~TreeWalkerValue}.
     *
     * This iterator uses {@link module:engine/view/treewalker~TreeWalker TreeWalker} with `boundaries` set to this range and
     * `ignoreElementEnd` option
     * set to `true`.
     *
     * @returns {Iterable.<module:engine/view/treewalker~TreeWalkerValue>}
     */
    *[Symbol.iterator]() {
        yield* new treewalker_TreeWalker({ boundaries: this, ignoreElementEnd: true });
    }
    /**
     * Returns whether the range is collapsed, that is it start and end positions are equal.
     *
     * @type {Boolean}
     */
    get isCollapsed() {
        return this.start.isEqual(this.end);
    }
    /**
     * Returns whether this range is flat, that is if {@link module:engine/view/range~Range#start start} position and
     * {@link module:engine/view/range~Range#end end} position are in the same {@link module:engine/view/position~Position#parent parent}.
     *
     * @type {Boolean}
     */
    get isFlat() {
        return this.start.parent === this.end.parent;
    }
    /**
     * Range root element.
     *
     * @type {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment}
     */
    get root() {
        return this.start.root;
    }
    /**
     * Creates a maximal range that has the same content as this range but is expanded in both ways (at the beginning
     * and at the end).
     *
     * For example:
     *
     *		<p>Foo</p><p><b>{Bar}</b></p> -> <p>Foo</p>[<p><b>Bar</b>]</p>
     *		<p><b>foo</b>{bar}<span></span></p> -> <p><b>foo[</b>bar<span></span>]</p>
     *
     * Note that in the sample above:
     *
     * - `<p>` have type of {@link module:engine/view/containerelement~ContainerElement},
     * - `<b>` have type of {@link module:engine/view/attributeelement~AttributeElement},
     * - `<span>` have type of {@link module:engine/view/uielement~UIElement}.
     *
     * @returns {module:engine/view/range~Range} Enlarged range.
     */
    getEnlarged() {
        let start = this.start.getLastMatchingPosition(enlargeTrimSkip, { direction: 'backward' });
        let end = this.end.getLastMatchingPosition(enlargeTrimSkip);
        // Fix positions, in case if they are in Text node.
        if (start.parent.is('$text') && start.isAtStart) {
            start = Position._createBefore(start.parent);
        }
        if (end.parent.is('$text') && end.isAtEnd) {
            end = Position._createAfter(end.parent);
        }
        return new Range(start, end);
    }
    /**
     * Creates a minimum range that has the same content as this range but is trimmed in both ways (at the beginning
     * and at the end).
     *
     * For example:
     *
     *		<p>Foo</p>[<p><b>Bar</b>]</p> -> <p>Foo</p><p><b>{Bar}</b></p>
     *		<p><b>foo[</b>bar<span></span>]</p> -> <p><b>foo</b>{bar}<span></span></p>
     *
     * Note that in the sample above:
     *
     * - `<p>` have type of {@link module:engine/view/containerelement~ContainerElement},
     * - `<b>` have type of {@link module:engine/view/attributeelement~AttributeElement},
     * - `<span>` have type of {@link module:engine/view/uielement~UIElement}.
     *
     * @returns {module:engine/view/range~Range} Shrink range.
     */
    getTrimmed() {
        let start = this.start.getLastMatchingPosition(enlargeTrimSkip);
        if (start.isAfter(this.end) || start.isEqual(this.end)) {
            return new Range(start, start);
        }
        let end = this.end.getLastMatchingPosition(enlargeTrimSkip, { direction: 'backward' });
        const nodeAfterStart = start.nodeAfter;
        const nodeBeforeEnd = end.nodeBefore;
        // Because TreeWalker prefers positions next to text node, we need to move them manually into these text nodes.
        if (nodeAfterStart && nodeAfterStart.is('$text')) {
            start = new Position(nodeAfterStart, 0);
        }
        if (nodeBeforeEnd && nodeBeforeEnd.is('$text')) {
            end = new Position(nodeBeforeEnd, nodeBeforeEnd.data.length);
        }
        return new Range(start, end);
    }
    /**
     * Two ranges are equal if their start and end positions are equal.
     *
     * @param {module:engine/view/range~Range} otherRange Range to compare with.
     * @returns {Boolean} `true` if ranges are equal, `false` otherwise
     */
    isEqual(otherRange) {
        return this == otherRange || (this.start.isEqual(otherRange.start) && this.end.isEqual(otherRange.end));
    }
    /**
     * Checks whether this range contains given {@link module:engine/view/position~Position position}.
     *
     * @param {module:engine/view/position~Position} position Position to check.
     * @returns {Boolean} `true` if given {@link module:engine/view/position~Position position} is contained in this range,
     * `false` otherwise.
     */
    containsPosition(position) {
        return position.isAfter(this.start) && position.isBefore(this.end);
    }
    /**
     * Checks whether this range contains given {@link module:engine/view/range~Range range}.
     *
     * @param {module:engine/view/range~Range} otherRange Range to check.
     * @param {Boolean} [loose=false] Whether the check is loose or strict. If the check is strict (`false`), compared range cannot
     * start or end at the same position as this range boundaries. If the check is loose (`true`), compared range can start, end or
     * even be equal to this range. Note that collapsed ranges are always compared in strict mode.
     * @returns {Boolean} `true` if given {@link module:engine/view/range~Range range} boundaries are contained by this range, `false`
     * otherwise.
     */
    containsRange(otherRange, loose = false) {
        if (otherRange.isCollapsed) {
            loose = false;
        }
        const containsStart = this.containsPosition(otherRange.start) || (loose && this.start.isEqual(otherRange.start));
        const containsEnd = this.containsPosition(otherRange.end) || (loose && this.end.isEqual(otherRange.end));
        return containsStart && containsEnd;
    }
    /**
     * Computes which part(s) of this {@link module:engine/view/range~Range range} is not a part of given
     * {@link module:engine/view/range~Range range}.
     * Returned array contains zero, one or two {@link module:engine/view/range~Range ranges}.
     *
     * Examples:
     *
     *		let foo = downcastWriter.createText( 'foo' );
     *		let img = downcastWriter.createContainerElement( 'img' );
     *		let bar = downcastWriter.createText( 'bar' );
     *		let p = downcastWriter.createContainerElement( 'p', null, [ foo, img, bar ] );
     *
     *		let range = view.createRange( view.createPositionAt( foo, 2 ), view.createPositionAt( bar, 1 ); // "o", img, "b" are in range.
     *		let otherRange = view.createRange( // "oo", img, "ba" are in range.
     *			view.createPositionAt( foo, 1 ),
     *			view.createPositionAt( bar, 2 )
     *		);
     *		let transformed = range.getDifference( otherRange );
     *		// transformed array has no ranges because `otherRange` contains `range`
     *
     *		otherRange = view.createRange( view.createPositionAt( foo, 1 ), view.createPositionAt( p, 2 ); // "oo", img are in range.
     *		transformed = range.getDifference( otherRange );
     *		// transformed array has one range: from ( p, 2 ) to ( bar, 1 )
     *
     *		otherRange = view.createRange( view.createPositionAt( p, 1 ), view.createPositionAt( p, 2 ) ); // img is in range.
     *		transformed = range.getDifference( otherRange );
     *		// transformed array has two ranges: from ( foo, 1 ) to ( p, 1 ) and from ( p, 2 ) to ( bar, 1 )
     *
     * @param {module:engine/view/range~Range} otherRange Range to differentiate against.
     * @returns {Array.<module:engine/view/range~Range>} The difference between ranges.
     */
    getDifference(otherRange) {
        const ranges = [];
        if (this.isIntersecting(otherRange)) {
            // Ranges intersect.
            if (this.containsPosition(otherRange.start)) {
                // Given range start is inside this range. This means that we have to
                // add shrunken range - from the start to the middle of this range.
                ranges.push(new Range(this.start, otherRange.start));
            }
            if (this.containsPosition(otherRange.end)) {
                // Given range end is inside this range. This means that we have to
                // add shrunken range - from the middle of this range to the end.
                ranges.push(new Range(otherRange.end, this.end));
            }
        }
        else {
            // Ranges do not intersect, return the original range.
            ranges.push(this.clone());
        }
        return ranges;
    }
    /**
     * Returns an intersection of this {@link module:engine/view/range~Range range} and given {@link module:engine/view/range~Range range}.
     * Intersection is a common part of both of those ranges. If ranges has no common part, returns `null`.
     *
     * Examples:
     *
     *		let foo = downcastWriter.createText( 'foo' );
     *		let img = downcastWriter.createContainerElement( 'img' );
     *		let bar = downcastWriter.createText( 'bar' );
     *		let p = downcastWriter.createContainerElement( 'p', null, [ foo, img, bar ] );
     *
     *		let range = view.createRange( view.createPositionAt( foo, 2 ), view.createPositionAt( bar, 1 ); // "o", img, "b" are in range.
     *		let otherRange = view.createRange( view.createPositionAt( foo, 1 ), view.createPositionAt( p, 2 ); // "oo", img are in range.
     *		let transformed = range.getIntersection( otherRange ); // range from ( foo, 1 ) to ( p, 2 ).
     *
     *		otherRange = view.createRange( view.createPositionAt( bar, 1 ), view.createPositionAt( bar, 3 ); "ar" is in range.
     *		transformed = range.getIntersection( otherRange ); // null - no common part.
     *
     * @param {module:engine/view/range~Range} otherRange Range to check for intersection.
     * @returns {module:engine/view/range~Range|null} A common part of given ranges or `null` if ranges have no common part.
     */
    getIntersection(otherRange) {
        if (this.isIntersecting(otherRange)) {
            // Ranges intersect, so a common range will be returned.
            // At most, it will be same as this range.
            let commonRangeStart = this.start;
            let commonRangeEnd = this.end;
            if (this.containsPosition(otherRange.start)) {
                // Given range start is inside this range. This means thaNt we have to
                // shrink common range to the given range start.
                commonRangeStart = otherRange.start;
            }
            if (this.containsPosition(otherRange.end)) {
                // Given range end is inside this range. This means that we have to
                // shrink common range to the given range end.
                commonRangeEnd = otherRange.end;
            }
            return new Range(commonRangeStart, commonRangeEnd);
        }
        // Ranges do not intersect, so they do not have common part.
        return null;
    }
    /**
     * Creates a {@link module:engine/view/treewalker~TreeWalker TreeWalker} instance with this range as a boundary.
     *
     * @param {Object} options Object with configuration options. See {@link module:engine/view/treewalker~TreeWalker}.
     * @param {module:engine/view/position~Position} [options.startPosition]
     * @param {Boolean} [options.singleCharacters=false]
     * @param {Boolean} [options.shallow=false]
     * @param {Boolean} [options.ignoreElementEnd=false]
     * @returns {module:engine/view/treewalker~TreeWalker}
     */
    getWalker(options = {}) {
        options.boundaries = this;
        return new treewalker_TreeWalker(options);
    }
    /**
     * Returns a {@link module:engine/view/node~Node} or {@link module:engine/view/documentfragment~DocumentFragment}
     * which is a common ancestor of range's both ends (in which the entire range is contained).
     *
     * @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment|null}
     */
    getCommonAncestor() {
        return this.start.getCommonAncestor(this.end);
    }
    /**
     * Returns an {@link module:engine/view/element~Element Element} contained by the range.
     * The element will be returned when it is the **only** node within the range and **fully–contained**
     * at the same time.
     *
     * @returns {module:engine/view/element~Element|null}
     */
    getContainedElement() {
        if (this.isCollapsed) {
            return null;
        }
        let nodeAfterStart = this.start.nodeAfter;
        let nodeBeforeEnd = this.end.nodeBefore;
        // Handle the situation when the range position is at the beginning / at the end of a text node.
        // In such situation `.nodeAfter` and `.nodeBefore` are `null` but the range still might be spanning
        // over one element.
        //
        // <p>Foo{<span class="widget"></span>}bar</p> vs <p>Foo[<span class="widget"></span>]bar</p>
        //
        // These are basically the same range, only the difference is if the range position is at
        // at the end/at the beginning of a text node or just before/just after the text node.
        //
        if (this.start.parent.is('$text') && this.start.isAtEnd && this.start.parent.nextSibling) {
            nodeAfterStart = this.start.parent.nextSibling;
        }
        if (this.end.parent.is('$text') && this.end.isAtStart && this.end.parent.previousSibling) {
            nodeBeforeEnd = this.end.parent.previousSibling;
        }
        if (nodeAfterStart && nodeAfterStart.is('element') && nodeAfterStart === nodeBeforeEnd) {
            return nodeAfterStart;
        }
        return null;
    }
    /**
     * Clones this range.
     *
     * @returns {module:engine/view/range~Range}
     */
    clone() {
        return new Range(this.start, this.end);
    }
    /**
     * Returns an iterator that iterates over all {@link module:engine/view/item~Item view items} that are in this range and returns
     * them.
     *
     * This method uses {@link module:engine/view/treewalker~TreeWalker} with `boundaries` set to this range and `ignoreElementEnd` option
     * set to `true`. However it returns only {@link module:engine/view/item~Item items},
     * not {@link module:engine/view/treewalker~TreeWalkerValue}.
     *
     * You may specify additional options for the tree walker. See {@link module:engine/view/treewalker~TreeWalker} for
     * a full list of available options.
     *
     * @param {Object} options Object with configuration options. See {@link module:engine/view/treewalker~TreeWalker}.
     * @returns {Iterable.<module:engine/view/item~Item>}
     */
    *getItems(options = {}) {
        options.boundaries = this;
        options.ignoreElementEnd = true;
        const treeWalker = new treewalker_TreeWalker(options);
        for (const value of treeWalker) {
            yield value.item;
        }
    }
    /**
     * Returns an iterator that iterates over all {@link module:engine/view/position~Position positions} that are boundaries or
     * contained in this range.
     *
     * This method uses {@link module:engine/view/treewalker~TreeWalker} with `boundaries` set to this range. However it returns only
     * {@link module:engine/view/position~Position positions}, not {@link module:engine/view/treewalker~TreeWalkerValue}.
     *
     * You may specify additional options for the tree walker. See {@link module:engine/view/treewalker~TreeWalker} for
     * a full list of available options.
     *
     * @param {Object} options Object with configuration options. See {@link module:engine/view/treewalker~TreeWalker}.
     * @returns {Iterable.<module:engine/view/position~Position>}
     */
    *getPositions(options = {}) {
        options.boundaries = this;
        const treeWalker = new treewalker_TreeWalker(options);
        yield treeWalker.position;
        for (const value of treeWalker) {
            yield value.nextPosition;
        }
    }
    /**
     * Checks and returns whether this range intersects with the given range.
     *
     * @param {module:engine/view/range~Range} otherRange Range to compare with.
     * @returns {Boolean} True if ranges intersect.
     */
    isIntersecting(otherRange) {
        return this.start.isBefore(otherRange.end) && this.end.isAfter(otherRange.start);
    }
    /**
     * Creates a range from the given parents and offsets.
     *
     * @protected
     * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} startElement Start position
     * parent element.
     * @param {Number} startOffset Start position offset.
     * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} endElement End position
     * parent element.
     * @param {Number} endOffset End position offset.
     * @returns {module:engine/view/range~Range} Created range.
     */
    static _createFromParentsAndOffsets(startElement, startOffset, endElement, endOffset) {
        return new this(new Position(startElement, startOffset), new Position(endElement, endOffset));
    }
    /**
     * Creates a new range, spreading from specified {@link module:engine/view/position~Position position} to a position moved by
     * given `shift`. If `shift` is a negative value, shifted position is treated as the beginning of the range.
     *
     * @protected
     * @param {module:engine/view/position~Position} position Beginning of the range.
     * @param {Number} shift How long the range should be.
     * @returns {module:engine/view/range~Range}
     */
    static _createFromPositionAndShift(position, shift) {
        const start = position;
        const end = position.getShiftedBy(shift);
        return shift > 0 ? new this(start, end) : new this(end, start);
    }
    /**
     * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     * @protected
     * @param {module:engine/view/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/view/range~Range}
     */
    static _createIn(element) {
        return this._createFromParentsAndOffsets(element, 0, element, element.childCount);
    }
    /**
     * Creates a range that starts before given {@link module:engine/view/item~Item view item} and ends after it.
     *
     * @protected
     * @param {module:engine/view/item~Item} item
     * @returns {module:engine/view/range~Range}
     */
    static _createOn(item) {
        const size = item.is('$textProxy') ? item.offsetSize : 1;
        return this._createFromPositionAndShift(Position._createBefore(item), size);
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		range.is( 'range' ); // -> true
 *		range.is( 'view:range' ); // -> true
 *
 *		range.is( 'model:range' ); // -> false
 *		range.is( 'element' ); // -> false
 *		range.is( 'selection' ); // -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
Range.prototype.is = function (type) {
    return type === 'range' || type === 'view:range';
};
// Function used by getEnlarged and getTrimmed methods.
function enlargeTrimSkip(value) {
    if (value.item.is('attributeElement') || value.item.is('uiElement')) {
        return true;
    }
    return false;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/count.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/count
 */
/**
 * Returns the number of items return by the iterator.
 *
 *		count( [ 1, 2, 3, 4, 5 ] ); // 5;
 *
 * @param {Iterable.<*>} iterable Any iterable.
 * @returns {Number} Number of items returned by that iterable.
 */
function count(iterable) {
    let count = 0;
    for (const _ of iterable) { // eslint-disable-line no-unused-vars
        count++;
    }
    return count;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/selection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/view/selection
 */









/**
 * Class representing an arbirtary selection in the view.
 * See also {@link module:engine/view/documentselection~DocumentSelection}.
 *
 * New selection instances can be created via the constructor or one these methods:
 *
 * * {@link module:engine/view/view~View#createSelection `View#createSelection()`},
 * * {@link module:engine/view/upcastwriter~UpcastWriter#createSelection `UpcastWriter#createSelection()`}.
 *
 * A selection can consist of {@link module:engine/view/range~Range ranges} that can be set by using
 * the {@link module:engine/view/selection~Selection#setTo `Selection#setTo()`} method.
 */
class Selection extends EmitterMixin(TypeCheckable) {
    /**
     * Creates new selection instance.
     *
     * **Note**: The selection constructor is available as a factory method:
     *
     * * {@link module:engine/view/view~View#createSelection `View#createSelection()`},
     * * {@link module:engine/view/upcastwriter~UpcastWriter#createSelection `UpcastWriter#createSelection()`}.
     *
     * 		// Creates empty selection without ranges.
     *		const selection = writer.createSelection();
     *
     *		// Creates selection at the given range.
     *		const range = writer.createRange( start, end );
     *		const selection = writer.createSelection( range );
     *
     *		// Creates selection at the given ranges
     * 		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		const selection = writer.createSelection( ranges );
     *
     *		// Creates selection from the other selection.
     *		const otherSelection = writer.createSelection();
     *		const selection = writer.createSelection( otherSelection );
     *
     *		// Creates selection from the document selection.
     *		const selection = writer.createSelection( editor.editing.view.document.selection );
     *
     * 		// Creates selection at the given position.
     *		const position = writer.createPositionFromPath( root, path );
     *		const selection = writer.createSelection( position );
     *
     *		// Creates collapsed selection at the position of given item and offset.
     *		const paragraph = writer.createContainerElement( 'paragraph' );
     *		const selection = writer.createSelection( paragraph, offset );
     *
     *		// Creates a range inside an {@link module:engine/view/element~Element element} which starts before the
     *		// first child of that element and ends after the last child of that element.
     *		const selection = writer.createSelection( paragraph, 'in' );
     *
     *		// Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends
     *		// just after the item.
     *		const selection = writer.createSelection( paragraph, 'on' );
     *
     * `Selection`'s constructor allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Creates backward selection.
     *		const selection = writer.createSelection( range, { backward: true } );
     *
     * Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
     * (and be  properly handled by screen readers).
     *
     *		// Creates fake selection with label.
     *		const selection = writer.createSelection( range, { fake: true, label: 'foo' } );
     *
     * @param {module:engine/view/selection~Selectable} [selectable=null]
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Offset or place when selectable is an `Item`.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     */
    constructor(...args) {
        super();
        /**
         * Stores all ranges that are selected.
         *
         * @protected
         * @member {Array.<module:engine/view/range~Range>}
         */
        this._ranges = [];
        /**
         * Specifies whether the last added range was added as a backward or forward range.
         *
         * @protected
         * @member {Boolean}
         */
        this._lastRangeBackward = false;
        /**
         * Specifies whether selection instance is fake.
         *
         * @private
         * @member {Boolean}
         */
        this._isFake = false;
        /**
         * Fake selection's label.
         *
         * @private
         * @member {String}
         */
        this._fakeSelectionLabel = '';
        if (args.length) {
            this.setTo(...args);
        }
    }
    /**
     * Returns true if selection instance is marked as `fake`.
     *
     * @see #setTo
     * @type {Boolean}
     */
    get isFake() {
        return this._isFake;
    }
    /**
     * Returns fake selection label.
     *
     * @see #setTo
     * @type {String}
     */
    get fakeSelectionLabel() {
        return this._fakeSelectionLabel;
    }
    /**
     * Selection anchor. Anchor may be described as a position where the selection starts. Together with
     * {@link #focus focus} they define the direction of selection, which is important
     * when expanding/shrinking selection. Anchor is always the start or end of the most recent added range.
     * It may be a bit unintuitive when there are multiple ranges in selection.
     *
     * @see #focus
     * @type {module:engine/view/position~Position}
     */
    get anchor() {
        if (!this._ranges.length) {
            return null;
        }
        const range = this._ranges[this._ranges.length - 1];
        const anchor = this._lastRangeBackward ? range.end : range.start;
        return anchor.clone();
    }
    /**
     * Selection focus. Focus is a position where the selection ends.
     *
     * @see #anchor
     * @type {module:engine/view/position~Position}
     */
    get focus() {
        if (!this._ranges.length) {
            return null;
        }
        const range = this._ranges[this._ranges.length - 1];
        const focus = this._lastRangeBackward ? range.start : range.end;
        return focus.clone();
    }
    /**
     * Returns whether the selection is collapsed. Selection is collapsed when there is exactly one range which is
     * collapsed.
     *
     * @type {Boolean}
     */
    get isCollapsed() {
        return this.rangeCount === 1 && this._ranges[0].isCollapsed;
    }
    /**
     * Returns number of ranges in selection.
     *
     * @type {Number}
     */
    get rangeCount() {
        return this._ranges.length;
    }
    /**
     * Specifies whether the {@link #focus} precedes {@link #anchor}.
     *
     * @type {Boolean}
     */
    get isBackward() {
        return !this.isCollapsed && this._lastRangeBackward;
    }
    /**
     * {@link module:engine/view/editableelement~EditableElement EditableElement} instance that contains this selection, or `null`
     * if the selection is not inside an editable element.
     *
     * @type {module:engine/view/editableelement~EditableElement|null}
     */
    get editableElement() {
        if (this.anchor) {
            return this.anchor.editableElement;
        }
        return null;
    }
    /**
     * Returns an iterable that contains copies of all ranges added to the selection.
     *
     * @returns {Iterable.<module:engine/view/range~Range>}
     */
    *getRanges() {
        for (const range of this._ranges) {
            yield range.clone();
        }
    }
    /**
     * Returns copy of the first range in the selection. First range is the one which
     * {@link module:engine/view/range~Range#start start} position {@link module:engine/view/position~Position#isBefore is before} start
     * position of all other ranges (not to confuse with the first range added to the selection).
     * Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/range~Range|null}
     */
    getFirstRange() {
        let first = null;
        for (const range of this._ranges) {
            if (!first || range.start.isBefore(first.start)) {
                first = range;
            }
        }
        return first ? first.clone() : null;
    }
    /**
     * Returns copy of the last range in the selection. Last range is the one which {@link module:engine/view/range~Range#end end}
     * position {@link module:engine/view/position~Position#isAfter is after} end position of all other ranges (not to confuse
     * with the last range added to the selection). Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/range~Range|null}
     */
    getLastRange() {
        let last = null;
        for (const range of this._ranges) {
            if (!last || range.end.isAfter(last.end)) {
                last = range;
            }
        }
        return last ? last.clone() : null;
    }
    /**
     * Returns copy of the first position in the selection. First position is the position that
     * {@link module:engine/view/position~Position#isBefore is before} any other position in the selection ranges.
     * Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/position~Position|null}
     */
    getFirstPosition() {
        const firstRange = this.getFirstRange();
        return firstRange ? firstRange.start.clone() : null;
    }
    /**
     * Returns copy of the last position in the selection. Last position is the position that
     * {@link module:engine/view/position~Position#isAfter is after} any other position in the selection ranges.
     * Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/position~Position|null}
     */
    getLastPosition() {
        const lastRange = this.getLastRange();
        return lastRange ? lastRange.end.clone() : null;
    }
    /**
     * Checks whether, this selection is equal to given selection. Selections are equal if they have same directions,
     * same number of ranges and all ranges from one selection equal to a range from other selection.
     *
     * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} otherSelection
     * Selection to compare with.
     * @returns {Boolean} `true` if selections are equal, `false` otherwise.
     */
    isEqual(otherSelection) {
        if (this.isFake != otherSelection.isFake) {
            return false;
        }
        if (this.isFake && this.fakeSelectionLabel != otherSelection.fakeSelectionLabel) {
            return false;
        }
        if (this.rangeCount != otherSelection.rangeCount) {
            return false;
        }
        else if (this.rangeCount === 0) {
            return true;
        }
        if (!this.anchor.isEqual(otherSelection.anchor) || !this.focus.isEqual(otherSelection.focus)) {
            return false;
        }
        for (const thisRange of this._ranges) {
            let found = false;
            for (const otherRange of otherSelection._ranges) {
                if (thisRange.isEqual(otherRange)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }
        return true;
    }
    /**
     * Checks whether this selection is similar to given selection. Selections are similar if they have same directions, same
     * number of ranges, and all {@link module:engine/view/range~Range#getTrimmed trimmed} ranges from one selection are
     * equal to any trimmed range from other selection.
     *
     * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} otherSelection
     * Selection to compare with.
     * @returns {Boolean} `true` if selections are similar, `false` otherwise.
     */
    isSimilar(otherSelection) {
        if (this.isBackward != otherSelection.isBackward) {
            return false;
        }
        const numOfRangesA = count(this.getRanges());
        const numOfRangesB = count(otherSelection.getRanges());
        // If selections have different number of ranges, they cannot be similar.
        if (numOfRangesA != numOfRangesB) {
            return false;
        }
        // If both selections have no ranges, they are similar.
        if (numOfRangesA == 0) {
            return true;
        }
        // Check if each range in one selection has a similar range in other selection.
        for (let rangeA of this.getRanges()) {
            rangeA = rangeA.getTrimmed();
            let found = false;
            for (let rangeB of otherSelection.getRanges()) {
                rangeB = rangeB.getTrimmed();
                if (rangeA.start.isEqual(rangeB.start) && rangeA.end.isEqual(rangeB.end)) {
                    found = true;
                    break;
                }
            }
            // For `rangeA`, neither range in `otherSelection` was similar. So selections are not similar.
            if (!found) {
                return false;
            }
        }
        // There were no ranges that weren't matched. Selections are similar.
        return true;
    }
    /**
     * Returns the selected element. {@link module:engine/view/element~Element Element} is considered as selected if there is only
     * one range in the selection, and that range contains exactly one element.
     * Returns `null` if there is no selected element.
     *
     * @returns {module:engine/view/element~Element|null}
     */
    getSelectedElement() {
        if (this.rangeCount !== 1) {
            return null;
        }
        return this.getFirstRange().getContainedElement();
    }
    /**
     * Sets this selection's ranges and direction to the specified location based on the given
     * {@link module:engine/view/selection~Selectable selectable}.
     *
     *		// Sets selection to the given range.
     *		const range = writer.createRange( start, end );
     *		selection.setTo( range );
     *
     *		// Sets selection to given ranges.
     * 		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		selection.setTo( range );
     *
     *		// Sets selection to the other selection.
     *		const otherSelection = writer.createSelection();
     *		selection.setTo( otherSelection );
     *
     *	 	// Sets selection to contents of DocumentSelection.
     *		selection.setTo( editor.editing.view.document.selection );
     *
     * 		// Sets collapsed selection at the given position.
     *		const position = writer.createPositionAt( root, path );
     *		selection.setTo( position );
     *
     * 		// Sets collapsed selection at the position of given item and offset.
     *		selection.setTo( paragraph, offset );
     *
     * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     *		selection.setTo( paragraph, 'in' );
     *
     * Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends just after the item.
     *
     *		selection.setTo( paragraph, 'on' );
     *
     * 		// Clears selection. Removes all ranges.
     *		selection.setTo( null );
     *
     * `Selection#setTo()` method allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Sets selection as backward.
     *		selection.setTo( range, { backward: true } );
     *
     * Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
     * (and be  properly handled by screen readers).
     *
     *		// Creates fake selection with label.
     *		selection.setTo( range, { fake: true, label: 'foo' } );
     *
     * @fires change
     * @param {module:engine/view/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     */
    setTo(...args) {
        let [selectable, placeOrOffset, options] = args;
        if (typeof placeOrOffset == 'object') {
            options = placeOrOffset;
            placeOrOffset = undefined;
        }
        if (selectable === null) {
            this._setRanges([]);
            this._setFakeOptions(options);
        }
        else if (selectable instanceof Selection || selectable instanceof DocumentSelection) {
            this._setRanges(selectable.getRanges(), selectable.isBackward);
            this._setFakeOptions({ fake: selectable.isFake, label: selectable.fakeSelectionLabel });
        }
        else if (selectable instanceof Range) {
            this._setRanges([selectable], options && options.backward);
            this._setFakeOptions(options);
        }
        else if (selectable instanceof Position) {
            this._setRanges([new Range(selectable)]);
            this._setFakeOptions(options);
        }
        else if (selectable instanceof node_Node) {
            const backward = !!options && !!options.backward;
            let range;
            if (placeOrOffset === undefined) {
                /**
                 * selection.setTo requires the second parameter when the first parameter is a node.
                 *
                 * @error view-selection-setto-required-second-parameter
                 */
                throw new CKEditorError('view-selection-setto-required-second-parameter', this);
            }
            else if (placeOrOffset == 'in') {
                range = Range._createIn(selectable);
            }
            else if (placeOrOffset == 'on') {
                range = Range._createOn(selectable);
            }
            else {
                range = new Range(Position._createAt(selectable, placeOrOffset));
            }
            this._setRanges([range], backward);
            this._setFakeOptions(options);
        }
        else if (isIterable(selectable)) {
            // We assume that the selectable is an iterable of ranges.
            // Array.from() is used to prevent setting ranges to the old iterable
            this._setRanges(selectable, options && options.backward);
            this._setFakeOptions(options);
        }
        else {
            /**
             * Cannot set selection to given place.
             *
             * @error view-selection-setto-not-selectable
             */
            throw new CKEditorError('view-selection-setto-not-selectable', this);
        }
        this.fire('change');
    }
    /**
     * Moves {@link #focus} to the specified location.
     *
     * The location can be specified in the same form as {@link module:engine/view/view~View#createPositionAt view.createPositionAt()}
     * parameters.
     *
     * @fires change
     * @param {module:engine/view/item~Item|module:engine/view/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/view/item~Item view item}.
     */
    setFocus(itemOrPosition, offset) {
        if (this.anchor === null) {
            /**
             * Cannot set selection focus if there are no ranges in selection.
             *
             * @error view-selection-setfocus-no-ranges
             */
            throw new CKEditorError('view-selection-setfocus-no-ranges', this);
        }
        const newFocus = Position._createAt(itemOrPosition, offset);
        if (newFocus.compareWith(this.focus) == 'same') {
            return;
        }
        const anchor = this.anchor;
        this._ranges.pop();
        if (newFocus.compareWith(anchor) == 'before') {
            this._addRange(new Range(newFocus, anchor), true);
        }
        else {
            this._addRange(new Range(anchor, newFocus));
        }
        this.fire('change');
    }
    /**
     * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array
     * is treated like the last added range and is used to set {@link #anchor anchor} and {@link #focus focus}.
     * Accepts a flag describing in which way the selection is made.
     *
     * @private
     * @param {Iterable.<module:engine/view/range~Range>} newRanges Iterable object of ranges to set.
     * @param {Boolean} [isLastBackward=false] Flag describing if last added range was selected forward - from start to end
     * (`false`) or backward - from end to start (`true`). Defaults to `false`.
     */
    _setRanges(newRanges, isLastBackward = false) {
        // New ranges should be copied to prevent removing them by setting them to `[]` first.
        // Only applies to situations when selection is set to the same selection or same selection's ranges.
        newRanges = Array.from(newRanges);
        this._ranges = [];
        for (const range of newRanges) {
            this._addRange(range);
        }
        this._lastRangeBackward = !!isLastBackward;
    }
    /**
     * Sets this selection instance to be marked as `fake`. A fake selection does not render as browser native selection
     * over selected elements and is hidden to the user. This way, no native selection UI artifacts are displayed to
     * the user and selection over elements can be represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM (and be
     * properly handled by screen readers).
     *
     * @private
     * @param {Object} [options] Options.
     * @param {Boolean} [options.fake] If set to true selection will be marked as `fake`.
     * @param {String} [options.label=''] Fake selection label.
     */
    _setFakeOptions(options = {}) {
        this._isFake = !!options.fake;
        this._fakeSelectionLabel = options.fake ? options.label || '' : '';
    }
    /**
     * Adds a range to the selection. Added range is copied. This means that passed range is not saved in the
     * selection instance and you can safely operate on it.
     *
     * Accepts a flag describing in which way the selection is made - passed range might be selected from
     * {@link module:engine/view/range~Range#start start} to {@link module:engine/view/range~Range#end end}
     * or from {@link module:engine/view/range~Range#end end} to {@link module:engine/view/range~Range#start start}.
     * The flag is used to set {@link #anchor anchor} and {@link #focus focus} properties.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-selection-range-intersects` if added range intersects
     * with ranges already stored in Selection instance.
     *
     * @private
     * @fires change
     * @param {module:engine/view/range~Range} range
     * @param {Boolean} [isBackward]
     */
    _addRange(range, isBackward = false) {
        if (!(range instanceof Range)) {
            /**
             * Selection range set to an object that is not an instance of {@link module:engine/view/range~Range}.
             *
             * @error view-selection-add-range-not-range
             */
            throw new CKEditorError('view-selection-add-range-not-range', this);
        }
        this._pushRange(range);
        this._lastRangeBackward = !!isBackward;
    }
    /**
     * Adds range to selection - creates copy of given range so it can be safely used and modified.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-selection-range-intersects` if added range intersects
     * with ranges already stored in selection instance.
     *
     * @private
     * @param {module:engine/view/range~Range} range
     */
    _pushRange(range) {
        for (const storedRange of this._ranges) {
            if (range.isIntersecting(storedRange)) {
                /**
                 * Trying to add a range that intersects with another range from selection.
                 *
                 * @error view-selection-range-intersects
                 * @param {module:engine/view/range~Range} addedRange Range that was added to the selection.
                 * @param {module:engine/view/range~Range} intersectingRange Range from selection that intersects with `addedRange`.
                 */
                throw new CKEditorError('view-selection-range-intersects', this, { addedRange: range, intersectingRange: storedRange });
            }
        }
        this._ranges.push(new Range(range.start, range.end));
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		selection.is( 'selection' ); // -> true
 *		selection.is( 'view:selection' ); // -> true
 *
 *		selection.is( 'model:selection' ); // -> false
 *		selection.is( 'element' ); // -> false
 *		selection.is( 'range' ); // -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
Selection.prototype.is = function (type) {
    return type === 'selection' || type === 'view:selection';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/documentselection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/view/documentselection
 */



/**
 * Class representing the document selection in the view.
 *
 * Its instance is available in {@link module:engine/view/document~Document#selection `Document#selection`}.
 *
 * It is similar to {@link module:engine/view/selection~Selection} but
 * it has a read-only API and can be modified only by the writer available in
 * the {@link module:engine/view/view~View#change `View#change()`} block
 * (so via {@link module:engine/view/downcastwriter~DowncastWriter#setSelection `DowncastWriter#setSelection()`}).
 */
class DocumentSelection extends EmitterMixin(TypeCheckable) {
    /**
     * Creates new DocumentSelection instance.
     *
     * 		// Creates empty selection without ranges.
     *		const selection = new DocumentSelection();
     *
     *		// Creates selection at the given range.
     *		const range = writer.createRange( start, end );
     *		const selection = new DocumentSelection( range );
     *
     *		// Creates selection at the given ranges
     * 		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( start2, end2 ) ];
     *		const selection = new DocumentSelection( ranges );
     *
     *		// Creates selection from the other selection.
     *		const otherSelection = writer.createSelection();
     *		const selection = new DocumentSelection( otherSelection );
     *
     * 		// Creates selection at the given position.
     *		const position = writer.createPositionAt( root, offset );
     *		const selection = new DocumentSelection( position );
     *
     *		// Creates collapsed selection at the position of given item and offset.
     *		const paragraph = writer.createContainerElement( 'paragraph' );
     *		const selection = new DocumentSelection( paragraph, offset );
     *
     *		// Creates a range inside an {@link module:engine/view/element~Element element} which starts before the
     *		// first child of that element and ends after the last child of that element.
     *		const selection = new DocumentSelection( paragraph, 'in' );
     *
     *		// Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends
     *		// just after the item.
     *		const selection = new DocumentSelection( paragraph, 'on' );
     *
     * `Selection`'s constructor allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Creates backward selection.
     *		const selection = new DocumentSelection( range, { backward: true } );
     *
     * Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
     * (and be  properly handled by screen readers).
     *
     *		// Creates fake selection with label.
     *		const selection = new DocumentSelection( range, { fake: true, label: 'foo' } );
     *
     * @param {module:engine/view/selection~Selectable} [selectable=null]
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Offset or place when selectable is an `Item`.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     */
    constructor(...args) {
        super();
        /**
         * Selection is used internally (`DocumentSelection` is a proxy to that selection).
         *
         * @private
         * @member {module:engine/view/selection~Selection}
         */
        this._selection = new Selection();
        // Delegate change event to be fired on DocumentSelection instance.
        this._selection.delegate('change').to(this);
        // Set selection data.
        if (args.length) {
            this._selection.setTo(...args);
        }
    }
    /**
     * Returns true if selection instance is marked as `fake`.
     *
     * @see #_setTo
     * @type {Boolean}
     */
    get isFake() {
        return this._selection.isFake;
    }
    /**
     * Returns fake selection label.
     *
     * @see #_setTo
     * @type {String}
     */
    get fakeSelectionLabel() {
        return this._selection.fakeSelectionLabel;
    }
    /**
     * Selection anchor. Anchor may be described as a position where the selection starts. Together with
     * {@link #focus focus} they define the direction of selection, which is important
     * when expanding/shrinking selection. Anchor is always the start or end of the most recent added range.
     * It may be a bit unintuitive when there are multiple ranges in selection.
     *
     * @see #focus
     * @type {module:engine/view/position~Position}
     */
    get anchor() {
        return this._selection.anchor;
    }
    /**
     * Selection focus. Focus is a position where the selection ends.
     *
     * @see #anchor
     * @type {module:engine/view/position~Position}
     */
    get focus() {
        return this._selection.focus;
    }
    /**
     * Returns whether the selection is collapsed. Selection is collapsed when there is exactly one range which is
     * collapsed.
     *
     * @type {Boolean}
     */
    get isCollapsed() {
        return this._selection.isCollapsed;
    }
    /**
     * Returns number of ranges in selection.
     *
     * @type {Number}
     */
    get rangeCount() {
        return this._selection.rangeCount;
    }
    /**
     * Specifies whether the {@link #focus} precedes {@link #anchor}.
     *
     * @type {Boolean}
     */
    get isBackward() {
        return this._selection.isBackward;
    }
    /**
     * {@link module:engine/view/editableelement~EditableElement EditableElement} instance that contains this selection, or `null`
     * if the selection is not inside an editable element.
     *
     * @type {module:engine/view/editableelement~EditableElement|null}
     */
    get editableElement() {
        return this._selection.editableElement;
    }
    /**
     * Used for the compatibility with the {@link module:engine/view/selection~Selection#isEqual} method.
     *
     * @protected
     */
    get _ranges() {
        return this._selection._ranges;
    }
    /**
     * Returns an iterable that contains copies of all ranges added to the selection.
     *
     * @returns {Iterable.<module:engine/view/range~Range>}
     */
    *getRanges() {
        yield* this._selection.getRanges();
    }
    /**
     * Returns copy of the first range in the selection. First range is the one which
     * {@link module:engine/view/range~Range#start start} position {@link module:engine/view/position~Position#isBefore is before} start
     * position of all other ranges (not to confuse with the first range added to the selection).
     * Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/range~Range|null}
     */
    getFirstRange() {
        return this._selection.getFirstRange();
    }
    /**
     * Returns copy of the last range in the selection. Last range is the one which {@link module:engine/view/range~Range#end end}
     * position {@link module:engine/view/position~Position#isAfter is after} end position of all other ranges (not to confuse
     * with the last range added to the selection). Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/range~Range|null}
     */
    getLastRange() {
        return this._selection.getLastRange();
    }
    /**
     * Returns copy of the first position in the selection. First position is the position that
     * {@link module:engine/view/position~Position#isBefore is before} any other position in the selection ranges.
     * Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/position~Position|null}
     */
    getFirstPosition() {
        return this._selection.getFirstPosition();
    }
    /**
     * Returns copy of the last position in the selection. Last position is the position that
     * {@link module:engine/view/position~Position#isAfter is after} any other position in the selection ranges.
     * Returns `null` if no ranges are added to selection.
     *
     * @returns {module:engine/view/position~Position|null}
     */
    getLastPosition() {
        return this._selection.getLastPosition();
    }
    /**
     * Returns the selected element. {@link module:engine/view/element~Element Element} is considered as selected if there is only
     * one range in the selection, and that range contains exactly one element.
     * Returns `null` if there is no selected element.
     *
     * @returns {module:engine/view/element~Element|null}
     */
    getSelectedElement() {
        return this._selection.getSelectedElement();
    }
    /**
     * Checks whether, this selection is equal to given selection. Selections are equal if they have same directions,
     * same number of ranges and all ranges from one selection equal to a range from other selection.
     *
     * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} otherSelection
     * Selection to compare with.
     * @returns {Boolean} `true` if selections are equal, `false` otherwise.
     */
    isEqual(otherSelection) {
        return this._selection.isEqual(otherSelection);
    }
    /**
     * Checks whether this selection is similar to given selection. Selections are similar if they have same directions, same
     * number of ranges, and all {@link module:engine/view/range~Range#getTrimmed trimmed} ranges from one selection are
     * equal to any trimmed range from other selection.
     *
     * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} otherSelection
     * Selection to compare with.
     * @returns {Boolean} `true` if selections are similar, `false` otherwise.
     */
    isSimilar(otherSelection) {
        return this._selection.isSimilar(otherSelection);
    }
    /**
     * Sets this selection's ranges and direction to the specified location based on the given
     * {@link module:engine/view/selection~Selectable selectable}.
     *
     *		// Sets selection to the given range.
     *		const range = writer.createRange( start, end );
     *		documentSelection._setTo( range );
     *
     *		// Sets selection to given ranges.
     * 		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( start2, end2 ) ];
     *		documentSelection._setTo( range );
     *
     *		// Sets selection to the other selection.
     *		const otherSelection = writer.createSelection();
     *		documentSelection._setTo( otherSelection );
     *
     * 		// Sets collapsed selection at the given position.
     *		const position = writer.createPositionAt( root, offset );
     *		documentSelection._setTo( position );
     *
     * 		// Sets collapsed selection at the position of given item and offset.
     *		documentSelection._setTo( paragraph, offset );
     *
     * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     *		documentSelection._setTo( paragraph, 'in' );
     *
     * Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends just after the item.
     *
     *		documentSelection._setTo( paragraph, 'on' );
     *
     * 		// Clears selection. Removes all ranges.
     *		documentSelection._setTo( null );
     *
     * `Selection#_setTo()` method allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Sets selection as backward.
     *		documentSelection._setTo( range, { backward: true } );
     *
     * Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to des cribe fake selection in DOM
     * (and be  properly handled by screen readers).
     *
     *		// Creates fake selection with label.
     *		documentSelection._setTo( range, { fake: true, label: 'foo' } );
     *
     * @protected
     * @fires change
     * @param {module:engine/view/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     */
    _setTo(...args) {
        this._selection.setTo(...args);
    }
    /**
     * Moves {@link #focus} to the specified location.
     *
     * The location can be specified in the same form as {@link module:engine/view/view~View#createPositionAt view.createPositionAt()}
     * parameters.
     *
     * @protected
     * @fires change
     * @param {module:engine/view/item~Item|module:engine/view/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/view/item~Item view item}.
     */
    _setFocus(itemOrPosition, offset) {
        this._selection.setFocus(itemOrPosition, offset);
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		docSelection.is( 'selection' ); // -> true
 *		docSelection.is( 'documentSelection' ); // -> true
 *		docSelection.is( 'view:selection' ); // -> true
 *		docSelection.is( 'view:documentSelection' ); // -> true
 *
 *		docSelection.is( 'model:documentSelection' ); // -> false
 *		docSelection.is( 'element' ); // -> false
 *		docSelection.is( 'node' ); // -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
DocumentSelection.prototype.is = function (type) {
    return type === 'selection' ||
        type == 'documentSelection' ||
        type == 'view:selection' ||
        type == 'view:documentSelection';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/bubblingeventinfo.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/bubblingeventinfo
 */

/**
 * The event object passed to bubbling event callbacks. It is used to provide information about the event as well as a tool to
 * manipulate it.
 *
 * @extends module:utils/eventinfo~EventInfo
 */
class BubblingEventInfo extends EventInfo {
    /**
     * @param {Object} source The emitter.
     * @param {String} name The event name.
     * @param {module:engine/view/range~Range} startRange The view range that the bubbling should start from.
     */
    constructor(source, name, startRange) {
        super(source, name);
        /**
         * The view range that the bubbling should start from.
         *
         * @readonly
         * @member {module:engine/view/range~Range}
         */
        this.startRange = startRange;
        /**
         * The current event phase.
         *
         * @protected
         * @member {'none'|'capturing'|'atTarget'|'bubbling'}
         */
        this._eventPhase = 'none';
        /**
         * The current bubbling target.
         *
         * @protected
         * @member {module:engine/view/document~Document|module:engine/view/node~Node|null}
         */
        this._currentTarget = null;
    }
    /**
     * The current event phase.
     *
     * @readonly
     * @member {'none'|'capturing'|'atTarget'|'bubbling'}
     */
    get eventPhase() {
        return this._eventPhase;
    }
    /**
     * The current bubbling target.
     *
     * @readonly
     * @member {module:engine/view/document~Document|module:engine/view/node~Node|null}
     */
    get currentTarget() {
        return this._currentTarget;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/bubblingemittermixin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/bubblingemittermixin
 */





const contextsSymbol = Symbol('bubbling contexts');
/**
 * Bubbling emitter mixin for the view document as described in the
 * {@link ~BubblingEmitter} interface.
 *
 * @mixin BubblingEmitterMixin
 * @implements module:engine/view/observer/bubblingemittermixin~BubblingEmitter
 */
function BubblingEmitterMixin(base) {
    class Mixin extends base {
        fire(eventOrInfo, ...eventArgs) {
            try {
                const eventInfo = eventOrInfo instanceof EventInfo ? eventOrInfo : new EventInfo(this, eventOrInfo);
                const eventContexts = getBubblingContexts(this);
                if (!eventContexts.size) {
                    return;
                }
                updateEventInfo(eventInfo, 'capturing', this);
                // The capture phase of the event.
                if (fireListenerFor(eventContexts, '$capture', eventInfo, ...eventArgs)) {
                    return eventInfo.return;
                }
                const startRange = eventInfo.startRange || this.selection.getFirstRange();
                const selectedElement = startRange ? startRange.getContainedElement() : null;
                const isCustomContext = selectedElement ? Boolean(getCustomContext(eventContexts, selectedElement)) : false;
                let node = selectedElement || getDeeperRangeParent(startRange);
                updateEventInfo(eventInfo, 'atTarget', node);
                // For the not yet bubbling event trigger for $text node if selection can be there and it's not a custom context selected.
                if (!isCustomContext) {
                    if (fireListenerFor(eventContexts, '$text', eventInfo, ...eventArgs)) {
                        return eventInfo.return;
                    }
                    updateEventInfo(eventInfo, 'bubbling', node);
                }
                while (node) {
                    // Root node handling.
                    if (node.is('rootElement')) {
                        if (fireListenerFor(eventContexts, '$root', eventInfo, ...eventArgs)) {
                            return eventInfo.return;
                        }
                    }
                    // Element node handling.
                    else if (node.is('element')) {
                        if (fireListenerFor(eventContexts, node.name, eventInfo, ...eventArgs)) {
                            return eventInfo.return;
                        }
                    }
                    // Check custom contexts (i.e., a widget).
                    if (fireListenerFor(eventContexts, node, eventInfo, ...eventArgs)) {
                        return eventInfo.return;
                    }
                    node = node.parent;
                    updateEventInfo(eventInfo, 'bubbling', node);
                }
                updateEventInfo(eventInfo, 'bubbling', this);
                // Document context.
                fireListenerFor(eventContexts, '$document', eventInfo, ...eventArgs);
                return eventInfo.return;
            }
            catch (err) {
                // @if CK_DEBUG // throw err;
                /* istanbul ignore next */
                CKEditorError.rethrowUnexpectedError(err, this);
            }
        }
        _addEventListener(event, callback, options) {
            const contexts = toArray(options.context || '$document');
            const eventContexts = getBubblingContexts(this);
            for (const context of contexts) {
                let emitter = eventContexts.get(context);
                if (!emitter) {
                    emitter = new Emitter();
                    eventContexts.set(context, emitter);
                }
                this.listenTo(emitter, event, callback, options);
            }
        }
        _removeEventListener(event, callback) {
            const eventContexts = getBubblingContexts(this);
            for (const emitter of eventContexts.values()) {
                this.stopListening(emitter, event, callback);
            }
        }
    }
    return Mixin;
}
// Backward compatibility with `mix`.
{
    const mixin = BubblingEmitterMixin(Object);
    ['fire', '_addEventListener', '_removeEventListener'].forEach(key => {
        BubblingEmitterMixin[key] = mixin.prototype[key];
    });
}
// Update the event info bubbling fields.
//
// @param {module:utils/eventinfo~EventInfo} eventInfo The event info object to update.
// @param {'none'|'capturing'|'atTarget'|'bubbling'} eventPhase The current event phase.
// @param {module:engine/view/document~Document|module:engine/view/node~Node} currentTarget The current bubbling target.
function updateEventInfo(eventInfo, eventPhase, currentTarget) {
    if (eventInfo instanceof BubblingEventInfo) {
        eventInfo._eventPhase = eventPhase;
        eventInfo._currentTarget = currentTarget;
    }
}
// Fires the listener for the specified context. Returns `true` if event was stopped.
//
// @private
// @param {Map.<String|Function, module:utils/emittermixin~Emitter>} eventContexts
// @param {String|module:engine/view/node~Node} context
// @param {module:utils/eventinfo~EventInfo} eventInfo The `EventInfo` object.
// @param {...*} [eventArgs] Additional arguments to be passed to the callbacks.
// @returns {Boolean} True if event stop was called.
function fireListenerFor(eventContexts, context, eventInfo, ...eventArgs) {
    const emitter = typeof context == 'string' ? eventContexts.get(context) : getCustomContext(eventContexts, context);
    if (!emitter) {
        return false;
    }
    emitter.fire(eventInfo, ...eventArgs);
    return eventInfo.stop.called;
}
// Returns an emitter for a specified view node.
//
// @private
// @param {Map.<String|Function, module:utils/emittermixin~Emitter>} eventContexts
// @param {module:engine/view/node~Node} node
// @returns {module:utils/emittermixin~Emitter|null}
function getCustomContext(eventContexts, node) {
    for (const [context, emitter] of eventContexts) {
        if (typeof context == 'function' && context(node)) {
            return emitter;
        }
    }
    return null;
}
// Returns bubbling contexts map for the source (emitter).
function getBubblingContexts(source) {
    if (!source[contextsSymbol]) {
        source[contextsSymbol] = new Map();
    }
    return source[contextsSymbol];
}
// Returns the deeper parent element for the range.
function getDeeperRangeParent(range) {
    if (!range) {
        return null;
    }
    const startParent = range.start.parent;
    const endParent = range.end.parent;
    const startPath = startParent.getPath();
    const endPath = endParent.getPath();
    return startPath.length > endPath.length ? startParent : endParent;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/document.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/view/document
 */




// @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' );
/**
 * Document class creates an abstract layer over the content editable area, contains a tree of view elements and
 * {@link module:engine/view/documentselection~DocumentSelection view selection} associated with this document.
 *
 * @mixes module:engine/view/observer/bubblingemittermixin~BubblingEmitterMixin
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class Document extends BubblingEmitterMixin(Observable) {
    /**
     * Creates a Document instance.
     *
     * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
     */
    constructor(stylesProcessor) {
        super();
        /**
         * Selection done on this document.
         *
         * @readonly
         * @member {module:engine/view/documentselection~DocumentSelection} module:engine/view/document~Document#selection
         */
        this.selection = new DocumentSelection();
        /**
         * Roots of the view tree. Collection of the {@link module:engine/view/element~Element view elements}.
         *
         * View roots are created as a result of binding between {@link module:engine/view/document~Document#roots} and
         * {@link module:engine/model/document~Document#roots} and this is handled by
         * {@link module:engine/controller/editingcontroller~EditingController}, so to create view root we need to create
         * model root using {@link module:engine/model/document~Document#createRoot}.
         *
         * @readonly
         * @member {module:utils/collection~Collection} module:engine/view/document~Document#roots
         */
        this.roots = new Collection({ idProperty: 'rootName' });
        /**
         * The styles processor instance used by this document when normalizing styles.
         *
         * @readonly
         * @member {module:engine/view/stylesmap~StylesProcessor}
         */
        this.stylesProcessor = stylesProcessor;
        /**
         * Defines whether document is in read-only mode.
         *
         * When document is read-ony then all roots are read-only as well and caret placed inside this root is hidden.
         *
         * @observable
         * @member {Boolean} #isReadOnly
         */
        this.set('isReadOnly', false);
        /**
         * True if document is focused.
         *
         * This property is updated by the {@link module:engine/view/observer/focusobserver~FocusObserver}.
         * If the {@link module:engine/view/observer/focusobserver~FocusObserver} is disabled this property will not change.
         *
         * @readonly
         * @observable
         * @member {Boolean} module:engine/view/document~Document#isFocused
         */
        this.set('isFocused', false);
        /**
         * `true` while the user is making a selection in the document (e.g. holding the mouse button and moving the cursor).
         * When they stop selecting, the property goes back to `false`.
         *
         * This property is updated by the {@link module:engine/view/observer/selectionobserver~SelectionObserver}.
         *
         * @readonly
         * @observable
         * @member {Boolean} module:engine/view/document~Document#isSelecting
         */
        this.set('isSelecting', false);
        /**
         * True if composition is in progress inside the document.
         *
         * This property is updated by the {@link module:engine/view/observer/compositionobserver~CompositionObserver}.
         * If the {@link module:engine/view/observer/compositionobserver~CompositionObserver} is disabled this property will not change.
         *
         * @readonly
         * @observable
         * @member {Boolean} module:engine/view/document~Document#isComposing
         */
        this.set('isComposing', false);
        /**
         * Post-fixer callbacks registered to the view document.
         *
         * @private
         * @member {Set}
         */
        this._postFixers = new Set();
    }
    /**
     * Gets a {@link module:engine/view/document~Document#roots view root element} with the specified name. If the name is not
     * specific "main" root is returned.
     *
     * @param {String} [name='main'] Name of the root.
     * @returns {module:engine/view/rooteditableelement~RootEditableElement|null} The view root element with the specified name
     * or null when there is no root of given name.
     */
    getRoot(name = 'main') {
        return this.roots.get(name);
    }
    /**
     * Allows registering post-fixer callbacks. A post-fixers mechanism allows to update the view tree just before it is rendered
     * to the DOM.
     *
     * Post-fixers are executed right after all changes from the outermost change block were applied but
     * before the {@link module:engine/view/view~View#event:render render event} is fired. If a post-fixer callback made
     * a change, it should return `true`. When this happens, all post-fixers are fired again to check if something else should
     * not be fixed in the new document tree state.
     *
     * View post-fixers are useful when you want to apply some fixes whenever the view structure changes. Keep in mind that
     * changes executed in a view post-fixer should not break model-view mapping.
     *
     * The types of changes which should be safe:
     *
     * * adding or removing attribute from elements,
     * * changes inside of {@link module:engine/view/uielement~UIElement UI elements},
     * * {@link module:engine/controller/editingcontroller~EditingController#reconvertItem marking some of the model elements to be
     * re-converted}.
     *
     * Try to avoid changes which touch view structure:
     *
     * * you should not add or remove nor wrap or unwrap any view elements,
     * * you should not change the editor data model in a view post-fixer.
     *
     * As a parameter, a post-fixer callback receives a {@link module:engine/view/downcastwriter~DowncastWriter downcast writer}.
     *
     * Typically, a post-fixer will look like this:
     *
     *		editor.editing.view.document.registerPostFixer( writer => {
     *			if ( checkSomeCondition() ) {
     *				writer.doSomething();
     *
     *				// Let other post-fixers know that something changed.
     *				return true;
     *			}
     *		} );
     *
     * Note that nothing happens right after you register a post-fixer (e.g. execute such a code in the console).
     * That is because adding a post-fixer does not execute it.
     * The post-fixer will be executed as soon as any change in the document needs to cause its rendering.
     * If you want to re-render the editor's view after registering the post-fixer then you should do it manually by calling
     * {@link module:engine/view/view~View#forceRender `view.forceRender()`}.
     *
     * If you need to register a callback which is executed when DOM elements are already updated,
     * use {@link module:engine/view/view~View#event:render render event}.
     *
     * @param {Function} postFixer
     */
    registerPostFixer(postFixer) {
        this._postFixers.add(postFixer);
    }
    /**
     * Destroys this instance. Makes sure that all observers are destroyed and listeners removed.
     */
    destroy() {
        this.roots.map(root => root.destroy());
        this.stopListening();
    }
    /**
     * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model.
     *
     * @protected
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer
     */
    _callPostFixers(writer) {
        let wasFixed = false;
        do {
            for (const callback of this._postFixers) {
                wasFixed = callback(writer);
                if (wasFixed) {
                    break;
                }
            }
        } while (wasFixed);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/attributeelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/attributeelement
 */


// Default attribute priority.
const DEFAULT_PRIORITY = 10;
/**
 * Attribute elements are used to represent formatting elements in the view (think – `<b>`, `<span style="font-size: 2em">`, etc.).
 * Most often they are created when downcasting model text attributes.
 *
 * Editing engine does not define a fixed HTML DTD. This is why a feature developer needs to choose between various
 * types (container element, {@link module:engine/view/attributeelement~AttributeElement attribute element},
 * {@link module:engine/view/emptyelement~EmptyElement empty element}, etc) when developing a feature.
 *
 * To create a new attribute element instance use the
 * {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement `DowncastWriter#createAttributeElement()`} method.
 *
 * @extends module:engine/view/element~Element
 */
class AttributeElement extends Element {
    /**
     * Creates an attribute element.
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#createAttributeElement
     * @see module:engine/view/element~Element
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
     * @param {String} name Node name.
     * @param {Object|Iterable} [attrs] Collection of attributes.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into created element.
     */
    constructor(...args) {
        super(...args);
        /**
         * Returns block {@link module:engine/view/filler filler} offset or `null` if block filler is not needed.
         *
         * @method #getFillerOffset
         * @returns {Number|null} Block filler offset or `null` if block filler is not needed.
         */
        this.getFillerOffset = attributeelement_getFillerOffset;
        /**
         * Element priority. Decides in what order elements are wrapped by {@link module:engine/view/downcastwriter~DowncastWriter}.
         *
         * @protected
         * @member {Number}
         */
        this._priority = DEFAULT_PRIORITY;
        /**
         * Element identifier. If set, it is used by {@link module:engine/view/element~Element#isSimilar},
         * and then two elements are considered similar if, and only if they have the same `_id`.
         *
         * @protected
         * @member {String|Number}
         */
        this._id = null;
        /**
         * Keeps all the attribute elements that have the same {@link module:engine/view/attributeelement~AttributeElement#id ids}
         * and still exist in the view tree.
         *
         * This property is managed by {@link module:engine/view/downcastwriter~DowncastWriter}.
         *
         * @protected
         * @member {Set.<module:engine/view/attributeelement~AttributeElement>|null}
         */
        this._clonesGroup = null;
    }
    /**
     * Element priority. Decides in what order elements are wrapped by {@link module:engine/view/downcastwriter~DowncastWriter}.
     *
     * @readonly
     * @type {Number}
     */
    get priority() {
        return this._priority;
    }
    /**
     * Element identifier. If set, it is used by {@link module:engine/view/element~Element#isSimilar},
     * and then two elements are considered similar if, and only if they have the same `id`.
     *
     * @readonly
     * @type {String|Number}
     */
    get id() {
        return this._id;
    }
    /**
     * Returns all {@link module:engine/view/attributeelement~AttributeElement attribute elements} that has the
     * same {@link module:engine/view/attributeelement~AttributeElement#id id} and are in the view tree (were not removed).
     *
     * Note: If this element has been removed from the tree, returned set will not include it.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError attribute-element-get-elements-with-same-id-no-id}
     * if this element has no `id`.
     *
     * @returns {Set.<module:engine/view/attributeelement~AttributeElement>} Set containing all the attribute elements
     * with the same `id` that were added and not removed from the view tree.
     */
    getElementsWithSameId() {
        if (this.id === null) {
            /**
             * Cannot get elements with the same id for an attribute element without id.
             *
             * @error attribute-element-get-elements-with-same-id-no-id
             */
            throw new CKEditorError('attribute-element-get-elements-with-same-id-no-id', this);
        }
        return new Set(this._clonesGroup);
    }
    /**
     * Checks if this element is similar to other element.
     *
     * If none of elements has set {@link module:engine/view/attributeelement~AttributeElement#id}, then both elements
     * should have the same name, attributes and priority to be considered as similar. Two similar elements can contain
     * different set of children nodes.
     *
     * If at least one element has {@link module:engine/view/attributeelement~AttributeElement#id} set, then both
     * elements have to have the same {@link module:engine/view/attributeelement~AttributeElement#id} value to be
     * considered similar.
     *
     * Similarity is important for {@link module:engine/view/downcastwriter~DowncastWriter}. For example:
     *
     * * two following similar elements can be merged together into one, longer element,
     * * {@link module:engine/view/downcastwriter~DowncastWriter#unwrap} checks similarity of passed element and processed element to
     * decide whether processed element should be unwrapped,
     * * etc.
     *
     * @param {module:engine/view/element~Element} otherElement
     * @returns {Boolean}
     */
    isSimilar(otherElement) {
        // If any element has an `id` set, just compare the ids.
        if (this.id !== null || otherElement.id !== null) {
            return this.id === otherElement.id;
        }
        return super.isSimilar(otherElement) && this.priority == otherElement.priority;
    }
    /**
     * Clones provided element with priority.
     *
     * @protected
     * @param {Boolean} deep If set to `true` clones element and all its children recursively. When set to `false`,
     * element will be cloned without any children.
     * @returns {module:engine/view/attributeelement~AttributeElement} Clone of this element.
     */
    _clone(deep = false) {
        const cloned = super._clone(deep);
        // Clone priority too.
        cloned._priority = this._priority;
        // And id too.
        cloned._id = this._id;
        return cloned;
    }
}
AttributeElement.DEFAULT_PRIORITY = DEFAULT_PRIORITY;
/**
 * Checks whether this object is of the given.
 *
 *		attributeElement.is( 'attributeElement' ); // -> true
 *		attributeElement.is( 'element' ); // -> true
 *		attributeElement.is( 'node' ); // -> true
 *		attributeElement.is( 'view:attributeElement' ); // -> true
 *		attributeElement.is( 'view:element' ); // -> true
 *		attributeElement.is( 'view:node' ); // -> true
 *
 *		attributeElement.is( 'model:element' ); // -> false
 *		attributeElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is an attribute element, you can also check its
 * {@link module:engine/view/attributeelement~AttributeElement#name name}:
 *
 *		attributeElement.is( 'element', 'b' ); // -> true if this is a bold element
 *		attributeElement.is( 'attributeElement', 'b' ); // -> same as above
 *		text.is( 'element', 'b' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
AttributeElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'attributeElement' || type === 'view:attributeElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'element' || type === 'view:element' ||
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'attributeElement' || type === 'view:attributeElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'element' || type === 'view:element');
    }
};
// Returns block {@link module:engine/view/filler~Filler filler} offset or `null` if block filler is not needed.
//
// @returns {Number|null} Block filler offset or `null` if block filler is not needed.
function attributeelement_getFillerOffset() {
    // <b>foo</b> does not need filler.
    if (nonUiChildrenCount(this)) {
        return null;
    }
    let element = this.parent;
    // <p><b></b></p> needs filler -> <p><b><br></b></p>
    while (element && element.is('attributeElement')) {
        if (nonUiChildrenCount(element) > 1) {
            return null;
        }
        element = element.parent;
    }
    if (!element || nonUiChildrenCount(element) > 1) {
        return null;
    }
    // Render block filler at the end of element (after all ui elements).
    return this.childCount;
}
// Returns total count of children that are not {@link module:engine/view/uielement~UIElement UIElements}.
//
// @param {module:engine/view/element~Element} element
// @returns {Number}
function nonUiChildrenCount(element) {
    return Array.from(element.getChildren()).filter(element => !element.is('uiElement')).length;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/emptyelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/emptyelement
 */



/**
 * Empty element class. It is used to represent elements that cannot contain any child nodes (for example `<img>` elements).
 *
 * To create a new empty element use the
 * {@link module:engine/view/downcastwriter~DowncastWriter#createEmptyElement `downcastWriter#createEmptyElement()`} method.
 *
 * @extends module:engine/view/element~Element
 */
class EmptyElement extends Element {
    /**
     * Creates new instance of EmptyElement.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-emptyelement-cannot-add` when third parameter is passed,
     * to inform that usage of EmptyElement is incorrect (adding child nodes to EmptyElement is forbidden).
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#createEmptyElement
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
     * @param {String} name Node name.
     * @param {Object|Iterable} [attrs] Collection of attributes.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into created element.
     */
    constructor(document, name, attributes, children) {
        super(document, name, attributes, children);
        /**
         * Returns `null` because filler is not needed for EmptyElements.
         *
         * @method #getFillerOffset
         * @returns {null} Always returns null.
         */
        this.getFillerOffset = emptyelement_getFillerOffset;
    }
    /**
     * Overrides {@link module:engine/view/element~Element#_insertChild} method.
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-emptyelement-cannot-add` to prevent
     * adding any child nodes to EmptyElement.
     *
     * @protected
     */
    _insertChild(index, items) {
        if (items && (items instanceof node_Node || Array.from(items).length > 0)) {
            /**
             * Cannot add children to {@link module:engine/view/emptyelement~EmptyElement}.
             *
             * @error view-emptyelement-cannot-add
             */
            throw new CKEditorError('view-emptyelement-cannot-add', [this, items]);
        }
        return 0;
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		emptyElement.is( 'emptyElement' ); // -> true
 *		emptyElement.is( 'element' ); // -> true
 *		emptyElement.is( 'node' ); // -> true
 *		emptyElement.is( 'view:emptyElement' ); // -> true
 *		emptyElement.is( 'view:element' ); // -> true
 *		emptyElement.is( 'view:node' ); // -> true
 *
 *		emptyElement.is( 'model:element' ); // -> false
 *		emptyElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is an empty element, you can also check its
 * {@link module:engine/view/emptyelement~EmptyElement#name name}:
 *
 *		emptyElement.is( 'element', 'img' ); // -> true if this is a img element
 *		emptyElement.is( 'emptyElement', 'img' ); // -> same as above
 *		text.is( 'element', 'img' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
EmptyElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'emptyElement' || type === 'view:emptyElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'element' || type === 'view:element' ||
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'emptyElement' || type === 'view:emptyElement' ||
            type === 'element' || type === 'view:element');
    }
};
// Returns `null` because block filler is not needed for EmptyElements.
//
// @returns {null}
function emptyelement_getFillerOffset() {
    return null;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/env.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* globals navigator:false */
/**
 * @module utils/env
 */
/**
 * Safely returns `userAgent` from browser's navigator API in a lower case.
 * If navigator API is not available it will return an empty string.
 *
 * @returns {String}
 */
function getUserAgent() {
    // In some environments navigator API might not be available.
    try {
        return navigator.userAgent.toLowerCase();
    }
    catch (e) {
        return '';
    }
}
const userAgent = getUserAgent();
/**
 * A namespace containing environment and browser information.
 *
 * @namespace
 */
const env_env = {
    /**
     * Indicates that the application is running on Macintosh.
     *
     * @static
     * @type {Boolean}
     */
    isMac: isMac(userAgent),
    /**
     * Indicates that the application is running on Windows.
     *
     * @static
     * @type {Boolean}
     */
    isWindows: isWindows(userAgent),
    /**
     * Indicates that the application is running in Firefox (Gecko).
     *
     * @static
     * @type {Boolean}
     */
    isGecko: isGecko(userAgent),
    /**
     * Indicates that the application is running in Safari.
     *
     * @static
     * @type {Boolean}
     */
    isSafari: isSafari(userAgent),
    /**
     * Indicates the the application is running in iOS.
     *
     * @static
     * @type {Boolean}
     */
    isiOS: isiOS(userAgent),
    /**
     * Indicates that the application is running on Android mobile device.
     *
     * @static
     * @type {Boolean}
     */
    isAndroid: isAndroid(userAgent),
    /**
     * Indicates that the application is running in a browser using the Blink engine.
     *
     * @static
     * @type {Boolean}
     */
    isBlink: isBlink(userAgent),
    /**
     * Environment features information.
     *
     * @memberOf module:utils/env~env
     * @namespace
     */
    features: {
        /**
         * Indicates that the environment supports ES2018 Unicode property escapes — like `\p{P}` or `\p{L}`.
         * More information about unicode properties might be found
         * [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
         *
         * @type {Boolean}
         */
        isRegExpUnicodePropertySupported: isRegExpUnicodePropertySupported()
    }
};
/* harmony default export */ const src_env = (env_env);
/**
 * Checks if User Agent represented by the string is running on Macintosh.
 *
 * @param {String} userAgent **Lowercase** `navigator.userAgent` string.
 * @returns {Boolean} Whether User Agent is running on Macintosh or not.
 */
function isMac(userAgent) {
    return userAgent.indexOf('macintosh') > -1;
}
/**
 * Checks if User Agent represented by the string is running on Windows.
 *
 * @param {String} userAgent **Lowercase** `navigator.userAgent` string.
 * @returns {Boolean} Whether User Agent is running on Windows or not.
 */
function isWindows(userAgent) {
    return userAgent.indexOf('windows') > -1;
}
/**
 * Checks if User Agent represented by the string is Firefox (Gecko).
 *
 * @param {String} userAgent **Lowercase** `navigator.userAgent` string.
 * @returns {Boolean} Whether User Agent is Firefox or not.
 */
function isGecko(userAgent) {
    return !!userAgent.match(/gecko\/\d+/);
}
/**
 * Checks if User Agent represented by the string is Safari.
 *
 * @param {String} userAgent **Lowercase** `navigator.userAgent` string.
 * @returns {Boolean} Whether User Agent is Safari or not.
 */
function isSafari(userAgent) {
    return userAgent.indexOf(' applewebkit/') > -1 && userAgent.indexOf('chrome') === -1;
}
/**
 * Checks if User Agent represented by the string is running in iOS.
 *
 * @param {String} userAgent **Lowercase** `navigator.userAgent` string.
 * @returns {Boolean} Whether User Agent is running in iOS or not.
 */
function isiOS(userAgent) {
    // "Request mobile site" || "Request desktop site".
    return !!userAgent.match(/iphone|ipad/i) || (isMac(userAgent) && navigator.maxTouchPoints > 0);
}
/**
 * Checks if User Agent represented by the string is Android mobile device.
 *
 * @param {String} userAgent **Lowercase** `navigator.userAgent` string.
 * @returns {Boolean} Whether User Agent is Safari or not.
 */
function isAndroid(userAgent) {
    return userAgent.indexOf('android') > -1;
}
/**
 * Checks if User Agent represented by the string is Blink engine.
 *
 * @param {String} userAgent **Lowercase** `navigator.userAgent` string.
 * @returns {Boolean} Whether User Agent is Blink engine or not.
 */
function isBlink(userAgent) {
    // The Edge browser before switching to the Blink engine used to report itself as Chrome (and "Edge/")
    // but after switching to the Blink it replaced "Edge/" with "Edg/".
    return userAgent.indexOf('chrome/') > -1 && userAgent.indexOf('edge/') < 0;
}
/**
 * Checks if the current environment supports ES2018 Unicode properties like `\p{P}` or `\p{L}`.
 * More information about unicode properties might be found
 * [in Unicode Standard Annex #44](https://www.unicode.org/reports/tr44/#GC_Values_Table).
 *
 * @returns {Boolean}
 */
function isRegExpUnicodePropertySupported() {
    let isSupported = false;
    // Feature detection for Unicode properties. Added in ES2018. Currently Firefox does not support it.
    // See https://github.com/ckeditor/ckeditor5-mention/issues/44#issuecomment-487002174.
    try {
        // Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534).
        isSupported = 'ć'.search(new RegExp('[\\p{L}]', 'u')) === 0;
    }
    catch (error) {
        // Firefox throws a SyntaxError when the group is unsupported.
    }
    return isSupported;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/keyboard.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */


const modifiersToGlyphsMac = {
    ctrl: '⌃',
    cmd: '⌘',
    alt: '⌥',
    shift: '⇧'
};
const modifiersToGlyphsNonMac = {
    ctrl: 'Ctrl+',
    alt: 'Alt+',
    shift: 'Shift+'
};
/**
 * An object with `keyName => keyCode` pairs for a set of known keys.
 *
 * Contains:
 *
 * * `a-z`,
 * * `0-9`,
 * * `f1-f12`,
 * * `` ` ``, `-`, `=`, `[`, `]`, `;`, `'`, `,`, `.`, `/`, `\`,
 * * `arrow(left|up|right|bottom)`,
 * * `backspace`, `delete`, `enter`, `esc`, `tab`,
 * * `ctrl`, `cmd`, `shift`, `alt`.
 */
const keyCodes = generateKnownKeyCodes();
const keyCodeNames = Object.fromEntries(Object.entries(keyCodes).map(([name, code]) => [code, name.charAt(0).toUpperCase() + name.slice(1)]));
/**
 * Converts a key name or {@link module:utils/keyboard~KeystrokeInfo keystroke info} into a key code.
 *
 * Note: Key names are matched with {@link module:utils/keyboard~keyCodes} in a case-insensitive way.
 *
 * @param {String|module:utils/keyboard~KeystrokeInfo} A key name (see {@link module:utils/keyboard~keyCodes})
 * or a keystroke data object.
 * @returns {Number} Key or keystroke code.
 */
function getCode(key) {
    let keyCode;
    if (typeof key == 'string') {
        keyCode = keyCodes[key.toLowerCase()];
        if (!keyCode) {
            /**
             * Unknown key name. Only key names included in the {@link module:utils/keyboard~keyCodes} can be used.
             *
             * @error keyboard-unknown-key
             * @param {String} key
             */
            throw new CKEditorError('keyboard-unknown-key', null, { key });
        }
    }
    else {
        keyCode = key.keyCode +
            (key.altKey ? keyCodes.alt : 0) +
            (key.ctrlKey ? keyCodes.ctrl : 0) +
            (key.shiftKey ? keyCodes.shift : 0) +
            (key.metaKey ? keyCodes.cmd : 0);
    }
    return keyCode;
}
/**
 * Parses the keystroke and returns a keystroke code that will match the code returned by
 * {@link module:utils/keyboard~getCode} for the corresponding {@link module:utils/keyboard~KeystrokeInfo keystroke info}.
 *
 * The keystroke can be passed in two formats:
 *
 * * as a single string – e.g. `ctrl + A`,
 * * as an array of {@link module:utils/keyboard~keyCodes known key names} and key codes – e.g.:
 *   * `[ 'ctrl', 32 ]` (ctrl + space),
 *   * `[ 'ctrl', 'a' ]` (ctrl + A).
 *
 * Note: Key names are matched with {@link module:utils/keyboard~keyCodes} in a case-insensitive way.
 *
 * Note: Only keystrokes with a single non-modifier key are supported (e.g. `ctrl+A` is OK, but `ctrl+A+B` is not).
 *
 * Note: On macOS, keystroke handling is translating the `Ctrl` key to the `Cmd` key and handling only that keystroke.
 * For example, a registered keystroke `Ctrl+A` will be translated to `Cmd+A` on macOS. To disable the translation of some keystroke,
 * use the forced modifier: `Ctrl!+A` (note the exclamation mark).
 *
 * @param {String|Array.<Number|String>} keystroke The keystroke definition.
 * @returns {Number} Keystroke code.
 */
function parseKeystroke(keystroke) {
    if (typeof keystroke == 'string') {
        keystroke = splitKeystrokeText(keystroke);
    }
    return keystroke
        .map(key => (typeof key == 'string') ? getEnvKeyCode(key) : key)
        .reduce((key, sum) => sum + key, 0);
}
/**
 * Translates any keystroke string text like `"Ctrl+A"` to an
 * environment–specific keystroke, i.e. `"⌘A"` on macOS.
 *
 * @param {String} keystroke The keystroke text.
 * @returns {String} The keystroke text specific for the environment.
 */
function getEnvKeystrokeText(keystroke) {
    let keystrokeCode = parseKeystroke(keystroke);
    const modifiersToGlyphs = Object.entries(src_env.isMac ? modifiersToGlyphsMac : modifiersToGlyphsNonMac);
    const modifiers = modifiersToGlyphs.reduce((modifiers, [name, glyph]) => {
        // Modifier keys are stored as a bit mask so extract those from the keystroke code.
        if ((keystrokeCode & keyCodes[name]) != 0) {
            keystrokeCode &= ~keyCodes[name];
            modifiers += glyph;
        }
        return modifiers;
    }, '');
    return modifiers + (keystrokeCode ? keyCodeNames[keystrokeCode] : '');
}
/**
 * Returns `true` if the provided key code represents one of the arrow keys.
 *
 * @param {Number} keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
 * @returns {Boolean}
 */
function isArrowKeyCode(keyCode) {
    return keyCode == keyCodes.arrowright ||
        keyCode == keyCodes.arrowleft ||
        keyCode == keyCodes.arrowup ||
        keyCode == keyCodes.arrowdown;
}
/**
 * Returns the direction in which the {@link module:engine/model/documentselection~DocumentSelection selection}
 * will move when the provided arrow key code is pressed considering the language direction of the editor content.
 *
 * For instance, in right–to–left (RTL) content languages, pressing the left arrow means moving the selection right (forward)
 * in the model structure. Similarly, pressing the right arrow moves the selection left (backward).
 *
 * @param {Number} keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
 * @param {module:utils/language~LanguageDirection} contentLanguageDirection The content language direction, corresponding to
 * {@link module:utils/locale~Locale#contentLanguageDirection}.
 * @returns {module:utils/keyboard~ArrowKeyCodeDirection|undefined} Localized arrow direction or `undefined` for non-arrow key codes.
 */
function getLocalizedArrowKeyCodeDirection(keyCode, contentLanguageDirection) {
    const isLtrContent = contentLanguageDirection === 'ltr';
    switch (keyCode) {
        case keyCodes.arrowleft:
            return isLtrContent ? 'left' : 'right';
        case keyCodes.arrowright:
            return isLtrContent ? 'right' : 'left';
        case keyCodes.arrowup:
            return 'up';
        case keyCodes.arrowdown:
            return 'down';
    }
}
// Converts a key name to the key code with mapping based on the env.
//
// See: {@link module:utils/keyboard~getCode}.
//
// @param {String} key The key name (see {@link module:utils/keyboard~keyCodes}).
// @returns {Number} Key code.
function getEnvKeyCode(key) {
    // Don't remap modifier key for forced modifiers.
    if (key.endsWith('!')) {
        return getCode(key.slice(0, -1));
    }
    const code = getCode(key);
    return src_env.isMac && code == keyCodes.ctrl ? keyCodes.cmd : code;
}
/**
 * Determines if the provided key code moves the {@link module:engine/model/documentselection~DocumentSelection selection}
 * forward or backward considering the language direction of the editor content.
 *
 * For instance, in right–to–left (RTL) languages, pressing the left arrow means moving forward
 * in the model structure. Similarly, pressing the right arrow moves the selection backward.
 *
 * @param {Number} keyCode A key code as in {@link module:utils/keyboard~KeystrokeInfo#keyCode}.
 * @param {module:utils/language~LanguageDirection} contentLanguageDirection The content language direction, corresponding to
 * {@link module:utils/locale~Locale#contentLanguageDirection}.
 * @returns {Boolean}
 */
function isForwardArrowKeyCode(keyCode, contentLanguageDirection) {
    const localizedKeyCodeDirection = getLocalizedArrowKeyCodeDirection(keyCode, contentLanguageDirection);
    return localizedKeyCodeDirection === 'down' || localizedKeyCodeDirection === 'right';
}
function generateKnownKeyCodes() {
    const keyCodes = {
        arrowleft: 37,
        arrowup: 38,
        arrowright: 39,
        arrowdown: 40,
        backspace: 8,
        delete: 46,
        enter: 13,
        space: 32,
        esc: 27,
        tab: 9,
        // The idea about these numbers is that they do not collide with any real key codes, so we can use them
        // like bit masks.
        ctrl: 0x110000,
        shift: 0x220000,
        alt: 0x440000,
        cmd: 0x880000
    };
    // a-z
    for (let code = 65; code <= 90; code++) {
        const letter = String.fromCharCode(code);
        keyCodes[letter.toLowerCase()] = code;
    }
    // 0-9
    for (let code = 48; code <= 57; code++) {
        keyCodes[code - 48] = code;
    }
    // F1-F12
    for (let code = 112; code <= 123; code++) {
        keyCodes['f' + (code - 111)] = code;
    }
    // other characters
    for (const char of '`-=[];\',./\\') {
        keyCodes[char] = char.charCodeAt(0);
    }
    return keyCodes;
}
function splitKeystrokeText(keystroke) {
    return keystroke.split('+').map(key => key.trim());
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/uielement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/uielement
 */




/**
 * UI element class. It should be used to represent editing UI which needs to be injected into the editing view
 * If possible, you should keep your UI outside the editing view. However, if that is not possible,
 * UI elements can be used.
 *
 * How a UI element is rendered is in your control (you pass a callback to
 * {@link module:engine/view/downcastwriter~DowncastWriter#createUIElement `downcastWriter#createUIElement()`}).
 * The editor will ignore your UI element – the selection cannot be placed in it, it is skipped (invisible) when
 * the user modifies the selection by using arrow keys and the editor does not listen to any mutations which
 * happen inside your UI elements.
 *
 * The limitation is that you cannot convert a model element to a UI element. UI elements need to be
 * created for {@link module:engine/model/markercollection~Marker markers} or as additinal elements
 * inside normal {@link module:engine/view/containerelement~ContainerElement container elements}.
 *
 * To create a new UI element use the
 * {@link module:engine/view/downcastwriter~DowncastWriter#createUIElement `downcastWriter#createUIElement()`} method.
 *
 * @extends module:engine/view/element~Element
 */
class UIElement extends Element {
    /**
     * Creates new instance of UIElement.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-uielement-cannot-add` when third parameter is passed,
     * to inform that usage of UIElement is incorrect (adding child nodes to UIElement is forbidden).
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#createUIElement
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
     * @param {String} name Node name.
     * @param {Object|Iterable} [attributes] Collection of attributes.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into created element.
     */
    constructor(...args) {
        super(...args);
        /**
         * Returns `null` because filler is not needed for UIElements.
         *
         * @method #getFillerOffset
         * @returns {null} Always returns null.
         */
        this.getFillerOffset = uielement_getFillerOffset;
    }
    /**
     * Overrides {@link module:engine/view/element~Element#_insertChild} method.
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-uielement-cannot-add` to prevent adding any child nodes
     * to UIElement.
     *
     * @protected
     */
    _insertChild(index, items) {
        if (items && (items instanceof node_Node || Array.from(items).length > 0)) {
            /**
             * Cannot add children to {@link module:engine/view/uielement~UIElement}.
             *
             * @error view-uielement-cannot-add
             */
            throw new CKEditorError('view-uielement-cannot-add', [this, items]);
        }
        return 0;
    }
    /**
     * Renders this {@link module:engine/view/uielement~UIElement} to DOM. This method is called by
     * {@link module:engine/view/domconverter~DomConverter}.
     * Do not use inheritance to create custom rendering method, replace `render()` method instead:
     *
     *		const myUIElement = downcastWriter.createUIElement( 'span' );
     *		myUIElement.render = function( domDocument, domConverter ) {
     *			const domElement = this.toDomElement( domDocument );
     *
     *			domConverter.setContentOf( domElement, '<b>this is ui element</b>' );
     *
     *			return domElement;
     *		};
     *
     * If changes in your UI element should trigger some editor UI update you should call
     * the {@link module:core/editor/editorui~EditorUI#update `editor.ui.update()`} method
     * after rendering your UI element.
     *
     * @param {Document} domDocument
     * @param {module:engine/view/domconverter~DomConverter} domConverter Instance of the DomConverter used to optimize the output.
     * @returns {HTMLElement}
     */
    render(domDocument, domConverter) {
        // Provide basic, default output.
        return this.toDomElement(domDocument);
    }
    /**
     * Creates DOM element based on this view UIElement.
     * Note that each time this method is called new DOM element is created.
     *
     * @param {Document} domDocument
     * @returns {HTMLElement}
     */
    toDomElement(domDocument) {
        const domElement = domDocument.createElement(this.name);
        for (const key of this.getAttributeKeys()) {
            domElement.setAttribute(key, this.getAttribute(key));
        }
        return domElement;
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		uiElement.is( 'uiElement' ); // -> true
 *		uiElement.is( 'element' ); // -> true
 *		uiElement.is( 'node' ); // -> true
 *		uiElement.is( 'view:uiElement' ); // -> true
 *		uiElement.is( 'view:element' ); // -> true
 *		uiElement.is( 'view:node' ); // -> true
 *
 *		uiElement.is( 'model:element' ); // -> false
 *		uiElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is an ui element, you can also check its
 * {@link module:engine/view/uielement~UIElement#name name}:
 *
 *		uiElement.is( 'element', 'span' ); // -> true if this is a span ui element
 *		uiElement.is( 'uiElement', 'span' ); // -> same as above
 *		text.is( 'element', 'span' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
UIElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'uiElement' || type === 'view:uiElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'element' || type === 'view:element' ||
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'uiElement' || type === 'view:uiElement' ||
            type === 'element' || type === 'view:element');
    }
};
/**
 * This function injects UI element handling to the given {@link module:engine/view/document~Document document}.
 *
 * A callback is added to {@link module:engine/view/document~Document#event:keydown document keydown event}.
 * The callback handles the situation when right arrow key is pressed and selection is collapsed before a UI element.
 * Without this handler, it would be impossible to "jump over" UI element using right arrow key.
 *
 * @param {module:engine/view/view~View} view View controller to which the quirks handling will be injected.
 */
function injectUiElementHandling(view) {
    view.document.on('arrowKey', (evt, data) => jumpOverUiElement(evt, data, view.domConverter), { priority: 'low' });
}
// Returns `null` because block filler is not needed for UIElements.
//
// @returns {null}
function uielement_getFillerOffset() {
    return null;
}
// Selection cannot be placed in a `UIElement`. Whenever it is placed there, it is moved before it. This
// causes a situation when it is impossible to jump over `UIElement` using right arrow key, because the selection
// ends up in ui element (in DOM) and is moved back to the left. This handler fixes this situation.
function jumpOverUiElement(evt, data, domConverter) {
    if (data.keyCode == keyCodes.arrowright) {
        const domSelection = data.domTarget.ownerDocument.defaultView.getSelection();
        const domSelectionCollapsed = domSelection.rangeCount == 1 && domSelection.getRangeAt(0).collapsed;
        // Jump over UI element if selection is collapsed or shift key is pressed. These are the cases when selection would extend.
        if (domSelectionCollapsed || data.shiftKey) {
            const domParent = domSelection.focusNode;
            const domOffset = domSelection.focusOffset;
            const viewPosition = domConverter.domPositionToView(domParent, domOffset);
            // In case if dom element is not converted to view or is not mapped or something. Happens for example in some tests.
            if (viewPosition === null) {
                return;
            }
            // Skip all following ui elements.
            let jumpedOverAnyUiElement = false;
            const nextViewPosition = viewPosition.getLastMatchingPosition(value => {
                if (value.item.is('uiElement')) {
                    // Remember that there was at least one ui element.
                    jumpedOverAnyUiElement = true;
                }
                // Jump over ui elements, jump over empty attribute elements, move up from inside of attribute element.
                if (value.item.is('uiElement') || value.item.is('attributeElement')) {
                    return true;
                }
                // Don't jump over text or don't get out of container element.
                return false;
            });
            // If anything has been skipped, fix position.
            // This `if` could be possibly omitted but maybe it is better not to mess with DOM selection if not needed.
            if (jumpedOverAnyUiElement) {
                const newDomPosition = domConverter.viewPositionToDom(nextViewPosition);
                if (domSelectionCollapsed) {
                    // Selection was collapsed, so collapse it at further position.
                    domSelection.collapse(newDomPosition.parent, newDomPosition.offset);
                }
                else {
                    // Selection was not collapse, so extend it instead of collapsing.
                    domSelection.extend(newDomPosition.parent, newDomPosition.offset);
                }
            }
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/rawelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/rawelement
 */



/**
 * The raw element class.
 *
 * The raw elements work as data containers ("wrappers", "sandboxes") but their children are not managed or
 * even recognized by the editor. This encapsulation allows integrations to maintain custom DOM structures
 * in the editor content without, for instance, worrying about compatibility with other editor features.
 * Raw elements are a perfect tool for integration with external frameworks and data sources.
 *
 * Unlike {@link module:engine/view/uielement~UIElement UI elements}, raw elements act like real editor
 * content (similar to {@link module:engine/view/containerelement~ContainerElement} or
 * {@link module:engine/view/emptyelement~EmptyElement}), they are considered by the editor selection and
 * {@link module:widget/utils~toWidget they can work as widgets}.
 *
 * To create a new raw element, use the
 * {@link module:engine/view/downcastwriter~DowncastWriter#createRawElement `downcastWriter#createRawElement()`} method.
 *
 * @extends module:engine/view/element~Element
 */
class RawElement extends Element {
    /**
     * Creates a new instance of a raw element.
     *
     * Throws the `view-rawelement-cannot-add` {@link module:utils/ckeditorerror~CKEditorError CKEditorError} when the `children`
     * parameter is passed to inform that the usage of `RawElement` is incorrect (adding child nodes to `RawElement` is forbidden).
     *
     * @see module:engine/view/downcastwriter~DowncastWriter#createRawElement
     * @protected
     * @param {module:engine/view/document~Document} document The document instance to which this element belongs.
     * @param {String} name A node name.
     * @param {Object|Iterable} [attrs] The collection of attributes.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into the created element.
     */
    constructor(...args) {
        super(...args);
        /**
         * Returns `null` because filler is not needed for raw elements.
         *
         * @method #getFillerOffset
         * @returns {null} Always returns null.
         */
        this.getFillerOffset = rawelement_getFillerOffset;
    }
    /**
     * Overrides the {@link module:engine/view/element~Element#_insertChild} method.
     * Throws the `view-rawelement-cannot-add` {@link module:utils/ckeditorerror~CKEditorError CKEditorError} to prevent
     * adding any child nodes to a raw element.
     *
     * @protected
     */
    _insertChild(index, items) {
        if (items && (items instanceof node_Node || Array.from(items).length > 0)) {
            /**
             * Cannot add children to a {@link module:engine/view/rawelement~RawElement} instance.
             *
             * @error view-rawelement-cannot-add
             */
            throw new CKEditorError('view-rawelement-cannot-add', [this, items]);
        }
        return 0;
    }
    render() { }
}
/**
 * Checks whether this object is of the given type or name.
 *
 *		rawElement.is( 'rawElement' ); // -> true
 *		rawElement.is( 'element' ); // -> true
 *		rawElement.is( 'node' ); // -> true
 *		rawElement.is( 'view:rawElement' ); // -> true
 *		rawElement.is( 'view:element' ); // -> true
 *		rawElement.is( 'view:node' ); // -> true
 *
 *		rawElement.is( 'model:element' ); // -> false
 *		rawElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is a raw element, you can also check its
 * {@link module:engine/view/rawelement~RawElement#name name}:
 *
 *		rawElement.is( 'img' ); // -> true if this is an img element
 *		rawElement.is( 'rawElement', 'img' ); // -> same as above
 *		text.is( 'img' ); -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type The type to check when the `name` parameter is present.
 * Otherwise, it acts like the `name` parameter.
 * @param {String} [name] The element name.
 * @returns {Boolean}
 */
RawElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'rawElement' || type === 'view:rawElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === this.name || type === 'view:' + this.name ||
            type === 'element' || type === 'view:element' ||
            type === 'node' || type === 'view:node';
    }
    else {
        return name === this.name && (type === 'rawElement' || type === 'view:rawElement' ||
            type === 'element' || type === 'view:element');
    }
};
// Returns `null` because block filler is not needed for raw elements.
//
// @returns {null}
function rawelement_getFillerOffset() {
    return null;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/documentfragment.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/view/documentfragment
 */





/**
 * Document fragment.
 *
 * To create a new document fragment instance use the
 * {@link module:engine/view/upcastwriter~UpcastWriter#createDocumentFragment `UpcastWriter#createDocumentFragment()`}
 * method.
 */
class DocumentFragment extends EmitterMixin(TypeCheckable) {
    /**
     * Creates new DocumentFragment instance.
     *
     * @protected
     * @param {module:engine/view/document~Document} document The document to which this document fragment belongs.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into the created document fragment.
     */
    constructor(document, children) {
        super();
        /**
         * The document to which this document fragment belongs.
         *
         * @readonly
         * @member {module:engine/view/document~Document}
         */
        this.document = document;
        /**
         * Array of child nodes.
         *
         * @protected
         * @member {Array.<module:engine/view/node~Node>} module:engine/view/documentfragment~DocumentFragment#_children
         */
        this._children = [];
        if (children) {
            this._insertChild(0, children);
        }
    }
    /**
     * Iterable interface.
     *
     * Iterates over nodes added to this document fragment.
     *
     * @returns {Iterable.<module:engine/view/node~Node>}
     */
    [Symbol.iterator]() {
        return this._children[Symbol.iterator]();
    }
    /**
     * Number of child nodes in this document fragment.
     *
     * @readonly
     * @type {Number}
     */
    get childCount() {
        return this._children.length;
    }
    /**
     * Is `true` if there are no nodes inside this document fragment, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isEmpty() {
        return this.childCount === 0;
    }
    /**
     * Artificial root of `DocumentFragment`. Returns itself. Added for compatibility reasons.
     *
     * @readonly
     * @type {module:engine/model/documentfragment~DocumentFragment}
     */
    get root() {
        return this;
    }
    /**
     * Artificial parent of `DocumentFragment`. Returns `null`. Added for compatibility reasons.
     *
     * @readonly
     * @type {null}
     */
    get parent() {
        return null;
    }
    /**
     * {@link module:engine/view/documentfragment~DocumentFragment#_insertChild Insert} a child node or a list of child nodes at the end
     * and sets the parent of these nodes to this fragment.
     *
     * @param {module:engine/view/item~Item|Iterable.<module:engine/view/item~Item>} items Items to be inserted.
     * @returns {Number} Number of appended nodes.
     */
    _appendChild(items) {
        return this._insertChild(this.childCount, items);
    }
    /**
     * Gets child at the given index.
     *
     * @param {Number} index Index of child.
     * @returns {module:engine/view/node~Node} Child node.
     */
    getChild(index) {
        return this._children[index];
    }
    /**
     * Gets index of the given child node. Returns `-1` if child node is not found.
     *
     * @param {module:engine/view/node~Node} node Child node.
     * @returns {Number} Index of the child node.
     */
    getChildIndex(node) {
        return this._children.indexOf(node);
    }
    /**
     * Gets child nodes iterator.
     *
     * @returns {Iterable.<module:engine/view/node~Node>} Child nodes iterator.
     */
    getChildren() {
        return this._children[Symbol.iterator]();
    }
    /**
     * Inserts a child node or a list of child nodes on the given index and sets the parent of these nodes to
     * this fragment.
     *
     * @param {Number} index Position where nodes should be inserted.
     * @param {module:engine/view/item~Item|Iterable.<module:engine/view/item~Item>} items Items to be inserted.
     * @returns {Number} Number of inserted nodes.
     */
    _insertChild(index, items) {
        this._fireChange('children', this);
        let count = 0;
        const nodes = documentfragment_normalize(this.document, items);
        for (const node of nodes) {
            // If node that is being added to this element is already inside another element, first remove it from the old parent.
            if (node.parent !== null) {
                node._remove();
            }
            node.parent = this;
            this._children.splice(index, 0, node);
            index++;
            count++;
        }
        return count;
    }
    /**
     * Removes number of child nodes starting at the given index and set the parent of these nodes to `null`.
     *
     * @internal
     * @param {Number} index Number of the first node to remove.
     * @param {Number} [howMany=1] Number of nodes to remove.
     * @returns {Array.<module:engine/view/node~Node>} The array of removed nodes.
     */
    _removeChildren(index, howMany = 1) {
        this._fireChange('children', this);
        for (let i = index; i < index + howMany; i++) {
            this._children[i].parent = null;
        }
        return this._children.splice(index, howMany);
    }
    /**
     * Fires `change` event with given type of the change.
     *
     * @private
     * @param {module:engine/view/document~ChangeType} type Type of the change.
     * @param {module:engine/view/node~Node} node Changed node.
     * @fires module:engine/view/node~Node#change
     */
    _fireChange(type, node) {
        this.fire('change:' + type, node);
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		docFrag.is( 'documentFragment' ); // -> true
 *		docFrag.is( 'view:documentFragment' ); // -> true
 *
 *		docFrag.is( 'model:documentFragment' ); // -> false
 *		docFrag.is( 'element' ); // -> false
 *		docFrag.is( 'node' ); // -> false
 *
 * {@link module:engine/view/node~Node#is Check the entire list of view objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
DocumentFragment.prototype.is = function (type) {
    return type === 'documentFragment' || type === 'view:documentFragment';
};
// Converts strings to Text and non-iterables to arrays.
//
// @param {String|module:engine/view/item~Item|Iterable.<String|module:engine/view/item~Item>}
// @returns {Iterable.<module:engine/view/node~Node>}
function documentfragment_normalize(document, nodes) {
    // Separate condition because string is iterable.
    if (typeof nodes == 'string') {
        return [new text_Text(document, nodes)];
    }
    if (!isIterable(nodes)) {
        nodes = [nodes];
    }
    // Array.from to enable .map() on non-arrays.
    return Array.from(nodes)
        .map(node => {
        if (typeof node == 'string') {
            return new text_Text(document, node);
        }
        if (node instanceof TextProxy) {
            return new text_Text(document, node.data);
        }
        return node;
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/downcastwriter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module module:engine/view/downcastwriter
 */














/**
 * View downcast writer.
 *
 * It provides a set of methods used to manipulate view nodes.
 *
 * Do not create an instance of this writer manually. To modify a view structure, use
 * the {@link module:engine/view/view~View#change `View#change()`} block.
 *
 * The `DowncastWriter` is designed to work with semantic views which are the views that were/are being downcasted from the model.
 * To work with ordinary views (e.g. parsed from a pasted content) use the
 * {@link module:engine/view/upcastwriter~UpcastWriter upcast writer}.
 *
 * Read more about changing the view in the {@glink framework/guides/architecture/editing-engine#changing-the-view Changing the view}
 * section of the {@glink framework/guides/architecture/editing-engine Editing engine architecture} guide.
 */
class DowncastWriter {
    /**
     * @param {module:engine/view/document~Document} document The view document instance.
     */
    constructor(document) {
        /**
         * The view document instance in which this writer operates.
         *
         * @readonly
         * @type {module:engine/view/document~Document}
         */
        this.document = document;
        /**
         * Holds references to the attribute groups that share the same {@link module:engine/view/attributeelement~AttributeElement#id id}.
         * The keys are `id`s, the values are `Set`s holding {@link module:engine/view/attributeelement~AttributeElement}s.
         *
         * @private
         * @type {Map.<String,Set>}
         */
        this._cloneGroups = new Map();
        /**
         * The slot factory used by the `elementToStructure` downcast helper.
         *
         * @private
         * @type {Function|null}
         */
        this._slotFactory = null;
    }
    /**
     * Sets {@link module:engine/view/documentselection~DocumentSelection selection's} ranges and direction to the
     * specified location based on the given {@link module:engine/view/selection~Selectable selectable}.
     *
     * Usage:
     *
     *		// Sets selection to the given range.
     *		const range = writer.createRange( start, end );
     *		writer.setSelection( range );
     *
     *		// Sets backward selection to the given range.
     *		const range = writer.createRange( start, end );
     *		writer.setSelection( range );
     *
     *		// Sets selection to given ranges.
     * 		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( start2, end2 ) ];
     *		writer.setSelection( range );
     *
     *		// Sets selection to the other selection.
     *		const otherSelection = writer.createSelection();
     *		writer.setSelection( otherSelection );
     *
     * 		// Sets collapsed selection at the given position.
     *		const position = writer.createPositionFromPath( root, path );
     *		writer.setSelection( position );
     *
     * 		// Sets collapsed selection at the position of given item and offset.
     *		const paragraph = writer.createContainerElement( 'p' );
     *		writer.setSelection( paragraph, offset );
     *
     * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     * 		writer.setSelection( paragraph, 'in' );
     *
     * Creates a range on the {@link module:engine/view/item~Item item} which starts before the item and ends just after the item.
     *
     *		writer.setSelection( paragraph, 'on' );
     *
     * 		// Removes all ranges.
     *		writer.setSelection( null );
     *
     * `DowncastWriter#setSelection()` allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Sets selection as backward.
     *		writer.setSelection( range, { backward: true } );
     *
     *		// Sets selection as fake.
     *		// Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * 		// This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * 		// represented in other way, for example by applying proper CSS class.
     *		writer.setSelection( range, { fake: true } );
     *
     * 		// Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
     * 		// (and be  properly handled by screen readers).
     *		writer.setSelection( range, { fake: true, label: 'foo' } );
     *
     * @param {module:engine/view/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     */
    setSelection(...args) {
        this.document.selection._setTo(...args);
    }
    /**
     * Moves {@link module:engine/view/documentselection~DocumentSelection#focus selection's focus} to the specified location.
     *
     * The location can be specified in the same form as {@link module:engine/view/view~View#createPositionAt view.createPositionAt()}
     * parameters.
     *
     * @param {module:engine/view/item~Item|module:engine/view/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/view/item~Item view item}.
     */
    setSelectionFocus(...args) {
        this.document.selection._setFocus(...args);
    }
    /**
     * Creates a new {@link module:engine/view/documentfragment~DocumentFragment} instance.
     *
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into the created document fragment.
     * @returns {module:engine/view/documentfragment~DocumentFragment} The created document fragment.
     */
    createDocumentFragment(children) {
        return new DocumentFragment(this.document, children);
    }
    /**
     * Creates a new {@link module:engine/view/text~Text text node}.
     *
     *		writer.createText( 'foo' );
     *
     * @param {String} data The text's data.
     * @returns {module:engine/view/text~Text} The created text node.
     */
    createText(data) {
        return new text_Text(this.document, data);
    }
    /**
     * Creates a new {@link module:engine/view/attributeelement~AttributeElement}.
     *
     *		writer.createAttributeElement( 'strong' );
     *		writer.createAttributeElement( 'a', { href: 'foo.bar' } );
     *
     *		// Make `<a>` element contain other attributes element so the `<a>` element is not broken.
     *		writer.createAttributeElement( 'a', { href: 'foo.bar' }, { priority: 5 } );
     *
     *		// Set `id` of a marker element so it is not joined or merged with "normal" elements.
     *		writer.createAttributeElement( 'span', { class: 'my-marker' }, { id: 'marker:my' } );
     *
     * @param {String} name Name of the element.
     * @param {Object} [attributes] Element's attributes.
     * @param {Object} [options] Element's options.
     * @param {Number} [options.priority] Element's {@link module:engine/view/attributeelement~AttributeElement#priority priority}.
     * @param {Number|String} [options.id] Element's {@link module:engine/view/attributeelement~AttributeElement#id id}.
     * @param {Array.<String>} [options.renderUnsafeAttributes] A list of attribute names that should be rendered in the editing
     * pipeline even though they would normally be filtered out by unsafe attribute detection mechanisms.
     * @returns {module:engine/view/attributeelement~AttributeElement} Created element.
     */
    createAttributeElement(name, attributes, options = {}) {
        const attributeElement = new AttributeElement(this.document, name, attributes);
        if (typeof options.priority === 'number') {
            attributeElement._priority = options.priority;
        }
        if (options.id) {
            attributeElement._id = options.id;
        }
        if (options.renderUnsafeAttributes) {
            attributeElement._unsafeAttributesToRender.push(...options.renderUnsafeAttributes);
        }
        return attributeElement;
    }
    createContainerElement(name, attributes, childrenOrOptions = {}, options = {}) {
        let children = null;
        if (lodash_es_isPlainObject(childrenOrOptions)) {
            options = childrenOrOptions;
        }
        else {
            children = childrenOrOptions;
        }
        const containerElement = new ContainerElement(this.document, name, attributes, children);
        if (options.renderUnsafeAttributes) {
            containerElement._unsafeAttributesToRender.push(...options.renderUnsafeAttributes);
        }
        return containerElement;
    }
    /**
     * Creates a new {@link module:engine/view/editableelement~EditableElement}.
     *
     *		writer.createEditableElement( 'div' );
     *		writer.createEditableElement( 'div', { id: 'foo-1234' } );
     *
     * Note: The editable element is to be used in the editing pipeline. Usually, together with
     * {@link module:widget/utils~toWidgetEditable `toWidgetEditable()`}.
     *
     * @param {String} name Name of the element.
     * @param {Object} [attributes] Elements attributes.
     * @param {Object} [options] Element's options.
     * @param {Array.<String>} [options.renderUnsafeAttributes] A list of attribute names that should be rendered in the editing
     * pipeline even though they would normally be filtered out by unsafe attribute detection mechanisms.
     * @returns {module:engine/view/editableelement~EditableElement} Created element.
     */
    createEditableElement(name, attributes, options = {}) {
        const editableElement = new EditableElement(this.document, name, attributes);
        if (options.renderUnsafeAttributes) {
            editableElement._unsafeAttributesToRender.push(...options.renderUnsafeAttributes);
        }
        return editableElement;
    }
    /**
     * Creates a new {@link module:engine/view/emptyelement~EmptyElement}.
     *
     *		writer.createEmptyElement( 'img' );
     *		writer.createEmptyElement( 'img', { id: 'foo-1234' } );
     *
     * @param {String} name Name of the element.
     * @param {Object} [attributes] Elements attributes.
     * @param {Object} [options] Element's options.
     * @param {Array.<String>} [options.renderUnsafeAttributes] A list of attribute names that should be rendered in the editing
     * pipeline even though they would normally be filtered out by unsafe attribute detection mechanisms.
     * @returns {module:engine/view/emptyelement~EmptyElement} Created element.
     */
    createEmptyElement(name, attributes, options = {}) {
        const emptyElement = new EmptyElement(this.document, name, attributes);
        if (options.renderUnsafeAttributes) {
            emptyElement._unsafeAttributesToRender.push(...options.renderUnsafeAttributes);
        }
        return emptyElement;
    }
    /**
     * Creates a new {@link module:engine/view/uielement~UIElement}.
     *
     *		writer.createUIElement( 'span' );
     *		writer.createUIElement( 'span', { id: 'foo-1234' } );
     *
     * A custom render function can be provided as the third parameter:
     *
     *		writer.createUIElement( 'span', null, function( domDocument ) {
     *			const domElement = this.toDomElement( domDocument );
     *			domElement.innerHTML = '<b>this is ui element</b>';
     *
     *			return domElement;
     *		} );
     *
     * Unlike {@link #createRawElement raw elements}, UI elements are by no means editor content, for instance,
     * they are ignored by the editor selection system.
     *
     * You should not use UI elements as data containers. Check out {@link #createRawElement} instead.
     *
     * @param {String} name The name of the element.
     * @param {Object} [attributes] Element attributes.
     * @param {Function} [renderFunction] A custom render function.
     * @returns {module:engine/view/uielement~UIElement} The created element.
     */
    createUIElement(name, attributes, renderFunction) {
        const uiElement = new UIElement(this.document, name, attributes);
        if (renderFunction) {
            uiElement.render = renderFunction;
        }
        return uiElement;
    }
    /**
     * Creates a new {@link module:engine/view/rawelement~RawElement}.
     *
     *		writer.createRawElement( 'span', { id: 'foo-1234' }, function( domElement ) {
     *			domElement.innerHTML = '<b>This is the raw content of the raw element.</b>';
     *		} );
     *
     * Raw elements work as data containers ("wrappers", "sandboxes") but their children are not managed or
     * even recognized by the editor. This encapsulation allows integrations to maintain custom DOM structures
     * in the editor content without, for instance, worrying about compatibility with other editor features.
     * Raw elements are a perfect tool for integration with external frameworks and data sources.
     *
     * Unlike {@link #createUIElement UI elements}, raw elements act like "real" editor content (similar to
     * {@link module:engine/view/containerelement~ContainerElement} or {@link module:engine/view/emptyelement~EmptyElement}),
     * and they are considered by the editor selection.
     *
     * You should not use raw elements to render the UI in the editor content. Check out {@link #createUIElement `#createUIElement()`}
     * instead.
     *
     * @param {String} name The name of the element.
     * @param {Object} [attributes] Element attributes.
     * @param {Function} [renderFunction] A custom render function.
     * @param {Object} [options] Element's options.
     * @param {Array.<String>} [options.renderUnsafeAttributes] A list of attribute names that should be rendered in the editing
     * pipeline even though they would normally be filtered out by unsafe attribute detection mechanisms.
     * @returns {module:engine/view/rawelement~RawElement} The created element.
     */
    createRawElement(name, attributes, renderFunction, options = {}) {
        const rawElement = new RawElement(this.document, name, attributes);
        if (renderFunction) {
            rawElement.render = renderFunction;
        }
        if (options.renderUnsafeAttributes) {
            rawElement._unsafeAttributesToRender.push(...options.renderUnsafeAttributes);
        }
        return rawElement;
    }
    /**
     * Adds or overwrites the element's attribute with a specified key and value.
     *
     *		writer.setAttribute( 'href', 'http://ckeditor.com', linkElement );
     *
     * @param {String} key The attribute key.
     * @param {String} value The attribute value.
     * @param {module:engine/view/element~Element} element
     */
    setAttribute(key, value, element) {
        element._setAttribute(key, value);
    }
    /**
     * Removes attribute from the element.
     *
     *		writer.removeAttribute( 'href', linkElement );
     *
     * @param {String} key Attribute key.
     * @param {module:engine/view/element~Element} element
     */
    removeAttribute(key, element) {
        element._removeAttribute(key);
    }
    /**
     * Adds specified class to the element.
     *
     *		writer.addClass( 'foo', linkElement );
     *		writer.addClass( [ 'foo', 'bar' ], linkElement );
     *
     * @param {Array.<String>|String} className
     * @param {module:engine/view/element~Element} element
     */
    addClass(className, element) {
        element._addClass(className);
    }
    /**
     * Removes specified class from the element.
     *
     *		writer.removeClass( 'foo', linkElement );
     *		writer.removeClass( [ 'foo', 'bar' ], linkElement );
     *
     * @param {Array.<String>|String} className
     * @param {module:engine/view/element~Element} element
     */
    removeClass(className, element) {
        element._removeClass(className);
    }
    setStyle(property, value, element) {
        if (lodash_es_isPlainObject(property) && element === undefined) {
            value._setStyle(property);
        }
        else {
            element._setStyle(property, value);
        }
    }
    /**
     * Removes specified style from the element.
     *
     *		writer.removeStyle( 'color', element ); // Removes 'color' style.
     *		writer.removeStyle( [ 'color', 'border-top' ], element ); // Removes both 'color' and 'border-top' styles.
     *
     * **Note**: This method can work with normalized style names if
     * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
     * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
     *
     * @param {Array.<String>|String} property
     * @param {module:engine/view/element~Element} element
     */
    removeStyle(property, element) {
        element._removeStyle(property);
    }
    /**
     * Sets a custom property on element. Unlike attributes, custom properties are not rendered to the DOM,
     * so they can be used to add special data to elements.
     *
     * @param {String|Symbol} key
     * @param {*} value
     * @param {module:engine/view/element~Element} element
     */
    setCustomProperty(key, value, element) {
        element._setCustomProperty(key, value);
    }
    /**
     * Removes a custom property stored under the given key.
     *
     * @param {String|Symbol} key
     * @param {module:engine/view/element~Element} element
     * @returns {Boolean} Returns true if property was removed.
     */
    removeCustomProperty(key, element) {
        return element._removeCustomProperty(key);
    }
    /**
     * Breaks attribute elements at the provided position or at the boundaries of a provided range. It breaks attribute elements
     * up to their first ancestor that is a container element.
     *
     * In following examples `<p>` is a container, `<b>` and `<u>` are attribute elements:
     *
     *		<p>foo<b><u>bar{}</u></b></p> -> <p>foo<b><u>bar</u></b>[]</p>
     *		<p>foo<b><u>{}bar</u></b></p> -> <p>foo{}<b><u>bar</u></b></p>
     *		<p>foo<b><u>b{}ar</u></b></p> -> <p>foo<b><u>b</u></b>[]<b><u>ar</u></b></p>
     *		<p><b>fo{o</b><u>ba}r</u></p> -> <p><b>fo</b><b>o</b><u>ba</u><u>r</u></b></p>
     *
     * **Note:** {@link module:engine/view/documentfragment~DocumentFragment DocumentFragment} is treated like a container.
     *
     * **Note:** The difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes()} and
     * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer()} is that `breakAttributes()` breaks all
     * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of a given `position`,
     * up to the first encountered {@link module:engine/view/containerelement~ContainerElement container element}.
     * `breakContainer()` assumes that a given `position` is directly in the container element and breaks that container element.
     *
     * Throws the `view-writer-invalid-range-container` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
     * when the {@link module:engine/view/range~Range#start start}
     * and {@link module:engine/view/range~Range#end end} positions of a passed range are not placed inside same parent container.
     *
     * Throws the `view-writer-cannot-break-empty-element` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
     * when trying to break attributes inside an {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
     *
     * Throws the `view-writer-cannot-break-ui-element` {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
     * when trying to break attributes inside a {@link module:engine/view/uielement~UIElement UIElement}.
     *
     * @see module:engine/view/attributeelement~AttributeElement
     * @see module:engine/view/containerelement~ContainerElement
     * @see module:engine/view/downcastwriter~DowncastWriter#breakContainer
     * @param {module:engine/view/position~Position|module:engine/view/range~Range} positionOrRange The position where
     * to break attribute elements.
     * @returns {module:engine/view/position~Position|module:engine/view/range~Range} The new position or range, after breaking the
     * attribute elements.
     */
    breakAttributes(positionOrRange) {
        if (positionOrRange instanceof Position) {
            return this._breakAttributes(positionOrRange);
        }
        else {
            return this._breakAttributesRange(positionOrRange);
        }
    }
    /**
     * Breaks a {@link module:engine/view/containerelement~ContainerElement container view element} into two, at the given position.
     * The position has to be directly inside the container element and cannot be in the root. It does not break the conrainer view element
     * if the position is at the beginning or at the end of its parent element.
     *
     *		<p>foo^bar</p> -> <p>foo</p><p>bar</p>
     *		<div><p>foo</p>^<p>bar</p></div> -> <div><p>foo</p></div><div><p>bar</p></div>
     *		<p>^foobar</p> -> ^<p>foobar</p>
     *		<p>foobar^</p> -> <p>foobar</p>^
     *
     * **Note:** The difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes()} and
     * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer()} is that `breakAttributes()` breaks all
     * {@link module:engine/view/attributeelement~AttributeElement attribute elements} that are ancestors of a given `position`,
     * up to the first encountered {@link module:engine/view/containerelement~ContainerElement container element}.
     * `breakContainer()` assumes that the given `position` is directly in the container element and breaks that container element.
     *
     * @see module:engine/view/attributeelement~AttributeElement
     * @see module:engine/view/containerelement~ContainerElement
     * @see module:engine/view/downcastwriter~DowncastWriter#breakAttributes
     * @param {module:engine/view/position~Position} position The position where to break the element.
     * @returns {module:engine/view/position~Position} The position between broken elements. If an element has not been broken,
     * the returned position is placed either before or after it.
     */
    breakContainer(position) {
        const element = position.parent;
        if (!(element.is('containerElement'))) {
            /**
             * Trying to break an element which is not a container element.
             *
             * @error view-writer-break-non-container-element
             */
            throw new CKEditorError('view-writer-break-non-container-element', this.document);
        }
        if (!element.parent) {
            /**
             * Trying to break root element.
             *
             * @error view-writer-break-root
             */
            throw new CKEditorError('view-writer-break-root', this.document);
        }
        if (position.isAtStart) {
            return Position._createBefore(element);
        }
        else if (!position.isAtEnd) {
            const newElement = element._clone(false);
            this.insert(Position._createAfter(element), newElement);
            const sourceRange = new Range(position, Position._createAt(element, 'end'));
            const targetPosition = new Position(newElement, 0);
            this.move(sourceRange, targetPosition);
        }
        return Position._createAfter(element);
    }
    /**
     * Merges {@link module:engine/view/attributeelement~AttributeElement attribute elements}. It also merges text nodes if needed.
     * Only {@link module:engine/view/attributeelement~AttributeElement#isSimilar similar} attribute elements can be merged.
     *
     * In following examples `<p>` is a container and `<b>` is an attribute element:
     *
     *		<p>foo[]bar</p> -> <p>foo{}bar</p>
     *		<p><b>foo</b>[]<b>bar</b></p> -> <p><b>foo{}bar</b></p>
     *		<p><b foo="bar">a</b>[]<b foo="baz">b</b></p> -> <p><b foo="bar">a</b>[]<b foo="baz">b</b></p>
     *
     * It will also take care about empty attributes when merging:
     *
     *		<p><b>[]</b></p> -> <p>[]</p>
     *		<p><b>foo</b><i>[]</i><b>bar</b></p> -> <p><b>foo{}bar</b></p>
     *
     * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#mergeAttributes mergeAttributes} and
     * {@link module:engine/view/downcastwriter~DowncastWriter#mergeContainers mergeContainers} is that `mergeAttributes` merges two
     * {@link module:engine/view/attributeelement~AttributeElement attribute elements} or {@link module:engine/view/text~Text text nodes}
     * while `mergeContainer` merges two {@link module:engine/view/containerelement~ContainerElement container elements}.
     *
     * @see module:engine/view/attributeelement~AttributeElement
     * @see module:engine/view/containerelement~ContainerElement
     * @see module:engine/view/downcastwriter~DowncastWriter#mergeContainers
     * @param {module:engine/view/position~Position} position Merge position.
     * @returns {module:engine/view/position~Position} Position after merge.
     */
    mergeAttributes(position) {
        const positionOffset = position.offset;
        const positionParent = position.parent;
        // When inside text node - nothing to merge.
        if (positionParent.is('$text')) {
            return position;
        }
        // When inside empty attribute - remove it.
        if (positionParent.is('attributeElement') && positionParent.childCount === 0) {
            const parent = positionParent.parent;
            const offset = positionParent.index;
            positionParent._remove();
            this._removeFromClonedElementsGroup(positionParent);
            return this.mergeAttributes(new Position(parent, offset));
        }
        const nodeBefore = positionParent.getChild(positionOffset - 1);
        const nodeAfter = positionParent.getChild(positionOffset);
        // Position should be placed between two nodes.
        if (!nodeBefore || !nodeAfter) {
            return position;
        }
        // When position is between two text nodes.
        if (nodeBefore.is('$text') && nodeAfter.is('$text')) {
            return mergeTextNodes(nodeBefore, nodeAfter);
        }
        // When position is between two same attribute elements.
        else if (nodeBefore.is('attributeElement') && nodeAfter.is('attributeElement') && nodeBefore.isSimilar(nodeAfter)) {
            // Move all children nodes from node placed after selection and remove that node.
            const count = nodeBefore.childCount;
            nodeBefore._appendChild(nodeAfter.getChildren());
            nodeAfter._remove();
            this._removeFromClonedElementsGroup(nodeAfter);
            // New position is located inside the first node, before new nodes.
            // Call this method recursively to merge again if needed.
            return this.mergeAttributes(new Position(nodeBefore, count));
        }
        return position;
    }
    /**
     * Merges two {@link module:engine/view/containerelement~ContainerElement container elements} that are before and after given position.
     * Precisely, the element after the position is removed and it's contents are moved to element before the position.
     *
     *		<p>foo</p>^<p>bar</p> -> <p>foo^bar</p>
     *		<div>foo</div>^<p>bar</p> -> <div>foo^bar</div>
     *
     * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#mergeAttributes mergeAttributes} and
     * {@link module:engine/view/downcastwriter~DowncastWriter#mergeContainers mergeContainers} is that `mergeAttributes` merges two
     * {@link module:engine/view/attributeelement~AttributeElement attribute elements} or {@link module:engine/view/text~Text text nodes}
     * while `mergeContainer` merges two {@link module:engine/view/containerelement~ContainerElement container elements}.
     *
     * @see module:engine/view/attributeelement~AttributeElement
     * @see module:engine/view/containerelement~ContainerElement
     * @see module:engine/view/downcastwriter~DowncastWriter#mergeAttributes
     * @param {module:engine/view/position~Position} position Merge position.
     * @returns {module:engine/view/position~Position} Position after merge.
     */
    mergeContainers(position) {
        const prev = position.nodeBefore;
        const next = position.nodeAfter;
        if (!prev || !next || !prev.is('containerElement') || !next.is('containerElement')) {
            /**
             * Element before and after given position cannot be merged.
             *
             * @error view-writer-merge-containers-invalid-position
             */
            throw new CKEditorError('view-writer-merge-containers-invalid-position', this.document);
        }
        const lastChild = prev.getChild(prev.childCount - 1);
        const newPosition = lastChild instanceof text_Text ? Position._createAt(lastChild, 'end') : Position._createAt(prev, 'end');
        this.move(Range._createIn(next), Position._createAt(prev, 'end'));
        this.remove(Range._createOn(next));
        return newPosition;
    }
    /**
     * Inserts a node or nodes at specified position. Takes care about breaking attributes before insertion
     * and merging them afterwards.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-insert-invalid-node` when nodes to insert
     * contains instances that are not {@link module:engine/view/text~Text Texts},
     * {@link module:engine/view/attributeelement~AttributeElement AttributeElements},
     * {@link module:engine/view/containerelement~ContainerElement ContainerElements},
     * {@link module:engine/view/emptyelement~EmptyElement EmptyElements},
     * {@link module:engine/view/rawelement~RawElement RawElements} or
     * {@link module:engine/view/uielement~UIElement UIElements}.
     *
     * @param {module:engine/view/position~Position} position Insertion position.
     * @param {module:engine/view/text~Text|module:engine/view/attributeelement~AttributeElement|
     * module:engine/view/containerelement~ContainerElement|module:engine/view/emptyelement~EmptyElement|
     * module:engine/view/rawelement~RawElement|module:engine/view/uielement~UIElement|
     * Iterable.<module:engine/view/text~Text|
     * module:engine/view/attributeelement~AttributeElement|module:engine/view/containerelement~ContainerElement|
     * module:engine/view/emptyelement~EmptyElement|module:engine/view/rawelement~RawElement|
     * module:engine/view/uielement~UIElement>} nodes Node or nodes to insert.
     * @returns {module:engine/view/range~Range} Range around inserted nodes.
     */
    insert(position, nodes) {
        nodes = isIterable(nodes) ? [...nodes] : [nodes];
        // Check if nodes to insert are instances of AttributeElements, ContainerElements, EmptyElements, UIElements or Text.
        validateNodesToInsert(nodes, this.document);
        // Group nodes in batches of nodes that require or do not require breaking an AttributeElements.
        const nodeGroups = nodes.reduce((groups, node) => {
            const lastGroup = groups[groups.length - 1];
            // Break attributes on nodes that do exist in the model tree so they can have attributes, other elements
            // can't have an attribute in model and won't get wrapped with an AttributeElement while down-casted.
            const breakAttributes = !node.is('uiElement');
            if (!lastGroup || lastGroup.breakAttributes != breakAttributes) {
                groups.push({
                    breakAttributes,
                    nodes: [node]
                });
            }
            else {
                lastGroup.nodes.push(node);
            }
            return groups;
        }, []);
        // Insert nodes in batches.
        let start = null;
        let end = position;
        for (const { nodes, breakAttributes } of nodeGroups) {
            const range = this._insertNodes(end, nodes, breakAttributes);
            if (!start) {
                start = range.start;
            }
            end = range.end;
        }
        // When no nodes were inserted - return collapsed range.
        if (!start) {
            return new Range(position);
        }
        return new Range(start, end);
    }
    /**
     * Removes provided range from the container.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when
     * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside
     * same parent container.
     *
     * @param {module:engine/view/range~Range|module:engine/view/item~Item} rangeOrItem Range to remove from container
     * or an {@link module:engine/view/item~Item item} to remove. If range is provided, after removing, it will be updated
     * to a collapsed range showing the new position.
     * @returns {module:engine/view/documentfragment~DocumentFragment} Document fragment containing removed nodes.
     */
    remove(rangeOrItem) {
        const range = rangeOrItem instanceof Range ? rangeOrItem : Range._createOn(rangeOrItem);
        validateRangeContainer(range, this.document);
        // If range is collapsed - nothing to remove.
        if (range.isCollapsed) {
            return new DocumentFragment(this.document);
        }
        // Break attributes at range start and end.
        const { start: breakStart, end: breakEnd } = this._breakAttributesRange(range, true);
        const parentContainer = breakStart.parent;
        const count = breakEnd.offset - breakStart.offset;
        // Remove nodes in range.
        const removed = parentContainer._removeChildren(breakStart.offset, count);
        for (const node of removed) {
            this._removeFromClonedElementsGroup(node);
        }
        // Merge after removing.
        const mergePosition = this.mergeAttributes(breakStart);
        range.start = mergePosition;
        range.end = mergePosition.clone();
        // Return removed nodes.
        return new DocumentFragment(this.document, removed);
    }
    /**
     * Removes matching elements from given range.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when
     * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside
     * same parent container.
     *
     * @param {module:engine/view/range~Range} range Range to clear.
     * @param {module:engine/view/element~Element} element Element to remove.
     */
    clear(range, element) {
        validateRangeContainer(range, this.document);
        // Create walker on given range.
        // We walk backward because when we remove element during walk it modifies range end position.
        const walker = range.getWalker({
            direction: 'backward',
            ignoreElementEnd: true
        });
        // Let's walk.
        for (const current of walker) {
            const item = current.item;
            let rangeToRemove;
            // When current item matches to the given element.
            if (item.is('element') && element.isSimilar(item)) {
                // Create range on this element.
                rangeToRemove = Range._createOn(item);
                // When range starts inside Text or TextProxy element.
            }
            else if (!current.nextPosition.isAfter(range.start) && item.is('$textProxy')) {
                // We need to check if parent of this text matches to given element.
                const parentElement = item.getAncestors().find(ancestor => {
                    return ancestor.is('element') && element.isSimilar(ancestor);
                });
                // If it is then create range inside this element.
                if (parentElement) {
                    rangeToRemove = Range._createIn(parentElement);
                }
            }
            // If we have found element to remove.
            if (rangeToRemove) {
                // We need to check if element range stick out of the given range and truncate if it is.
                if (rangeToRemove.end.isAfter(range.end)) {
                    rangeToRemove.end = range.end;
                }
                if (rangeToRemove.start.isBefore(range.start)) {
                    rangeToRemove.start = range.start;
                }
                // At the end we remove range with found element.
                this.remove(rangeToRemove);
            }
        }
    }
    /**
     * Moves nodes from provided range to target position.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when
     * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside
     * same parent container.
     *
     * @param {module:engine/view/range~Range} sourceRange Range containing nodes to move.
     * @param {module:engine/view/position~Position} targetPosition Position to insert.
     * @returns {module:engine/view/range~Range} Range in target container. Inserted nodes are placed between
     * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions.
     */
    move(sourceRange, targetPosition) {
        let nodes;
        if (targetPosition.isAfter(sourceRange.end)) {
            targetPosition = this._breakAttributes(targetPosition, true);
            const parent = targetPosition.parent;
            const countBefore = parent.childCount;
            sourceRange = this._breakAttributesRange(sourceRange, true);
            nodes = this.remove(sourceRange);
            targetPosition.offset += (parent.childCount - countBefore);
        }
        else {
            nodes = this.remove(sourceRange);
        }
        return this.insert(targetPosition, nodes);
    }
    /**
     * Wraps elements within range with provided {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.
     * If a collapsed range is provided, it will be wrapped only if it is equal to view selection.
     *
     * If a collapsed range was passed and is same as selection, the selection
     * will be moved to the inside of the wrapped attribute element.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-invalid-range-container`
     * when {@link module:engine/view/range~Range#start}
     * and {@link module:engine/view/range~Range#end} positions are not placed inside same parent container.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-invalid-attribute` when passed attribute element is not
     * an instance of {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-nonselection-collapsed-range` when passed range
     * is collapsed and different than view selection.
     *
     * @param {module:engine/view/range~Range} range Range to wrap.
     * @param {module:engine/view/attributeelement~AttributeElement} attribute Attribute element to use as wrapper.
     * @returns {module:engine/view/range~Range} range Range after wrapping, spanning over wrapping attribute element.
     */
    wrap(range, attribute) {
        if (!(attribute instanceof AttributeElement)) {
            throw new CKEditorError('view-writer-wrap-invalid-attribute', this.document);
        }
        validateRangeContainer(range, this.document);
        if (!range.isCollapsed) {
            // Non-collapsed range. Wrap it with the attribute element.
            return this._wrapRange(range, attribute);
        }
        else {
            // Collapsed range. Wrap position.
            let position = range.start;
            if (position.parent.is('element') && !_hasNonUiChildren(position.parent)) {
                position = position.getLastMatchingPosition(value => value.item.is('uiElement'));
            }
            position = this._wrapPosition(position, attribute);
            const viewSelection = this.document.selection;
            // If wrapping position is equal to view selection, move view selection inside wrapping attribute element.
            if (viewSelection.isCollapsed && viewSelection.getFirstPosition().isEqual(range.start)) {
                this.setSelection(position);
            }
            return new Range(position);
        }
    }
    /**
     * Unwraps nodes within provided range from attribute element.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when
     * {@link module:engine/view/range~Range#start start} and {@link module:engine/view/range~Range#end end} positions are not placed inside
     * same parent container.
     *
     * @param {module:engine/view/range~Range} range
     * @param {module:engine/view/attributeelement~AttributeElement} attribute
     */
    unwrap(range, attribute) {
        if (!(attribute instanceof AttributeElement)) {
            /**
             * The `attribute` passed to {@link module:engine/view/downcastwriter~DowncastWriter#unwrap `DowncastWriter#unwrap()`}
             * must be an instance of {@link module:engine/view/attributeelement~AttributeElement `AttributeElement`}.
             *
             * @error view-writer-unwrap-invalid-attribute
             */
            throw new CKEditorError('view-writer-unwrap-invalid-attribute', this.document);
        }
        validateRangeContainer(range, this.document);
        // If range is collapsed - nothing to unwrap.
        if (range.isCollapsed) {
            return range;
        }
        // Break attributes at range start and end.
        const { start: breakStart, end: breakEnd } = this._breakAttributesRange(range, true);
        const parentContainer = breakStart.parent;
        // Unwrap children located between break points.
        const newRange = this._unwrapChildren(parentContainer, breakStart.offset, breakEnd.offset, attribute);
        // Merge attributes at the both ends and return a new range.
        const start = this.mergeAttributes(newRange.start);
        // If start position was merged - move end position back.
        if (!start.isEqual(newRange.start)) {
            newRange.end.offset--;
        }
        const end = this.mergeAttributes(newRange.end);
        return new Range(start, end);
    }
    /**
     * Renames element by creating a copy of renamed element but with changed name and then moving contents of the
     * old element to the new one. Keep in mind that this will invalidate all {@link module:engine/view/position~Position positions} which
     * has renamed element as {@link module:engine/view/position~Position#parent a parent}.
     *
     * New element has to be created because `Element#tagName` property in DOM is readonly.
     *
     * Since this function creates a new element and removes the given one, the new element is returned to keep reference.
     *
     * @param {String} newName New name for element.
     * @param {module:engine/view/containerelement~ContainerElement} viewElement Element to be renamed.
     * @returns {module:engine/view/containerelement~ContainerElement} Element created due to rename.
     */
    rename(newName, viewElement) {
        const newElement = new ContainerElement(this.document, newName, viewElement.getAttributes());
        this.insert(Position._createAfter(viewElement), newElement);
        this.move(Range._createIn(viewElement), Position._createAt(newElement, 0));
        this.remove(Range._createOn(viewElement));
        return newElement;
    }
    /**
     * Cleans up memory by removing obsolete cloned elements group from the writer.
     *
     * Should be used whenever all {@link module:engine/view/attributeelement~AttributeElement attribute elements}
     * with the same {@link module:engine/view/attributeelement~AttributeElement#id id} are going to be removed from the view and
     * the group will no longer be needed.
     *
     * Cloned elements group are not removed automatically in case if the group is still needed after all its elements
     * were removed from the view.
     *
     * Keep in mind that group names are equal to the `id` property of the attribute element.
     *
     * @param {String} groupName Name of the group to clear.
     */
    clearClonedElementsGroup(groupName) {
        this._cloneGroups.delete(groupName);
    }
    /**
     * Creates position at the given location. The location can be specified as:
     *
     * * a {@link module:engine/view/position~Position position},
     * * parent element and offset (offset defaults to `0`),
     * * parent element and `'end'` (sets position at the end of that element),
     * * {@link module:engine/view/item~Item view item} and `'before'` or `'after'` (sets position before or after given view item).
     *
     * This method is a shortcut to other constructors such as:
     *
     * * {@link #createPositionBefore},
     * * {@link #createPositionAfter},
     *
     * @param {module:engine/view/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/view/item~Item view item}.
     * @returns {module:engine/view/position~Position}
     */
    createPositionAt(itemOrPosition, offset) {
        return Position._createAt(itemOrPosition, offset);
    }
    /**
     * Creates a new position after given view item.
     *
     * @param {module:engine/view/item~Item} item View item after which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    createPositionAfter(item) {
        return Position._createAfter(item);
    }
    /**
     * Creates a new position before given view item.
     *
     * @param {module:engine/view/item~Item} item View item before which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    createPositionBefore(item) {
        return Position._createBefore(item);
    }
    /**
     * Creates a range spanning from `start` position to `end` position.
     *
     * **Note:** This factory method creates its own {@link module:engine/view/position~Position} instances basing on passed values.
     *
     * @param {module:engine/view/position~Position} start Start position.
     * @param {module:engine/view/position~Position} [end] End position. If not set, range will be collapsed at `start` position.
     * @returns {module:engine/view/range~Range}
     */
    createRange(...args) {
        return new Range(...args);
    }
    /**
     * Creates a range that starts before given {@link module:engine/view/item~Item view item} and ends after it.
     *
     * @param {module:engine/view/item~Item} item
     * @returns {module:engine/view/range~Range}
     */
    createRangeOn(item) {
        return Range._createOn(item);
    }
    /**
     * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     * @param {module:engine/view/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/view/range~Range}
     */
    createRangeIn(element) {
        return Range._createIn(element);
    }
    /**
     * Creates new {@link module:engine/view/selection~Selection} instance.
     *
     * 		// Creates empty selection without ranges.
     *		const selection = writer.createSelection();
     *
     *		// Creates selection at the given range.
     *		const range = writer.createRange( start, end );
     *		const selection = writer.createSelection( range );
     *
     *		// Creates selection at the given ranges
     * 		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		const selection = writer.createSelection( ranges );
     *
     *		// Creates selection from the other selection.
     *		const otherSelection = writer.createSelection();
     *		const selection = writer.createSelection( otherSelection );
     *
     *		// Creates selection from the document selection.
     *		const selection = writer.createSelection( editor.editing.view.document.selection );
     *
     * 		// Creates selection at the given position.
     *		const position = writer.createPositionFromPath( root, path );
     *		const selection = writer.createSelection( position );
     *
     *		// Creates collapsed selection at the position of given item and offset.
     *		const paragraph = writer.createContainerElement( 'p' );
     *		const selection = writer.createSelection( paragraph, offset );
     *
     *		// Creates a range inside an {@link module:engine/view/element~Element element} which starts before the
     *		// first child of that element and ends after the last child of that element.
     *		const selection = writer.createSelection( paragraph, 'in' );
     *
     *		// Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends
     *		// just after the item.
     *		const selection = writer.createSelection( paragraph, 'on' );
     *
     * `Selection`'s constructor allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Creates backward selection.
     *		const selection = writer.createSelection( range, { backward: true } );
     *
     * Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
     * (and be  properly handled by screen readers).
     *
     *		// Creates fake selection with label.
     *		const selection = writer.createSelection( range, { fake: true, label: 'foo' } );
     *
     * @param {module:engine/view/selection~Selectable} [selectable=null]
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Offset or place when selectable is an `Item`.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     * @returns {module:engine/view/selection~Selection}
     */
    createSelection(...args) {
        return new Selection(...args);
    }
    /**
     * Creates placeholders for child elements of the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure
     * `elementToStructure()`} conversion helper.
     *
     *		const viewSlot = conversionApi.writer.createSlot();
     *		const viewPosition = conversionApi.writer.createPositionAt( viewElement, 0 );
     *
     *		conversionApi.writer.insert( viewPosition, viewSlot );
     *
     * It could be filtered down to a specific subset of children (only `<foo>` model elements in this case):
     *
     *		const viewSlot = conversionApi.writer.createSlot( node => node.is( 'element', 'foo' ) );
     *		const viewPosition = conversionApi.writer.createPositionAt( viewElement, 0 );
     *
     *		conversionApi.writer.insert( viewPosition, viewSlot );
     *
     * While providing a filtered slot, make sure to provide slots for all child nodes. A single node can not be downcasted into
     * multiple slots.
     *
     * **Note**: You should not change the order of nodes. View elements should be in the same order as model nodes.
     *
     * @param {'children'|module:engine/conversion/downcasthelpers~SlotFilter} [modeOrFilter='children'] The filter for child nodes.
     * @returns {module:engine/view/element~Element} The slot element to be placed in to the view structure while processing
     * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure `elementToStructure()`}.
     */
    createSlot(modeOrFilter) {
        if (!this._slotFactory) {
            /**
             * The `createSlot()` method is only allowed inside the `elementToStructure` downcast helper callback.
             *
             * @error view-writer-invalid-create-slot-context
             */
            throw new CKEditorError('view-writer-invalid-create-slot-context', this.document);
        }
        return this._slotFactory(this, modeOrFilter);
    }
    /**
     * Registers a slot factory.
     *
     * @protected
     * @param {Function} slotFactory The slot factory.
     */
    _registerSlotFactory(slotFactory) {
        this._slotFactory = slotFactory;
    }
    /**
     * Clears the registered slot factory.
     *
     * @protected
     */
    _clearSlotFactory() {
        this._slotFactory = null;
    }
    /**
     * Inserts a node or nodes at the specified position. Takes care of breaking attributes before insertion
     * and merging them afterwards if requested by the breakAttributes param.
     *
     * @private
     * @param {module:engine/view/position~Position} position Insertion position.
     * @param {module:engine/view/text~Text|module:engine/view/attributeelement~AttributeElement|
     * module:engine/view/containerelement~ContainerElement|module:engine/view/emptyelement~EmptyElement|
     * module:engine/view/rawelement~RawElement|module:engine/view/uielement~UIElement|
     * Iterable.<module:engine/view/text~Text|
     * module:engine/view/attributeelement~AttributeElement|module:engine/view/containerelement~ContainerElement|
     * module:engine/view/emptyelement~EmptyElement|module:engine/view/rawelement~RawElement|
     * module:engine/view/uielement~UIElement>} nodes Node or nodes to insert.
     * @param {Boolean} breakAttributes Whether attributes should be broken.
     * @returns {module:engine/view/range~Range} Range around inserted nodes.
     */
    _insertNodes(position, nodes, breakAttributes) {
        let parentElement;
        // Break attributes on nodes that do exist in the model tree so they can have attributes, other elements
        // can't have an attribute in model and won't get wrapped with an AttributeElement while down-casted.
        if (breakAttributes) {
            parentElement = getParentContainer(position);
        }
        else {
            parentElement = position.parent.is('$text') ? position.parent.parent : position.parent;
        }
        if (!parentElement) {
            /**
             * Position's parent container cannot be found.
             *
             * @error view-writer-invalid-position-container
             */
            throw new CKEditorError('view-writer-invalid-position-container', this.document);
        }
        let insertionPosition;
        if (breakAttributes) {
            insertionPosition = this._breakAttributes(position, true);
        }
        else {
            insertionPosition = position.parent.is('$text') ? breakTextNode(position) : position;
        }
        const length = parentElement._insertChild(insertionPosition.offset, nodes);
        for (const node of nodes) {
            this._addToClonedElementsGroup(node);
        }
        const endPosition = insertionPosition.getShiftedBy(length);
        const start = this.mergeAttributes(insertionPosition);
        // If start position was merged - move end position.
        if (!start.isEqual(insertionPosition)) {
            endPosition.offset--;
        }
        const end = this.mergeAttributes(endPosition);
        return new Range(start, end);
    }
    /**
     * Wraps children with provided `wrapElement`. Only children contained in `parent` element between
     * `startOffset` and `endOffset` will be wrapped.
     *
     * @private
     * @param {module:engine/view/element~Element} parent
     * @param {Number} startOffset
     * @param {Number} endOffset
     * @param {module:engine/view/element~Element} wrapElement
     */
    _wrapChildren(parent, startOffset, endOffset, wrapElement) {
        let i = startOffset;
        const wrapPositions = [];
        while (i < endOffset) {
            const child = parent.getChild(i);
            const isText = child.is('$text');
            const isAttribute = child.is('attributeElement');
            //
            // (In all examples, assume that `wrapElement` is `<span class="foo">` element.)
            //
            // Check if `wrapElement` can be joined with the wrapped element. One of requirements is having same name.
            // If possible, join elements.
            //
            // <p><span class="bar">abc</span></p>  -->  <p><span class="foo bar">abc</span></p>
            //
            if (isAttribute && this._wrapAttributeElement(wrapElement, child)) {
                wrapPositions.push(new Position(parent, i));
            }
            //
            // Wrap the child if it is not an attribute element or if it is an attribute element that should be inside
            // `wrapElement` (due to priority).
            //
            // <p>abc</p>                   -->  <p><span class="foo">abc</span></p>
            // <p><strong>abc</strong></p>  -->  <p><span class="foo"><strong>abc</strong></span></p>
            else if (isText || !isAttribute || shouldABeOutsideB(wrapElement, child)) {
                // Clone attribute.
                const newAttribute = wrapElement._clone();
                // Wrap current node with new attribute.
                child._remove();
                newAttribute._appendChild(child);
                parent._insertChild(i, newAttribute);
                this._addToClonedElementsGroup(newAttribute);
                wrapPositions.push(new Position(parent, i));
            }
            //
            // If other nested attribute is found and it wasn't wrapped (see above), continue wrapping inside it.
            //
            // <p><a href="foo.html">abc</a></p>  -->  <p><a href="foo.html"><span class="foo">abc</span></a></p>
            //
            else /* if ( isAttribute ) */ {
                this._wrapChildren(child, 0, child.childCount, wrapElement);
            }
            i++;
        }
        // Merge at each wrap.
        let offsetChange = 0;
        for (const position of wrapPositions) {
            position.offset -= offsetChange;
            // Do not merge with elements outside selected children.
            if (position.offset == startOffset) {
                continue;
            }
            const newPosition = this.mergeAttributes(position);
            // If nodes were merged - other merge offsets will change.
            if (!newPosition.isEqual(position)) {
                offsetChange++;
                endOffset--;
            }
        }
        return Range._createFromParentsAndOffsets(parent, startOffset, parent, endOffset);
    }
    /**
     * Unwraps children from provided `unwrapElement`. Only children contained in `parent` element between
     * `startOffset` and `endOffset` will be unwrapped.
     *
     * @private
     * @param {module:engine/view/element~Element} parent
     * @param {Number} startOffset
     * @param {Number} endOffset
     * @param {module:engine/view/element~Element} unwrapElement
     */
    _unwrapChildren(parent, startOffset, endOffset, unwrapElement) {
        let i = startOffset;
        const unwrapPositions = [];
        // Iterate over each element between provided offsets inside parent.
        // We don't use tree walker or range iterator because we will be removing and merging potentially multiple nodes,
        // so it could get messy. It is safer to it manually in this case.
        while (i < endOffset) {
            const child = parent.getChild(i);
            // Skip all text nodes. There should be no container element's here either.
            if (!child.is('attributeElement')) {
                i++;
                continue;
            }
            //
            // (In all examples, assume that `unwrapElement` is `<span class="foo">` element.)
            //
            // If the child is similar to the given attribute element, unwrap it - it will be completely removed.
            //
            // <p><span class="foo">abc</span>xyz</p>  -->  <p>abcxyz</p>
            //
            if (child.isSimilar(unwrapElement)) {
                const unwrapped = child.getChildren();
                const count = child.childCount;
                // Replace wrapper element with its children
                child._remove();
                parent._insertChild(i, unwrapped);
                this._removeFromClonedElementsGroup(child);
                // Save start and end position of moved items.
                unwrapPositions.push(new Position(parent, i), new Position(parent, i + count));
                // Skip elements that were unwrapped. Assuming there won't be another element to unwrap in child elements.
                i += count;
                endOffset += count - 1;
                continue;
            }
            //
            // If the child is not similar but is an attribute element, try partial unwrapping - remove the same attributes/styles/classes.
            // Partial unwrapping will happen only if the elements have the same name.
            //
            // <p><span class="foo bar">abc</span>xyz</p>  -->  <p><span class="bar">abc</span>xyz</p>
            // <p><i class="foo">abc</i>xyz</p>            -->  <p><i class="foo">abc</i>xyz</p>
            //
            if (this._unwrapAttributeElement(unwrapElement, child)) {
                unwrapPositions.push(new Position(parent, i), new Position(parent, i + 1));
                i++;
                continue;
            }
            //
            // If other nested attribute is found, look through it's children for elements to unwrap.
            //
            // <p><i><span class="foo">abc</span></i><p>  -->  <p><i>abc</i><p>
            //
            this._unwrapChildren(child, 0, child.childCount, unwrapElement);
            i++;
        }
        // Merge at each unwrap.
        let offsetChange = 0;
        for (const position of unwrapPositions) {
            position.offset -= offsetChange;
            // Do not merge with elements outside selected children.
            if (position.offset == startOffset || position.offset == endOffset) {
                continue;
            }
            const newPosition = this.mergeAttributes(position);
            // If nodes were merged - other merge offsets will change.
            if (!newPosition.isEqual(position)) {
                offsetChange++;
                endOffset--;
            }
        }
        return Range._createFromParentsAndOffsets(parent, startOffset, parent, endOffset);
    }
    /**
     * Helper function for `view.writer.wrap`. Wraps range with provided attribute element.
     * This method will also merge newly added attribute element with its siblings whenever possible.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-invalid-attribute` when passed attribute element is not
     * an instance of {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.
     *
     * @private
     * @param {module:engine/view/range~Range} range
     * @param {module:engine/view/attributeelement~AttributeElement} attribute
     * @returns {module:engine/view/range~Range} New range after wrapping, spanning over wrapping attribute element.
     */
    _wrapRange(range, attribute) {
        // Break attributes at range start and end.
        const { start: breakStart, end: breakEnd } = this._breakAttributesRange(range, true);
        const parentContainer = breakStart.parent;
        // Wrap all children with attribute.
        const newRange = this._wrapChildren(parentContainer, breakStart.offset, breakEnd.offset, attribute);
        // Merge attributes at the both ends and return a new range.
        const start = this.mergeAttributes(newRange.start);
        // If start position was merged - move end position back.
        if (!start.isEqual(newRange.start)) {
            newRange.end.offset--;
        }
        const end = this.mergeAttributes(newRange.end);
        return new Range(start, end);
    }
    /**
     * Helper function for {@link #wrap}. Wraps position with provided attribute element.
     * This method will also merge newly added attribute element with its siblings whenever possible.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError} `view-writer-wrap-invalid-attribute` when passed attribute element is not
     * an instance of {@link module:engine/view/attributeelement~AttributeElement AttributeElement}.
     *
     * @private
     * @param {module:engine/view/position~Position} position
     * @param {module:engine/view/attributeelement~AttributeElement} attribute
     * @returns {module:engine/view/position~Position} New position after wrapping.
     */
    _wrapPosition(position, attribute) {
        // Return same position when trying to wrap with attribute similar to position parent.
        if (attribute.isSimilar(position.parent)) {
            return movePositionToTextNode(position.clone());
        }
        // When position is inside text node - break it and place new position between two text nodes.
        if (position.parent.is('$text')) {
            position = breakTextNode(position);
        }
        // Create fake element that will represent position, and will not be merged with other attributes.
        const fakeElement = this.createAttributeElement('_wrapPosition-fake-element');
        fakeElement._priority = Number.POSITIVE_INFINITY;
        fakeElement.isSimilar = () => false;
        // Insert fake element in position location.
        position.parent._insertChild(position.offset, fakeElement);
        // Range around inserted fake attribute element.
        const wrapRange = new Range(position, position.getShiftedBy(1));
        // Wrap fake element with attribute (it will also merge if possible).
        this.wrap(wrapRange, attribute);
        // Remove fake element and place new position there.
        const newPosition = new Position(fakeElement.parent, fakeElement.index);
        fakeElement._remove();
        // If position is placed between text nodes - merge them and return position inside.
        const nodeBefore = newPosition.nodeBefore;
        const nodeAfter = newPosition.nodeAfter;
        if (nodeBefore instanceof text_Text && nodeAfter instanceof text_Text) {
            return mergeTextNodes(nodeBefore, nodeAfter);
        }
        // If position is next to text node - move position inside.
        return movePositionToTextNode(newPosition);
    }
    /**
     * 	Wraps one {@link module:engine/view/attributeelement~AttributeElement AttributeElement} into another by
     * 	merging them if possible. When merging is possible - all attributes, styles and classes are moved from wrapper
     * 	element to element being wrapped.
     *
     * 	@private
     * 	@param {module:engine/view/attributeelement~AttributeElement} wrapper Wrapper AttributeElement.
     * 	@param {module:engine/view/attributeelement~AttributeElement} toWrap AttributeElement to wrap using wrapper element.
     * 	@returns {Boolean} Returns `true` if elements are merged.
     */
    _wrapAttributeElement(wrapper, toWrap) {
        if (!canBeJoined(wrapper, toWrap)) {
            return false;
        }
        // Can't merge if name or priority differs.
        if (wrapper.name !== toWrap.name || wrapper.priority !== toWrap.priority) {
            return false;
        }
        // Check if attributes can be merged.
        for (const key of wrapper.getAttributeKeys()) {
            // Classes and styles should be checked separately.
            if (key === 'class' || key === 'style') {
                continue;
            }
            // If some attributes are different we cannot wrap.
            if (toWrap.hasAttribute(key) && toWrap.getAttribute(key) !== wrapper.getAttribute(key)) {
                return false;
            }
        }
        // Check if styles can be merged.
        for (const key of wrapper.getStyleNames()) {
            if (toWrap.hasStyle(key) && toWrap.getStyle(key) !== wrapper.getStyle(key)) {
                return false;
            }
        }
        // Move all attributes/classes/styles from wrapper to wrapped AttributeElement.
        for (const key of wrapper.getAttributeKeys()) {
            // Classes and styles should be checked separately.
            if (key === 'class' || key === 'style') {
                continue;
            }
            // Move only these attributes that are not present - other are similar.
            if (!toWrap.hasAttribute(key)) {
                this.setAttribute(key, wrapper.getAttribute(key), toWrap);
            }
        }
        for (const key of wrapper.getStyleNames()) {
            if (!toWrap.hasStyle(key)) {
                this.setStyle(key, wrapper.getStyle(key), toWrap);
            }
        }
        for (const key of wrapper.getClassNames()) {
            if (!toWrap.hasClass(key)) {
                this.addClass(key, toWrap);
            }
        }
        return true;
    }
    /**
     * Unwraps {@link module:engine/view/attributeelement~AttributeElement AttributeElement} from another by removing
     * corresponding attributes, classes and styles. All attributes, classes and styles from wrapper should be present
     * inside element being unwrapped.
     *
     * @private
     * @param {module:engine/view/attributeelement~AttributeElement} wrapper Wrapper AttributeElement.
     * @param {module:engine/view/attributeelement~AttributeElement} toUnwrap AttributeElement to unwrap using wrapper element.
     * @returns {Boolean} Returns `true` if elements are unwrapped.
     **/
    _unwrapAttributeElement(wrapper, toUnwrap) {
        if (!canBeJoined(wrapper, toUnwrap)) {
            return false;
        }
        // Can't unwrap if name or priority differs.
        if (wrapper.name !== toUnwrap.name || wrapper.priority !== toUnwrap.priority) {
            return false;
        }
        // Check if AttributeElement has all wrapper attributes.
        for (const key of wrapper.getAttributeKeys()) {
            // Classes and styles should be checked separately.
            if (key === 'class' || key === 'style') {
                continue;
            }
            // If some attributes are missing or different we cannot unwrap.
            if (!toUnwrap.hasAttribute(key) || toUnwrap.getAttribute(key) !== wrapper.getAttribute(key)) {
                return false;
            }
        }
        // Check if AttributeElement has all wrapper classes.
        if (!toUnwrap.hasClass(...wrapper.getClassNames())) {
            return false;
        }
        // Check if AttributeElement has all wrapper styles.
        for (const key of wrapper.getStyleNames()) {
            // If some styles are missing or different we cannot unwrap.
            if (!toUnwrap.hasStyle(key) || toUnwrap.getStyle(key) !== wrapper.getStyle(key)) {
                return false;
            }
        }
        // Remove all wrapper's attributes from unwrapped element.
        for (const key of wrapper.getAttributeKeys()) {
            // Classes and styles should be checked separately.
            if (key === 'class' || key === 'style') {
                continue;
            }
            this.removeAttribute(key, toUnwrap);
        }
        // Remove all wrapper's classes from unwrapped element.
        this.removeClass(Array.from(wrapper.getClassNames()), toUnwrap);
        // Remove all wrapper's styles from unwrapped element.
        this.removeStyle(Array.from(wrapper.getStyleNames()), toUnwrap);
        return true;
    }
    /**
     * Helper function used by other `DowncastWriter` methods. Breaks attribute elements at the boundaries of given range.
     *
     * @private
     * @param {module:engine/view/range~Range} range Range which `start` and `end` positions will be used to break attributes.
     * @param {Boolean} [forceSplitText=false] If set to `true`, will break text nodes even if they are directly in container element.
     * This behavior will result in incorrect view state, but is needed by other view writing methods which then fixes view state.
     * @returns {module:engine/view/range~Range} New range with located at break positions.
     */
    _breakAttributesRange(range, forceSplitText = false) {
        const rangeStart = range.start;
        const rangeEnd = range.end;
        validateRangeContainer(range, this.document);
        // Break at the collapsed position. Return new collapsed range.
        if (range.isCollapsed) {
            const position = this._breakAttributes(range.start, forceSplitText);
            return new Range(position, position);
        }
        const breakEnd = this._breakAttributes(rangeEnd, forceSplitText);
        const count = breakEnd.parent.childCount;
        const breakStart = this._breakAttributes(rangeStart, forceSplitText);
        // Calculate new break end offset.
        breakEnd.offset += breakEnd.parent.childCount - count;
        return new Range(breakStart, breakEnd);
    }
    /**
     * Helper function used by other `DowncastWriter` methods. Breaks attribute elements at given position.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-empty-element` when break position
     * is placed inside {@link module:engine/view/emptyelement~EmptyElement EmptyElement}.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-cannot-break-ui-element` when break position
     * is placed inside {@link module:engine/view/uielement~UIElement UIElement}.
     *
     * @private
     * @param {module:engine/view/position~Position} position Position where to break attributes.
     * @param {Boolean} [forceSplitText=false] If set to `true`, will break text nodes even if they are directly in container element.
     * This behavior will result in incorrect view state, but is needed by other view writing methods which then fixes view state.
     * @returns {module:engine/view/position~Position} New position after breaking the attributes.
     */
    _breakAttributes(position, forceSplitText = false) {
        const positionOffset = position.offset;
        const positionParent = position.parent;
        // If position is placed inside EmptyElement - throw an exception as we cannot break inside.
        if (position.parent.is('emptyElement')) {
            /**
             * Cannot break an `EmptyElement` instance.
             *
             * This error is thrown if
             * {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes `DowncastWriter#breakAttributes()`}
             * was executed in an incorrect position.
             *
             * @error view-writer-cannot-break-empty-element
             */
            throw new CKEditorError('view-writer-cannot-break-empty-element', this.document);
        }
        // If position is placed inside UIElement - throw an exception as we cannot break inside.
        if (position.parent.is('uiElement')) {
            /**
             * Cannot break a `UIElement` instance.
             *
             * This error is thrown if
             * {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes `DowncastWriter#breakAttributes()`}
             * was executed in an incorrect position.
             *
             * @error view-writer-cannot-break-ui-element
             */
            throw new CKEditorError('view-writer-cannot-break-ui-element', this.document);
        }
        // If position is placed inside RawElement - throw an exception as we cannot break inside.
        if (position.parent.is('rawElement')) {
            /**
             * Cannot break a `RawElement` instance.
             *
             * This error is thrown if
             * {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes `DowncastWriter#breakAttributes()`}
             * was executed in an incorrect position.
             *
             * @error view-writer-cannot-break-raw-element
             */
            throw new CKEditorError('view-writer-cannot-break-raw-element', this.document);
        }
        // There are no attributes to break and text nodes breaking is not forced.
        if (!forceSplitText && positionParent.is('$text') && isContainerOrFragment(positionParent.parent)) {
            return position.clone();
        }
        // Position's parent is container, so no attributes to break.
        if (isContainerOrFragment(positionParent)) {
            return position.clone();
        }
        // Break text and start again in new position.
        if (positionParent.is('$text')) {
            return this._breakAttributes(breakTextNode(position), forceSplitText);
        }
        const length = positionParent.childCount;
        // <p>foo<b><u>bar{}</u></b></p>
        // <p>foo<b><u>bar</u>[]</b></p>
        // <p>foo<b><u>bar</u></b>[]</p>
        if (positionOffset == length) {
            const newPosition = new Position(positionParent.parent, positionParent.index + 1);
            return this._breakAttributes(newPosition, forceSplitText);
        }
        else {
            // <p>foo<b><u>{}bar</u></b></p>
            // <p>foo<b>[]<u>bar</u></b></p>
            // <p>foo{}<b><u>bar</u></b></p>
            if (positionOffset === 0) {
                const newPosition = new Position(positionParent.parent, positionParent.index);
                return this._breakAttributes(newPosition, forceSplitText);
            }
            // <p>foo<b><u>b{}ar</u></b></p>
            // <p>foo<b><u>b[]ar</u></b></p>
            // <p>foo<b><u>b</u>[]<u>ar</u></b></p>
            // <p>foo<b><u>b</u></b>[]<b><u>ar</u></b></p>
            else {
                const offsetAfter = positionParent.index + 1;
                // Break element.
                const clonedNode = positionParent._clone();
                // Insert cloned node to position's parent node.
                positionParent.parent._insertChild(offsetAfter, clonedNode);
                this._addToClonedElementsGroup(clonedNode);
                // Get nodes to move.
                const count = positionParent.childCount - positionOffset;
                const nodesToMove = positionParent._removeChildren(positionOffset, count);
                // Move nodes to cloned node.
                clonedNode._appendChild(nodesToMove);
                // Create new position to work on.
                const newPosition = new Position(positionParent.parent, offsetAfter);
                return this._breakAttributes(newPosition, forceSplitText);
            }
        }
    }
    /**
     * Stores the information that an {@link module:engine/view/attributeelement~AttributeElement attribute element} was
     * added to the tree. Saves the reference to the group in the given element and updates the group, so other elements
     * from the group now keep a reference to the given attribute element.
     *
     * The clones group can be obtained using {@link module:engine/view/attributeelement~AttributeElement#getElementsWithSameId}.
     *
     * Does nothing if added element has no {@link module:engine/view/attributeelement~AttributeElement#id id}.
     *
     * @private
     * @param {module:engine/view/attributeelement~AttributeElement} element Attribute element to save.
     */
    _addToClonedElementsGroup(element) {
        // Add only if the element is in document tree.
        if (!element.root.is('rootElement')) {
            return;
        }
        // Traverse the element's children recursively to find other attribute elements that also might got inserted.
        // The loop is at the beginning so we can make fast returns later in the code.
        if (element.is('element')) {
            for (const child of element.getChildren()) {
                this._addToClonedElementsGroup(child);
            }
        }
        const id = element.id;
        if (!id) {
            return;
        }
        let group = this._cloneGroups.get(id);
        if (!group) {
            group = new Set();
            this._cloneGroups.set(id, group);
        }
        group.add(element);
        element._clonesGroup = group;
    }
    /**
     * Removes all the information about the given {@link module:engine/view/attributeelement~AttributeElement attribute element}
     * from its clones group.
     *
     * Keep in mind, that the element will still keep a reference to the group (but the group will not keep a reference to it).
     * This allows to reference the whole group even if the element was already removed from the tree.
     *
     * Does nothing if the element has no {@link module:engine/view/attributeelement~AttributeElement#id id}.
     *
     * @private
     * @param {module:engine/view/attributeelement~AttributeElement} element Attribute element to remove.
     */
    _removeFromClonedElementsGroup(element) {
        // Traverse the element's children recursively to find other attribute elements that also got removed.
        // The loop is at the beginning so we can make fast returns later in the code.
        if (element.is('element')) {
            for (const child of element.getChildren()) {
                this._removeFromClonedElementsGroup(child);
            }
        }
        const id = element.id;
        if (!id) {
            return;
        }
        const group = this._cloneGroups.get(id);
        if (!group) {
            return;
        }
        group.delete(element);
        // Not removing group from element on purpose!
        // If other parts of code have reference to this element, they will be able to get references to other elements from the group.
    }
}
// Helper function for `view.writer.wrap`. Checks if given element has any children that are not ui elements.
function _hasNonUiChildren(parent) {
    return Array.from(parent.getChildren()).some(child => !child.is('uiElement'));
}
/**
 * The `attribute` passed to {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#wrap()`}
 * must be an instance of {@link module:engine/view/attributeelement~AttributeElement `AttributeElement`}.
 *
 * @error view-writer-wrap-invalid-attribute
 */
// Returns first parent container of specified {@link module:engine/view/position~Position Position}.
// Position's parent node is checked as first, then next parents are checked.
// Note that {@link module:engine/view/documentfragment~DocumentFragment DocumentFragment} is treated like a container.
//
// @param {module:engine/view/position~Position} position Position used as a start point to locate parent container.
// @returns {module:engine/view/containerelement~ContainerElement|module:engine/view/documentfragment~DocumentFragment|undefined}
// Parent container element or `undefined` if container is not found.
function getParentContainer(position) {
    let parent = position.parent;
    while (!isContainerOrFragment(parent)) {
        if (!parent) {
            return undefined;
        }
        parent = parent.parent;
    }
    return parent;
}
// Checks if first {@link module:engine/view/attributeelement~AttributeElement AttributeElement} provided to the function
// can be wrapped outside second element. It is done by comparing elements'
// {@link module:engine/view/attributeelement~AttributeElement#priority priorities}, if both have same priority
// {@link module:engine/view/element~Element#getIdentity identities} are compared.
//
// @param {module:engine/view/attributeelement~AttributeElement} a
// @param {module:engine/view/attributeelement~AttributeElement} b
// @returns {Boolean}
function shouldABeOutsideB(a, b) {
    if (a.priority < b.priority) {
        return true;
    }
    else if (a.priority > b.priority) {
        return false;
    }
    // When priorities are equal and names are different - use identities.
    return a.getIdentity() < b.getIdentity();
}
// Returns new position that is moved to near text node. Returns same position if there is no text node before of after
// specified position.
//
//		<p>foo[]</p>  ->  <p>foo{}</p>
//		<p>[]foo</p>  ->  <p>{}foo</p>
//
// @param {module:engine/view/position~Position} position
// @returns {module:engine/view/position~Position} Position located inside text node or same position if there is no text nodes
// before or after position location.
function movePositionToTextNode(position) {
    const nodeBefore = position.nodeBefore;
    if (nodeBefore && nodeBefore.is('$text')) {
        return new Position(nodeBefore, nodeBefore.data.length);
    }
    const nodeAfter = position.nodeAfter;
    if (nodeAfter && nodeAfter.is('$text')) {
        return new Position(nodeAfter, 0);
    }
    return position;
}
// Breaks text node into two text nodes when possible.
//
//		<p>foo{}bar</p> -> <p>foo[]bar</p>
//		<p>{}foobar</p> -> <p>[]foobar</p>
//		<p>foobar{}</p> -> <p>foobar[]</p>
//
// @param {module:engine/view/position~Position} position Position that need to be placed inside text node.
// @returns {module:engine/view/position~Position} New position after breaking text node.
function breakTextNode(position) {
    if (position.offset == position.parent.data.length) {
        return new Position(position.parent.parent, position.parent.index + 1);
    }
    if (position.offset === 0) {
        return new Position(position.parent.parent, position.parent.index);
    }
    // Get part of the text that need to be moved.
    const textToMove = position.parent.data.slice(position.offset);
    // Leave rest of the text in position's parent.
    position.parent._data = position.parent.data.slice(0, position.offset);
    // Insert new text node after position's parent text node.
    position.parent.parent._insertChild(position.parent.index + 1, new text_Text(position.root.document, textToMove));
    // Return new position between two newly created text nodes.
    return new Position(position.parent.parent, position.parent.index + 1);
}
// Merges two text nodes into first node. Removes second node and returns merge position.
//
// @param {module:engine/view/text~Text} t1 First text node to merge. Data from second text node will be moved at the end of
// this text node.
// @param {module:engine/view/text~Text} t2 Second text node to merge. This node will be removed after merging.
// @returns {module:engine/view/position~Position} Position after merging text nodes.
function mergeTextNodes(t1, t2) {
    // Merge text data into first text node and remove second one.
    const nodeBeforeLength = t1.data.length;
    t1._data += t2.data;
    t2._remove();
    return new Position(t1, nodeBeforeLength);
}
const validNodesToInsert = [text_Text, AttributeElement, ContainerElement, EmptyElement, RawElement, UIElement];
// Checks if provided nodes are valid to insert.
//
// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-insert-invalid-node` when nodes to insert
// contains instances that are not supported ones (see error description for valid ones.
//
// @param Iterable.<module:engine/view/text~Text|module:engine/view/element~Element> nodes
// @param {Object} errorContext
function validateNodesToInsert(nodes, errorContext) {
    for (const node of nodes) {
        if (!validNodesToInsert.some((validNode => node instanceof validNode))) { // eslint-disable-line no-use-before-define
            /**
             * One of the nodes to be inserted is of an invalid type.
             *
             * Nodes to be inserted with {@link module:engine/view/downcastwriter~DowncastWriter#insert `DowncastWriter#insert()`} should be
             * of the following types:
             *
             * * {@link module:engine/view/attributeelement~AttributeElement AttributeElement},
             * * {@link module:engine/view/containerelement~ContainerElement ContainerElement},
             * * {@link module:engine/view/emptyelement~EmptyElement EmptyElement},
             * * {@link module:engine/view/uielement~UIElement UIElement},
             * * {@link module:engine/view/rawelement~RawElement RawElement},
             * * {@link module:engine/view/text~Text Text}.
             *
             * @error view-writer-insert-invalid-node-type
             */
            throw new CKEditorError('view-writer-insert-invalid-node-type', errorContext);
        }
        if (!node.is('$text')) {
            validateNodesToInsert(node.getChildren(), errorContext);
        }
    }
}
// Checks if node is ContainerElement or DocumentFragment, because in most cases they should be treated the same way.
//
// @param {module:engine/view/node~Node} node
// @returns {Boolean} Returns `true` if node is instance of ContainerElement or DocumentFragment.
function isContainerOrFragment(node) {
    return node && (node.is('containerElement') || node.is('documentFragment'));
}
// Checks if {@link module:engine/view/range~Range#start range start} and {@link module:engine/view/range~Range#end range end} are placed
// inside same {@link module:engine/view/containerelement~ContainerElement container element}.
// Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when validation fails.
//
// @param {module:engine/view/range~Range} range
// @param {Object} errorContext
function validateRangeContainer(range, errorContext) {
    const startContainer = getParentContainer(range.start);
    const endContainer = getParentContainer(range.end);
    if (!startContainer || !endContainer || startContainer !== endContainer) {
        /**
         * The container of the given range is invalid.
         *
         * This may happen if {@link module:engine/view/range~Range#start range start} and
         * {@link module:engine/view/range~Range#end range end} positions are not placed inside the same container element or
         * a parent container for these positions cannot be found.
         *
         * Methods like {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#remove()`},
         * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#clean()`},
         * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#wrap()`},
         * {@link module:engine/view/downcastwriter~DowncastWriter#wrap `DowncastWriter#unwrap()`} need to be called
         * on a range that has its start and end positions located in the same container element. Both positions can be
         * nested within other elements (e.g. an attribute element) but the closest container ancestor must be the same.
         *
         * @error view-writer-invalid-range-container
         */
        throw new CKEditorError('view-writer-invalid-range-container', errorContext);
    }
}
// Checks if two attribute elements can be joined together. Elements can be joined together if, and only if
// they do not have ids specified.
//
// @private
// @param {module:engine/view/element~Element} a
// @param {module:engine/view/element~Element} b
// @returns {Boolean}
function canBeJoined(a, b) {
    return a.id === null && b.id === null;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/istext.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/istext
 */
/**
 * Checks if the object is a native DOM Text node.
 *
 * @param {*} obj
 * @returns {Boolean}
 */
function isText(obj) {
    return Object.prototype.toString.call(obj) == '[object Text]';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/filler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */


/**
 * Set of utilities related to handling block and inline fillers.
 *
 * Browsers do not allow to put caret in elements which does not have height. Because of it, we need to fill all
 * empty elements which should be selectable with elements or characters called "fillers". Unfortunately there is no one
 * universal filler, this is why two types are uses:
 *
 * * Block filler is an element which fill block elements, like `<p>`. CKEditor uses `<br>` as a block filler during the editing,
 * as browsers do natively. So instead of an empty `<p>` there will be `<p><br></p>`. The advantage of block filler is that
 * it is transparent for the selection, so when the caret is before the `<br>` and user presses right arrow he will be
 * moved to the next paragraph, not after the `<br>`. The disadvantage is that it breaks a block, so it can not be used
 * in the middle of a line of text. The {@link module:engine/view/filler~BR_FILLER `<br>` filler} can be replaced with any other
 * character in the data output, for instance {@link module:engine/view/filler~NBSP_FILLER non-breaking space} or
 * {@link module:engine/view/filler~MARKED_NBSP_FILLER marked non-breaking space}.
 *
 * * Inline filler is a filler which does not break a line of text, so it can be used inside the text, for instance in the empty
 * `<b>` surrendered by text: `foo<b></b>bar`, if we want to put the caret there. CKEditor uses a sequence of the zero-width
 * spaces as an {@link module:engine/view/filler~INLINE_FILLER inline filler} having the predetermined
 * {@link module:engine/view/filler~INLINE_FILLER_LENGTH length}. A sequence is used, instead of a single character to
 * avoid treating random zero-width spaces as the inline filler. Disadvantage of the inline filler is that it is not
 * transparent for the selection. The arrow key moves the caret between zero-width spaces characters, so the additional
 * code is needed to handle the caret.
 *
 * Both inline and block fillers are handled by the {@link module:engine/view/renderer~Renderer renderer} and are not present in the
 * view.
 *
 * @module engine/view/filler
 */
/**
 * Non-breaking space filler creator. This function creates the `&nbsp;` text node.
 * It defines how the filler is created.
 *
 * @see module:engine/view/filler~MARKED_NBSP_FILLER
 * @see module:engine/view/filler~BR_FILLER
 * @function
 */
const NBSP_FILLER = (domDocument) => domDocument.createTextNode('\u00A0');
/**
 * Marked non-breaking space filler creator. This function creates the `<span data-cke-filler="true">&nbsp;</span>` element.
 * It defines how the filler is created.
 *
 * @see module:engine/view/filler~NBSP_FILLER
 * @see module:engine/view/filler~BR_FILLER
 * @function
 */
const MARKED_NBSP_FILLER = (domDocument) => {
    const span = domDocument.createElement('span');
    span.dataset.ckeFiller = 'true';
    span.innerText = '\u00A0';
    return span;
};
/**
 * `<br>` filler creator. This function creates the `<br data-cke-filler="true">` element.
 * It defines how the filler is created.
 *
 * @see module:engine/view/filler~NBSP_FILLER
 * @see module:engine/view/filler~MARKED_NBSP_FILLER
 * @function
 */
const BR_FILLER = (domDocument) => {
    const fillerBr = domDocument.createElement('br');
    fillerBr.dataset.ckeFiller = 'true';
    return fillerBr;
};
/**
 * Length of the {@link module:engine/view/filler~INLINE_FILLER INLINE_FILLER}.
 */
const INLINE_FILLER_LENGTH = 7;
/**
 * Inline filler which is a sequence of the word joiners.
 *
 * @type {String}
 */
const INLINE_FILLER = '\u2060'.repeat(INLINE_FILLER_LENGTH);
/**
 * Checks if the node is a text node which starts with the {@link module:engine/view/filler~INLINE_FILLER inline filler}.
 *
 *		startsWithFiller( document.createTextNode( INLINE_FILLER ) ); // true
 *		startsWithFiller( document.createTextNode( INLINE_FILLER + 'foo' ) ); // true
 *		startsWithFiller( document.createTextNode( 'foo' ) ); // false
 *		startsWithFiller( document.createElement( 'p' ) ); // false
 *
 * @param {Node} domNode DOM node.
 * @returns {Boolean} True if the text node starts with the {@link module:engine/view/filler~INLINE_FILLER inline filler}.
 */
function startsWithFiller(domNode) {
    return isText(domNode) && (domNode.data.substr(0, INLINE_FILLER_LENGTH) === INLINE_FILLER);
}
/**
 * Checks if the text node contains only the {@link module:engine/view/filler~INLINE_FILLER inline filler}.
 *
 *		isInlineFiller( document.createTextNode( INLINE_FILLER ) ); // true
 *		isInlineFiller( document.createTextNode( INLINE_FILLER + 'foo' ) ); // false
 *
 * @param {Text} domText DOM text node.
 * @returns {Boolean} True if the text node contains only the {@link module:engine/view/filler~INLINE_FILLER inline filler}.
 */
function isInlineFiller(domText) {
    return domText.data.length == INLINE_FILLER_LENGTH && startsWithFiller(domText);
}
/**
 * Get string data from the text node, removing an {@link module:engine/view/filler~INLINE_FILLER inline filler} from it,
 * if text node contains it.
 *
 *		getDataWithoutFiller( document.createTextNode( INLINE_FILLER + 'foo' ) ) == 'foo' // true
 *		getDataWithoutFiller( document.createTextNode( 'foo' ) ) == 'foo' // true
 *
 * @param {Text} domText DOM text node, possible with inline filler.
 * @returns {String} Data without filler.
 */
function getDataWithoutFiller(domText) {
    if (startsWithFiller(domText)) {
        return domText.data.slice(INLINE_FILLER_LENGTH);
    }
    else {
        return domText.data;
    }
}
/**
 * Assign key observer which move cursor from the end of the inline filler to the beginning of it when
 * the left arrow is pressed, so the filler does not break navigation.
 *
 * @param {module:engine/view/view~View} view View controller instance we should inject quirks handling on.
 */
function injectQuirksHandling(view) {
    view.document.on('arrowKey', jumpOverInlineFiller, { priority: 'low' });
}
// Move cursor from the end of the inline filler to the beginning of it when, so the filler does not break navigation.
function jumpOverInlineFiller(evt, data) {
    if (data.keyCode == keyCodes.arrowleft) {
        const domSelection = data.domTarget.ownerDocument.defaultView.getSelection();
        if (domSelection.rangeCount == 1 && domSelection.getRangeAt(0).collapsed) {
            const domParent = domSelection.getRangeAt(0).startContainer;
            const domOffset = domSelection.getRangeAt(0).startOffset;
            if (startsWithFiller(domParent) && domOffset <= INLINE_FILLER_LENGTH) {
                domSelection.collapse(domParent, 0);
            }
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/fastdiff.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Finds positions of the first and last change in the given string/array and generates a set of changes:
 *
 *		fastDiff( '12a', '12xyza' );
 *		// [ { index: 2, type: 'insert', values: [ 'x', 'y', 'z' ] } ]
 *
 *		fastDiff( '12a', '12aa' );
 *		// [ { index: 3, type: 'insert', values: [ 'a' ] } ]
 *
 *		fastDiff( '12xyza', '12a' );
 *		// [ { index: 2, type: 'delete', howMany: 3 } ]
 *
 *		fastDiff( [ '1', '2', 'a', 'a' ], [ '1', '2', 'a' ] );
 *		// [ { index: 3, type: 'delete', howMany: 1 } ]
 *
 *		fastDiff( [ '1', '2', 'a', 'b', 'c', '3' ], [ '2', 'a', 'b' ] );
 *		// [ { index: 0, type: 'insert', values: [ '2', 'a', 'b' ] }, { index: 3, type: 'delete', howMany: 6 } ]
 *
 * Passed arrays can contain any type of data, however to compare them correctly custom comparator function
 * should be passed as a third parameter:
 *
 *		fastDiff( [ { value: 1 }, { value: 2 } ], [ { value: 1 }, { value: 3 } ], ( a, b ) => {
 *			return a.value === b.value;
 *		} );
 *		// [ { index: 1, type: 'insert', values: [ { value: 3 } ] }, { index: 2, type: 'delete', howMany: 1 } ]
 *
 * The resulted set of changes can be applied to the input in order to transform it into the output, for example:
 *
 *		let input = '12abc3';
 *		const output = '2ab';
 *		const changes = fastDiff( input, output );
 *
 *		changes.forEach( change => {
 *			if ( change.type == 'insert' ) {
 *				input = input.substring( 0, change.index ) + change.values.join( '' ) + input.substring( change.index );
 *			} else if ( change.type == 'delete' ) {
 *				input = input.substring( 0, change.index ) + input.substring( change.index + change.howMany );
 *			}
 *		} );
 *
 *		// input equals output now
 *
 * or in case of arrays:
 *
 *		let input = [ '1', '2', 'a', 'b', 'c', '3' ];
 *		const output = [ '2', 'a', 'b' ];
 *		const changes = fastDiff( input, output );
 *
 *		changes.forEach( change => {
 *			if ( change.type == 'insert' ) {
 *				input = input.slice( 0, change.index ).concat( change.values, input.slice( change.index ) );
 *			} else if ( change.type == 'delete' ) {
 *				input = input.slice( 0, change.index ).concat( input.slice( change.index + change.howMany ) );
 *			}
 *		} );
 *
 *		// input equals output now
 *
 * By passing `true` as the fourth parameter (`atomicChanges`) the output of this function will become compatible with
 * the {@link module:utils/diff~diff `diff()`} function:
 *
 *		fastDiff( '12a', '12xyza' );
 *		// [ 'equal', 'equal', 'insert', 'insert', 'insert', 'equal' ]
 *
 * The default output format of this function is compatible with the output format of
 * {@link module:utils/difftochanges~diffToChanges `diffToChanges()`}. The `diffToChanges()` input format is, in turn,
 * compatible with the output of {@link module:utils/diff~diff `diff()`}:
 *
 *		const a = '1234';
 *		const b = '12xyz34';
 *
 *		// Both calls will return the same results (grouped changes format).
 *		fastDiff( a, b );
 *		diffToChanges( diff( a, b ) );
 *
 *		// Again, both calls will return the same results (atomic changes format).
 *		fastDiff( a, b, null, true );
 *		diff( a, b );
 *
 *
 * @param {Array|String} a Input array or string.
 * @param {Array|String} b Input array or string.
 * @param {Function} [cmp] Optional function used to compare array values, by default `===` (strict equal operator) is used.
 * @param {Boolean} [atomicChanges=false] Whether an array of `inset|delete|equal` operations should
 * be returned instead of changes set. This makes this function compatible with {@link module:utils/diff~diff `diff()`}.
 * @returns {Array} Array of changes.
 */
function fastDiff(a, b, cmp, atomicChanges = false) {
    // Set the comparator function.
    cmp = cmp || function (a, b) {
        return a === b;
    };
    // Convert the string (or any array-like object - eg. NodeList) to an array by using the slice() method because,
    // unlike Array.from(), it returns array of UTF-16 code units instead of the code points of a string.
    // One code point might be a surrogate pair of two code units. All text offsets are expected to be in code units.
    // See ckeditor/ckeditor5#3147.
    //
    // We need to make sure here that fastDiff() works identical to diff().
    const arrayA = Array.isArray(a) ? a : Array.prototype.slice.call(a);
    const arrayB = Array.isArray(b) ? b : Array.prototype.slice.call(b);
    // Find first and last change.
    const changeIndexes = findChangeBoundaryIndexes(arrayA, arrayB, cmp);
    // Transform into changes array.
    return atomicChanges ? changeIndexesToAtomicChanges(changeIndexes, arrayB.length) : changeIndexesToChanges(arrayB, changeIndexes);
}
// Finds position of the first and last change in the given arrays. For example:
//
//		const indexes = findChangeBoundaryIndexes( [ '1', '2', '3', '4' ], [ '1', '3', '4', '2', '4' ] );
//		console.log( indexes ); // { firstIndex: 1, lastIndexOld: 3, lastIndexNew: 4 }
//
// The above indexes means that in the first array the modified part is `1[23]4` and in the second array it is `1[342]4`.
// Based on such indexes, array with `insert`/`delete` operations which allows transforming first value into the second one
// can be generated.
//
// @param {Array} arr1
// @param {Array} arr2
// @param {Function} cmp Comparator function.
// @returns {Object}
// @returns {Number} return.firstIndex Index of the first change in both values (always the same for both).
// @returns {Number} result.lastIndexOld Index of the last common value in `arr1`.
// @returns {Number} result.lastIndexNew Index of the last common value in `arr2`.
function findChangeBoundaryIndexes(arr1, arr2, cmp) {
    // Find the first difference between passed values.
    const firstIndex = findFirstDifferenceIndex(arr1, arr2, cmp);
    // If arrays are equal return -1 indexes object.
    if (firstIndex === -1) {
        return { firstIndex: -1, lastIndexOld: -1, lastIndexNew: -1 };
    }
    // Remove the common part of each value and reverse them to make it simpler to find the last difference between them.
    const oldArrayReversed = cutAndReverse(arr1, firstIndex);
    const newArrayReversed = cutAndReverse(arr2, firstIndex);
    // Find the first difference between reversed values.
    // It should be treated as "how many elements from the end the last difference occurred".
    //
    // For example:
    //
    // 				initial	->	after cut	-> reversed:
    // oldValue:	'321ba'	->	'21ba'		-> 'ab12'
    // newValue:	'31xba'	->	'1xba'		-> 'abx1'
    // lastIndex:							-> 2
    //
    // So the last change occurred two characters from the end of the arrays.
    const lastIndex = findFirstDifferenceIndex(oldArrayReversed, newArrayReversed, cmp);
    // Use `lastIndex` to calculate proper offset, starting from the beginning (`lastIndex` kind of starts from the end).
    const lastIndexOld = arr1.length - lastIndex;
    const lastIndexNew = arr2.length - lastIndex;
    return { firstIndex, lastIndexOld, lastIndexNew };
}
// Returns a first index on which given arrays differ. If both arrays are the same, -1 is returned.
//
// @param {Array} arr1
// @param {Array} arr2
// @param {Function} cmp Comparator function.
// @returns {Number}
function findFirstDifferenceIndex(arr1, arr2, cmp) {
    for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
        if (arr1[i] === undefined || arr2[i] === undefined || !cmp(arr1[i], arr2[i])) {
            return i;
        }
    }
    return -1; // Return -1 if arrays are equal.
}
// Returns a copy of the given array with `howMany` elements removed starting from the beginning and in reversed order.
//
// @param {Array} arr Array to be processed.
// @param {Number} howMany How many elements from array beginning to remove.
// @returns {Array} Shortened and reversed array.
function cutAndReverse(arr, howMany) {
    return arr.slice(howMany).reverse();
}
// Generates changes array based on change indexes from `findChangeBoundaryIndexes` function. This function will
// generate array with 0 (no changes), 1 (deletion or insertion) or 2 records (insertion and deletion).
//
// @param {Array} newArray New array for which change indexes were calculated.
// @param {Object} changeIndexes Change indexes object from `findChangeBoundaryIndexes` function.
// @returns {Array.<module:utils/difftochanges~Change>} Array of changes compatible with
// {@link module:utils/difftochanges~diffToChanges} format.
function changeIndexesToChanges(newArray, changeIndexes) {
    const result = [];
    const { firstIndex, lastIndexOld, lastIndexNew } = changeIndexes;
    // Order operations as 'insert', 'delete' array to keep compatibility with {@link module:utils/difftochanges~diffToChanges}
    // in most cases. However, 'diffToChanges' does not stick to any order so in some cases
    // (for example replacing '12345' with 'abcd') it will generate 'delete', 'insert' order.
    if (lastIndexNew - firstIndex > 0) {
        result.push({
            index: firstIndex,
            type: 'insert',
            values: newArray.slice(firstIndex, lastIndexNew)
        });
    }
    if (lastIndexOld - firstIndex > 0) {
        result.push({
            index: firstIndex + (lastIndexNew - firstIndex),
            type: 'delete',
            howMany: lastIndexOld - firstIndex
        });
    }
    return result;
}
// Generates array with set `equal|insert|delete` operations based on change indexes from `findChangeBoundaryIndexes` function.
//
// @param {Object} changeIndexes Change indexes object from `findChangeBoundaryIndexes` function.
// @param {Number} newLength Length of the new array on which `findChangeBoundaryIndexes` calculated change indexes.
// @returns {Array.<module:utils/diff~DiffResult>} Array of changes compatible with {@link module:utils/diff~diff} format.
function changeIndexesToAtomicChanges(changeIndexes, newLength) {
    const { firstIndex, lastIndexOld, lastIndexNew } = changeIndexes;
    // No changes.
    if (firstIndex === -1) {
        return Array(newLength).fill('equal');
    }
    let result = [];
    if (firstIndex > 0) {
        result = result.concat(Array(firstIndex).fill('equal'));
    }
    if (lastIndexNew - firstIndex > 0) {
        result = result.concat(Array(lastIndexNew - firstIndex).fill('insert'));
    }
    if (lastIndexOld - firstIndex > 0) {
        result = result.concat(Array(lastIndexOld - firstIndex).fill('delete'));
    }
    if (lastIndexNew < newLength) {
        result = result.concat(Array(newLength - lastIndexNew).fill('equal'));
    }
    return result;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/diff.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/diff
 */

// The following code is based on the "O(NP) Sequence Comparison Algorithm"
// by Sun Wu, Udi Manber, Gene Myers, Webb Miller.
/**
 * Calculates the difference between two arrays or strings producing an array containing a list of changes
 * necessary to transform input into output.
 *
 *		diff( 'aba', 'acca' ); // [ 'equal', 'insert', 'insert', 'delete', 'equal' ]
 *
 * This function is based on the "O(NP) Sequence Comparison Algorithm" by Sun Wu, Udi Manber, Gene Myers, Webb Miller.
 * Unfortunately, while it gives the most precise results, its to complex for longer strings/arrow (above 200 items).
 * Therefore, `diff()` automatically switches to {@link module:utils/fastdiff~fastDiff `fastDiff()`} when detecting
 * such a scenario. The return formats of both functions are identical.
 *
 * @param {Array|String} a Input array or string.
 * @param {Array|String} b Output array or string.
 * @param {Function} [cmp] Optional function used to compare array values, by default === is used.
 * @returns {Array.<module:utils/diff~DiffResult>} Array of changes.
 */
function diff(a, b, cmp) {
    // Set the comparator function.
    cmp = cmp || function (a, b) {
        return a === b;
    };
    const aLength = a.length;
    const bLength = b.length;
    // Perform `fastDiff` for longer strings/arrays (see #269).
    if (aLength > 200 || bLength > 200 || aLength + bLength > 300) {
        return diff.fastDiff(a, b, cmp, true);
    }
    // Temporary action type statics.
    let _insert, _delete;
    // Swapped the arrays to use the shorter one as the first one.
    if (bLength < aLength) {
        const tmp = a;
        a = b;
        b = tmp;
        // We swap the action types as well.
        _insert = 'delete';
        _delete = 'insert';
    }
    else {
        _insert = 'insert';
        _delete = 'delete';
    }
    const m = a.length;
    const n = b.length;
    const delta = n - m;
    // Edit scripts, for each diagonal.
    const es = {};
    // Furthest points, the furthest y we can get on each diagonal.
    const fp = {};
    function snake(k) {
        // We use -1 as an alternative below to handle initial values ( instead of filling the fp with -1 first ).
        // Furthest points (y) on the diagonal below k.
        const y1 = (fp[k - 1] !== undefined ? fp[k - 1] : -1) + 1;
        // Furthest points (y) on the diagonal above k.
        const y2 = fp[k + 1] !== undefined ? fp[k + 1] : -1;
        // The way we should go to get further.
        const dir = y1 > y2 ? -1 : 1;
        // Clone previous changes array (if any).
        if (es[k + dir]) {
            es[k] = es[k + dir].slice(0);
        }
        // Create changes array.
        if (!es[k]) {
            es[k] = [];
        }
        // Push the action.
        es[k].push(y1 > y2 ? _insert : _delete);
        // Set the beginning coordinates.
        let y = Math.max(y1, y2);
        let x = y - k;
        // Traverse the diagonal as long as the values match.
        while (x < m && y < n && cmp(a[x], b[y])) {
            x++;
            y++;
            // Push no change action.
            es[k].push('equal');
        }
        return y;
    }
    let p = 0;
    let k;
    // Traverse the graph until we reach the end of the longer string.
    do {
        // Updates furthest points and edit scripts for diagonals below delta.
        for (k = -p; k < delta; k++) {
            fp[k] = snake(k);
        }
        // Updates furthest points and edit scripts for diagonals above delta.
        for (k = delta + p; k > delta; k--) {
            fp[k] = snake(k);
        }
        // Updates furthest point and edit script for the delta diagonal.
        // note that the delta diagonal is the one which goes through the sink (m, n).
        fp[delta] = snake(delta);
        p++;
    } while (fp[delta] !== n);
    // Return the final list of edit changes.
    // We remove the first item that represents the action for the injected nulls.
    return es[delta].slice(1);
}
// Store the API in static property to easily overwrite it in tests.
// Too bad dependency injection does not work in Webpack + ES 6 (const) + Babel.
diff.fastDiff = fastDiff;

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/insertat.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/insertat
 */
/**
 * Inserts node to the parent at given index.
 *
 * @param {Element} parentElement Parent element.
 * @param {Number} index Insertions index.
 * @param {Node} nodeToInsert Node to insert.
 */
function insertAt(parentElement, index, nodeToInsert) {
    parentElement.insertBefore(nodeToInsert, parentElement.childNodes[index] || null);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/remove.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/remove
 */
/**
 * Removes given node from parent.
 *
 * @param {Node} node Node to remove.
 */
function remove(node) {
    const parent = node.parentNode;
    if (parent) {
        parent.removeChild(node);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/iscomment.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* globals Node */
/**
 * @module utils/dom/iscomment
 */
/**
 * Checks whether the object is a native DOM Comment node.
 *
 * @param {*} obj
 * @returns {Boolean}
 */
function isComment(obj) {
    return obj && obj.nodeType === Node.COMMENT_NODE;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/isnode.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/isnode
 */
/**
 * Checks if the object is a native DOM Node.
 *
 * @param {*} obj
 * @returns {Boolean}
 */
function isNode(obj) {
    if (obj) {
        if (obj.defaultView) {
            return obj instanceof obj.defaultView.Document;
        }
        else if (obj.ownerDocument && obj.ownerDocument.defaultView) {
            return obj instanceof obj.ownerDocument.defaultView.Node;
        }
    }
    return false;
}

// EXTERNAL MODULE: ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js
var injectStylesIntoStyleTag = __webpack_require__(3379);
var injectStylesIntoStyleTag_default = /*#__PURE__*/__webpack_require__.n(injectStylesIntoStyleTag);
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-engine/theme/renderer.css
var renderer = __webpack_require__(4401);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/theme/renderer.css

            

var options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

options.insert = "head";
options.singleton = true;

var update = injectStylesIntoStyleTag_default()(renderer/* default */.Z, options);



/* harmony default export */ const theme_renderer = (renderer/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/renderer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/renderer
 */














/**
 * Renderer is responsible for updating the DOM structure and the DOM selection based on
 * the {@link module:engine/view/renderer~Renderer#markToSync information about updated view nodes}.
 * In other words, it renders the view to the DOM.
 *
 * Its main responsibility is to make only the necessary, minimal changes to the DOM. However, unlike in many
 * virtual DOM implementations, the primary reason for doing minimal changes is not the performance but ensuring
 * that native editing features such as text composition, autocompletion, spell checking, selection's x-index are
 * affected as little as possible.
 *
 * Renderer uses {@link module:engine/view/domconverter~DomConverter} to transform view nodes and positions
 * to and from the DOM.
 */
class Renderer extends Observable {
    /**
     * Creates a renderer instance.
     *
     * @param {module:engine/view/domconverter~DomConverter} domConverter Converter instance.
     * @param {module:engine/view/documentselection~DocumentSelection} selection View selection.
     */
    constructor(domConverter, selection) {
        super();
        /**
         * Set of DOM Documents instances.
         *
         * @readonly
         * @member {Set.<Document>}
         */
        this.domDocuments = new Set();
        /**
         * Converter instance.
         *
         * @readonly
         * @member {module:engine/view/domconverter~DomConverter}
         */
        this.domConverter = domConverter;
        /**
         * Set of nodes which attributes changed and may need to be rendered.
         *
         * @readonly
         * @member {Set.<module:engine/view/node~ViewNode>}
         */
        this.markedAttributes = new Set();
        /**
         * Set of elements which child lists changed and may need to be rendered.
         *
         * @readonly
         * @member {Set.<module:engine/view/node~ViewNode>}
         */
        this.markedChildren = new Set();
        /**
         * Set of text nodes which text data changed and may need to be rendered.
         *
         * @readonly
         * @member {Set.<module:engine/view/node~ViewNode>}
         */
        this.markedTexts = new Set();
        /**
         * View selection. Renderer updates DOM selection based on the view selection.
         *
         * @readonly
         * @member {module:engine/view/documentselection~DocumentSelection}
         */
        this.selection = selection;
        /**
         * Indicates if the view document is focused and selection can be rendered. Selection will not be rendered if
         * this is set to `false`.
         *
         * @member {Boolean}
         * @observable
         */
        this.set('isFocused', false);
        /**
         * Indicates whether the user is making a selection in the document (e.g. holding the mouse button and moving the cursor).
         * When they stop selecting, the property goes back to `false`.
         *
         * Note: In some browsers, the renderer will stop rendering the selection and inline fillers while the user is making
         * a selection to avoid glitches in DOM selection
         * (https://github.com/ckeditor/ckeditor5/issues/10562, https://github.com/ckeditor/ckeditor5/issues/10723).
         *
         * @member {Boolean}
         * @observable
         */
        this.set('isSelecting', false);
        // Rendering the selection and inline filler manipulation should be postponed in (non-Android) Blink until the user finishes
        // creating the selection in DOM to avoid accidental selection collapsing
        // (https://github.com/ckeditor/ckeditor5/issues/10562, https://github.com/ckeditor/ckeditor5/issues/10723).
        // When the user stops selecting, all pending changes should be rendered ASAP, though.
        if (src_env.isBlink && !src_env.isAndroid) {
            this.on('change:isSelecting', () => {
                if (!this.isSelecting) {
                    this.render();
                }
            });
        }
        /**
         * True if composition is in progress inside the document.
         *
         * This property is bound to the {@link module:engine/view/document~Document#isComposing `Document#isComposing`} property.
         *
         * @member {Boolean}
         * @observable
         */
        this.set('isComposing', false);
        this.on('change:isComposing', () => {
            if (!this.isComposing) {
                this.render();
            }
        });
        /**
         * The text node in which the inline filler was rendered.
         *
         * @private
         * @member {Text}
         */
        this._inlineFiller = null;
        /**
         * DOM element containing fake selection.
         *
         * @private
         * @type {null|HTMLElement}
         */
        this._fakeSelectionContainer = null;
    }
    /**
     * Marks a view node to be updated in the DOM by {@link #render `render()`}.
     *
     * Note that only view nodes whose parents have corresponding DOM elements need to be marked to be synchronized.
     *
     * @see #markedAttributes
     * @see #markedChildren
     * @see #markedTexts
     *
     * @param {module:engine/view/document~ChangeType} type Type of the change.
     * @param {module:engine/view/node~ViewNode} node ViewNode to be marked.
     */
    markToSync(type, node) {
        if (type === 'text') {
            if (this.domConverter.mapViewToDom(node.parent)) {
                this.markedTexts.add(node);
            }
        }
        else {
            // If the node has no DOM element it is not rendered yet,
            // its children/attributes do not need to be marked to be sync.
            if (!this.domConverter.mapViewToDom(node)) {
                return;
            }
            if (type === 'attributes') {
                this.markedAttributes.add(node);
            }
            else if (type === 'children') {
                this.markedChildren.add(node);
            }
            else {
                /**
                 * Unknown type passed to Renderer.markToSync.
                 *
                 * @error view-renderer-unknown-type
                 */
                throw new CKEditorError('view-renderer-unknown-type', this);
            }
        }
    }
    /**
     * Renders all buffered changes ({@link #markedAttributes}, {@link #markedChildren} and {@link #markedTexts}) and
     * the current view selection (if needed) to the DOM by applying a minimal set of changes to it.
     *
     * Renderer tries not to break the text composition (e.g. IME) and x-index of the selection,
     * so it does as little as it is needed to update the DOM.
     *
     * Renderer also handles {@link module:engine/view/filler fillers}. Especially, it checks if the inline filler is needed
     * at the selection position and adds or removes it. To prevent breaking text composition inline filler will not be
     * removed as long as the selection is in the text node which needed it at first.
     */
    render() {
        // Ignore rendering while in the composition mode. Composition events are not cancellable and browser will modify the DOM tree.
        // All marked elements, attributes, etc. will wait until next render after the composition ends.
        // On Android composition events are immediately applied to the model, so we don't need to skip rendering,
        // and we should not do it because the difference between view and DOM could lead to position mapping problems.
        if (this.isComposing && !src_env.isAndroid) {
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.info( '%c[Renderer]%c Rendering aborted while isComposing',
            // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', ''
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            return;
        }
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.group( '%c[Renderer]%c Rendering',
        // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', ''
        // @if CK_DEBUG_TYPING // 	);
        // @if CK_DEBUG_TYPING // }
        let inlineFillerPosition = null;
        const isInlineFillerRenderingPossible = src_env.isBlink && !src_env.isAndroid ? !this.isSelecting : true;
        // Refresh mappings.
        for (const element of this.markedChildren) {
            this._updateChildrenMappings(element);
        }
        // Don't manipulate inline fillers while the selection is being made in (non-Android) Blink to prevent accidental
        // DOM selection collapsing
        // (https://github.com/ckeditor/ckeditor5/issues/10562, https://github.com/ckeditor/ckeditor5/issues/10723).
        if (isInlineFillerRenderingPossible) {
            // There was inline filler rendered in the DOM but it's not
            // at the selection position any more, so we can remove it
            // (cause even if it's needed, it must be placed in another location).
            if (this._inlineFiller && !this._isSelectionInInlineFiller()) {
                this._removeInlineFiller();
            }
            // If we've got the filler, let's try to guess its position in the view.
            if (this._inlineFiller) {
                inlineFillerPosition = this._getInlineFillerPosition();
            }
            // Otherwise, if it's needed, create it at the selection position.
            else if (this._needsInlineFillerAtSelection()) {
                inlineFillerPosition = this.selection.getFirstPosition();
                // Do not use `markToSync` so it will be added even if the parent is already added.
                this.markedChildren.add(inlineFillerPosition.parent);
            }
        }
        // Make sure the inline filler has any parent, so it can be mapped to view position by DomConverter.
        else if (this._inlineFiller && this._inlineFiller.parentNode) {
            // While the user is making selection, preserve the inline filler at its original position.
            inlineFillerPosition = this.domConverter.domPositionToView(this._inlineFiller);
            // While down-casting the document selection attributes, all existing empty
            // attribute elements (for selection position) are removed from the view and DOM,
            // so make sure that we were able to map filler position.
            // https://github.com/ckeditor/ckeditor5/issues/12026
            if (inlineFillerPosition && inlineFillerPosition.parent.is('$text')) {
                // The inline filler position is expected to be before the text node.
                inlineFillerPosition = Position._createBefore(inlineFillerPosition.parent);
            }
        }
        for (const element of this.markedAttributes) {
            this._updateAttrs(element);
        }
        for (const element of this.markedChildren) {
            this._updateChildren(element, { inlineFillerPosition });
        }
        for (const node of this.markedTexts) {
            if (!this.markedChildren.has(node.parent) && this.domConverter.mapViewToDom(node.parent)) {
                this._updateText(node, { inlineFillerPosition });
            }
        }
        // * Check whether the inline filler is required and where it really is in the DOM.
        //   At this point in most cases it will be in the DOM, but there are exceptions.
        //   For example, if the inline filler was deep in the created DOM structure, it will not be created.
        //   Similarly, if it was removed at the beginning of this function and then neither text nor children were updated,
        //   it will not be present. Fix those and similar scenarios.
        // * Don't manipulate inline fillers while the selection is being made in (non-Android) Blink to prevent accidental
        //   DOM selection collapsing
        //   (https://github.com/ckeditor/ckeditor5/issues/10562, https://github.com/ckeditor/ckeditor5/issues/10723).
        if (isInlineFillerRenderingPossible) {
            if (inlineFillerPosition) {
                const fillerDomPosition = this.domConverter.viewPositionToDom(inlineFillerPosition);
                const domDocument = fillerDomPosition.parent.ownerDocument;
                if (!startsWithFiller(fillerDomPosition.parent)) {
                    // Filler has not been created at filler position. Create it now.
                    this._inlineFiller = addInlineFiller(domDocument, fillerDomPosition.parent, fillerDomPosition.offset);
                }
                else {
                    // Filler has been found, save it.
                    this._inlineFiller = fillerDomPosition.parent;
                }
            }
            else {
                // There is no filler needed.
                this._inlineFiller = null;
            }
        }
        // First focus the new editing host, then update the selection.
        // Otherwise, FF may throw an error (https://github.com/ckeditor/ckeditor5/issues/721).
        this._updateFocus();
        this._updateSelection();
        this.markedTexts.clear();
        this.markedAttributes.clear();
        this.markedChildren.clear();
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.groupEnd();
        // @if CK_DEBUG_TYPING // }
    }
    /**
     * Updates mappings of view element's children.
     *
     * Children that were replaced in the view structure by similar elements (same tag name) are treated as 'replaced'.
     * This means that their mappings can be updated so the new view elements are mapped to the existing DOM elements.
     * Thanks to that these elements do not need to be re-rendered completely.
     *
     * @private
     * @param {module:engine/view/node~ViewNode} viewElement The view element whose children mappings will be updated.
     */
    _updateChildrenMappings(viewElement) {
        const domElement = this.domConverter.mapViewToDom(viewElement);
        if (!domElement) {
            // If there is no `domElement` it means that it was already removed from DOM and there is no need to process it.
            return;
        }
        // Removing nodes from the DOM as we iterate can cause `actualDomChildren`
        // (which is a live-updating `NodeList`) to get out of sync with the
        // indices that we compute as we iterate over `actions`.
        // This would produce incorrect element mappings.
        //
        // Converting live list to an array to make the list static.
        const actualDomChildren = Array.from(this.domConverter.mapViewToDom(viewElement).childNodes);
        const expectedDomChildren = Array.from(this.domConverter.viewChildrenToDom(viewElement, { withChildren: false }));
        const diff = this._diffNodeLists(actualDomChildren, expectedDomChildren);
        const actions = this._findReplaceActions(diff, actualDomChildren, expectedDomChildren);
        if (actions.indexOf('replace') !== -1) {
            const counter = { equal: 0, insert: 0, delete: 0 };
            for (const action of actions) {
                if (action === 'replace') {
                    const insertIndex = counter.equal + counter.insert;
                    const deleteIndex = counter.equal + counter.delete;
                    const viewChild = viewElement.getChild(insertIndex);
                    // UIElement and RawElement are special cases. Their children are not stored in a view (#799)
                    // so we cannot use them with replacing flow (since they use view children during rendering
                    // which will always result in rendering empty elements).
                    if (viewChild && !(viewChild.is('uiElement') || viewChild.is('rawElement'))) {
                        this._updateElementMappings(viewChild, actualDomChildren[deleteIndex]);
                    }
                    remove(expectedDomChildren[insertIndex]);
                    counter.equal++;
                }
                else {
                    counter[action]++;
                }
            }
        }
    }
    /**
     * Updates mappings of a given view element.
     *
     * @private
     * @param {module:engine/view/node~ViewNode} viewElement The view element whose mappings will be updated.
     * @param {ViewNode} domElement The DOM element representing the given view element.
     */
    _updateElementMappings(viewElement, domElement) {
        // Remap 'DomConverter' bindings.
        this.domConverter.unbindDomElement(domElement);
        this.domConverter.bindElements(domElement, viewElement);
        // View element may have children which needs to be updated, but are not marked, mark them to update.
        this.markedChildren.add(viewElement);
        // Because we replace new view element mapping with the existing one, the corresponding DOM element
        // will not be rerendered. The new view element may have different attributes than the previous one.
        // Since its corresponding DOM element will not be rerendered, new attributes will not be added
        // to the DOM, so we need to mark it here to make sure its attributes gets updated. See #1427 for more
        // detailed case study.
        // Also there are cases where replaced element is removed from the view structure and then has
        // its attributes changed or removed. In such cases the element will not be present in `markedAttributes`
        // and also may be the same (`element.isSimilar()`) as the reused element not having its attributes updated.
        // To prevent such situations we always mark reused element to have its attributes rerenderd (#1560).
        this.markedAttributes.add(viewElement);
    }
    /**
     * Gets the position of the inline filler based on the current selection.
     * Here, we assume that we know that the filler is needed and
     * {@link #_isSelectionInInlineFiller is at the selection position}, and, since it is needed,
     * it is somewhere at the selection position.
     *
     * Note: The filler position cannot be restored based on the filler's DOM text node, because
     * when this method is called (before rendering), the bindings will often be broken. View-to-DOM
     * bindings are only dependable after rendering.
     *
     * @private
     * @returns {module:engine/view/position~Position}
     */
    _getInlineFillerPosition() {
        const firstPos = this.selection.getFirstPosition();
        if (firstPos.parent.is('$text')) {
            return Position._createBefore(firstPos.parent);
        }
        else {
            return firstPos;
        }
    }
    /**
     * Returns `true` if the selection has not left the inline filler's text node.
     * If it is `true`, it means that the filler had been added for a reason and the selection did not
     * leave the filler's text node. For example, the user can be in the middle of a composition so it should not be touched.
     *
     * @private
     * @returns {Boolean} `true` if the inline filler and selection are in the same place.
     */
    _isSelectionInInlineFiller() {
        if (this.selection.rangeCount != 1 || !this.selection.isCollapsed) {
            return false;
        }
        // Note, we can't check if selection's position equals position of the
        // this._inlineFiller node, because of #663. We may not be able to calculate
        // the filler's position in the view at this stage.
        // Instead, we check it the other way – whether selection is anchored in
        // that text node or next to it.
        // Possible options are:
        // "FILLER{}"
        // "FILLERadded-text{}"
        const selectionPosition = this.selection.getFirstPosition();
        const position = this.domConverter.viewPositionToDom(selectionPosition);
        if (position && isText(position.parent) && startsWithFiller(position.parent)) {
            return true;
        }
        return false;
    }
    /**
     * Removes the inline filler.
     *
     * @private
     */
    _removeInlineFiller() {
        const domFillerNode = this._inlineFiller;
        // Something weird happened and the stored node doesn't contain the filler's text.
        if (!startsWithFiller(domFillerNode)) {
            /**
             * The inline filler node was lost. Most likely, something overwrote the filler text node
             * in the DOM.
             *
             * @error view-renderer-filler-was-lost
             */
            throw new CKEditorError('view-renderer-filler-was-lost', this);
        }
        if (isInlineFiller(domFillerNode)) {
            domFillerNode.remove();
        }
        else {
            domFillerNode.data = domFillerNode.data.substr(INLINE_FILLER_LENGTH);
        }
        this._inlineFiller = null;
    }
    /**
     * Checks if the inline {@link module:engine/view/filler filler} should be added.
     *
     * @private
     * @returns {Boolean} `true` if the inline filler should be added.
     */
    _needsInlineFillerAtSelection() {
        if (this.selection.rangeCount != 1 || !this.selection.isCollapsed) {
            return false;
        }
        const selectionPosition = this.selection.getFirstPosition();
        const selectionParent = selectionPosition.parent;
        const selectionOffset = selectionPosition.offset;
        // If there is no DOM root we do not care about fillers.
        if (!this.domConverter.mapViewToDom(selectionParent.root)) {
            return false;
        }
        if (!(selectionParent.is('element'))) {
            return false;
        }
        // Prevent adding inline filler inside elements with contenteditable=false.
        // https://github.com/ckeditor/ckeditor5-engine/issues/1170
        if (!isEditable(selectionParent)) {
            return false;
        }
        // We have block filler, we do not need inline one.
        if (selectionOffset === selectionParent.getFillerOffset()) {
            return false;
        }
        const nodeBefore = selectionPosition.nodeBefore;
        const nodeAfter = selectionPosition.nodeAfter;
        if (nodeBefore instanceof text_Text || nodeAfter instanceof text_Text) {
            return false;
        }
        // Do not use inline filler while typing outside inline elements on Android.
        // The deleteContentBackward would remove part of the inline filler instead of removing last letter in a link.
        if (src_env.isAndroid && (nodeBefore || nodeAfter)) {
            return false;
        }
        return true;
    }
    /**
     * Checks if text needs to be updated and possibly updates it.
     *
     * @private
     * @param {module:engine/view/text~Text} viewText View text to update.
     * @param {Object} options
     * @param {module:engine/view/position~Position} options.inlineFillerPosition The position where the inline
     * filler should be rendered.
     */
    _updateText(viewText, options) {
        const domText = this.domConverter.findCorrespondingDomText(viewText);
        const newDomText = this.domConverter.viewToDom(viewText);
        let expectedText = newDomText.data;
        const filler = options.inlineFillerPosition;
        if (filler && filler.parent == viewText.parent && filler.offset == viewText.index) {
            expectedText = INLINE_FILLER + expectedText;
        }
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.group( '%c[Renderer]%c Update text',
        // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', ''
        // @if CK_DEBUG_TYPING // 	);
        // @if CK_DEBUG_TYPING // }
        updateTextNode(domText, expectedText);
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.groupEnd();
        // @if CK_DEBUG_TYPING // }
    }
    /**
     * Checks if attribute list needs to be updated and possibly updates it.
     *
     * @private
     * @param {module:engine/view/element~Element} viewElement The view element to update.
     */
    _updateAttrs(viewElement) {
        const domElement = this.domConverter.mapViewToDom(viewElement);
        if (!domElement) {
            // If there is no `domElement` it means that 'viewElement' is outdated as its mapping was updated
            // in 'this._updateChildrenMappings()'. There is no need to process it as new view element which
            // replaced old 'viewElement' mapping was also added to 'this.markedAttributes'
            // in 'this._updateChildrenMappings()' so it will be processed separately.
            return;
        }
        const domAttrKeys = Array.from(domElement.attributes).map(attr => attr.name);
        const viewAttrKeys = viewElement.getAttributeKeys();
        // Add or overwrite attributes.
        for (const key of viewAttrKeys) {
            this.domConverter.setDomElementAttribute(domElement, key, viewElement.getAttribute(key), viewElement);
        }
        // Remove from DOM attributes which do not exists in the view.
        for (const key of domAttrKeys) {
            // All other attributes not present in the DOM should be removed.
            if (!viewElement.hasAttribute(key)) {
                this.domConverter.removeDomElementAttribute(domElement, key);
            }
        }
    }
    /**
     * Checks if elements child list needs to be updated and possibly updates it.
     *
     * Note that on Android, to reduce the risk of composition breaks, it tries to update data of an existing
     * child text nodes instead of replacing them completely.
     *
     * @private
     * @param {module:engine/view/element~Element} viewElement View element to update.
     * @param {Object} options
     * @param {module:engine/view/position~Position} options.inlineFillerPosition The position where the inline
     * filler should be rendered.
     */
    _updateChildren(viewElement, options) {
        const domElement = this.domConverter.mapViewToDom(viewElement);
        if (!domElement) {
            // If there is no `domElement` it means that it was already removed from DOM.
            // There is no need to process it. It will be processed when re-inserted.
            return;
        }
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.group( '%c[Renderer]%c Update children',
        // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', ''
        // @if CK_DEBUG_TYPING // 	);
        // @if CK_DEBUG_TYPING // }
        // IME on Android inserts a new text node while typing after a link
        // instead of updating an existing text node that follows the link.
        // We must normalize those text nodes so the diff won't get confused.
        // https://github.com/ckeditor/ckeditor5/issues/12574.
        if (src_env.isAndroid) {
            let previousDomNode = null;
            for (const domNode of Array.from(domElement.childNodes)) {
                if (previousDomNode && isText(previousDomNode) && isText(domNode)) {
                    domElement.normalize();
                    break;
                }
                previousDomNode = domNode;
            }
        }
        const inlineFillerPosition = options.inlineFillerPosition;
        const actualDomChildren = domElement.childNodes;
        const expectedDomChildren = Array.from(this.domConverter.viewChildrenToDom(viewElement, { bind: true }));
        // Inline filler element has to be created as it is present in the DOM, but not in the view. It is required
        // during diffing so text nodes could be compared correctly and also during rendering to maintain
        // proper order and indexes while updating the DOM.
        if (inlineFillerPosition && inlineFillerPosition.parent === viewElement) {
            addInlineFiller(domElement.ownerDocument, expectedDomChildren, inlineFillerPosition.offset);
        }
        const diff = this._diffNodeLists(actualDomChildren, expectedDomChildren);
        // The rendering is not disabled on Android in the composition mode.
        // Composition events are not cancellable and browser will modify the DOM tree.
        // On Android composition events are immediately applied to the model, so we don't need to skip rendering,
        // and we should not do it because the difference between view and DOM could lead to position mapping problems.
        // Since the composition is fragile and often breaks if the composed text node is replaced while composing
        // we need to make sure that we update the existing text node and not replace it with another one.
        // We don't want to change the behavior on other browsers for safety, but maybe one day cause it seems to make sense.
        // https://github.com/ckeditor/ckeditor5/issues/12455.
        const actions = src_env.isAndroid ?
            this._findReplaceActions(diff, actualDomChildren, expectedDomChildren, { replaceText: true }) :
            diff;
        let i = 0;
        const nodesToUnbind = new Set();
        // Handle deletions first.
        // This is to prevent a situation where an element that already exists in `actualDomChildren` is inserted at a different
        // index in `actualDomChildren`. Since `actualDomChildren` is a `NodeList`, this works like move, not like an insert,
        // and it disrupts the whole algorithm. See https://github.com/ckeditor/ckeditor5/issues/6367.
        //
        // It doesn't matter in what order we remove or add nodes, as long as we remove and add correct nodes at correct indexes.
        for (const action of actions) {
            if (action === 'delete') {
                // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
                // @if CK_DEBUG_TYPING // 	console.info( '%c[Renderer]%c Remove node',
                // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', '', actualDomChildren[ i ]
                // @if CK_DEBUG_TYPING // 	);
                // @if CK_DEBUG_TYPING // }
                nodesToUnbind.add(actualDomChildren[i]);
                remove(actualDomChildren[i]);
            }
            else if (action === 'equal' || action === 'replace') {
                i++;
            }
        }
        i = 0;
        for (const action of actions) {
            if (action === 'insert') {
                // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
                // @if CK_DEBUG_TYPING // 	console.info( '%c[Renderer]%c Insert node',
                // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', '', expectedDomChildren[ i ]
                // @if CK_DEBUG_TYPING // 	);
                // @if CK_DEBUG_TYPING // }
                insertAt(domElement, i, expectedDomChildren[i]);
                i++;
            }
            // Update the existing text node data. Note that replace action is generated only for Android for now.
            else if (action === 'replace') {
                // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
                // @if CK_DEBUG_TYPING // 	console.group( '%c[Renderer]%c Update text node',
                // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', ''
                // @if CK_DEBUG_TYPING // 	);
                // @if CK_DEBUG_TYPING // }
                updateTextNode(actualDomChildren[i], expectedDomChildren[i].data);
                i++;
                // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
                // @if CK_DEBUG_TYPING // 	console.groupEnd();
                // @if CK_DEBUG_TYPING // }
            }
            else if (action === 'equal') {
                // Force updating text nodes inside elements which did not change and do not need to be re-rendered (#1125).
                // Do it here (not in the loop above) because only after insertions the `i` index is correct.
                this._markDescendantTextToSync(this.domConverter.domToView(expectedDomChildren[i]));
                i++;
            }
        }
        // Unbind removed nodes. When node does not have a parent it means that it was removed from DOM tree during
        // comparison with the expected DOM. We don't need to check child nodes, because if child node was reinserted,
        // it was moved to DOM tree out of the removed node.
        for (const node of nodesToUnbind) {
            if (!node.parentNode) {
                this.domConverter.unbindDomElement(node);
            }
        }
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.groupEnd();
        // @if CK_DEBUG_TYPING // }
    }
    /**
     * Shorthand for diffing two arrays or node lists of DOM nodes.
     *
     * @private
     * @param {Array.<ViewNode>|NodeList} actualDomChildren Actual DOM children
     * @param {Array.<ViewNode>|NodeList} expectedDomChildren Expected DOM children.
     * @returns {Array.<String>} The list of actions based on the {@link module:utils/diff~diff} function.
     */
    _diffNodeLists(actualDomChildren, expectedDomChildren) {
        actualDomChildren = filterOutFakeSelectionContainer(actualDomChildren, this._fakeSelectionContainer);
        return diff(actualDomChildren, expectedDomChildren, sameNodes.bind(null, this.domConverter));
    }
    /**
     * Finds DOM nodes that were replaced with the similar nodes (same tag name) in the view. All nodes are compared
     * within one `insert`/`delete` action group, for example:
     *
     * 		Actual DOM:		<p><b>Foo</b>Bar<i>Baz</i><b>Bax</b></p>
     * 		Expected DOM:	<p>Bar<b>123</b><i>Baz</i><b>456</b></p>
     * 		Input actions:	[ insert, insert, delete, delete, equal, insert, delete ]
     * 		Output actions:	[ insert, replace, delete, equal, replace ]
     *
     * @private
     * @param {Array.<String>} actions Actions array which is a result of the {@link module:utils/diff~diff} function.
     * @param {Array.<ViewNode>|NodeList} actualDom Actual DOM children
     * @param {Array.<ViewNode>} expectedDom Expected DOM children.
     * @param {Object} [options] Options
     * @param {Boolean} [options.replaceText] Mark text nodes replacement.
     * @returns {Array.<String>} Actions array modified with the `replace` actions.
     */
    _findReplaceActions(actions, actualDom, expectedDom, options = {}) {
        // If there is no both 'insert' and 'delete' actions, no need to check for replaced elements.
        if (actions.indexOf('insert') === -1 || actions.indexOf('delete') === -1) {
            return actions;
        }
        let newActions = [];
        let actualSlice = [];
        let expectedSlice = [];
        const counter = { equal: 0, insert: 0, delete: 0 };
        for (const action of actions) {
            if (action === 'insert') {
                expectedSlice.push(expectedDom[counter.equal + counter.insert]);
            }
            else if (action === 'delete') {
                actualSlice.push(actualDom[counter.equal + counter.delete]);
            }
            else { // equal
                newActions = newActions.concat(diff(actualSlice, expectedSlice, options.replaceText ? areTextNodes : areSimilar)
                    .map(x => x === 'equal' ? 'replace' : x));
                newActions.push('equal');
                // Reset stored elements on 'equal'.
                actualSlice = [];
                expectedSlice = [];
            }
            counter[action]++;
        }
        return newActions.concat(diff(actualSlice, expectedSlice, options.replaceText ? areTextNodes : areSimilar)
            .map(x => x === 'equal' ? 'replace' : x));
    }
    /**
     * Marks text nodes to be synchronized.
     *
     * If a text node is passed, it will be marked. If an element is passed, all descendant text nodes inside it will be marked.
     *
     * @private
     * @param {module:engine/view/node~ViewNode} viewNode View node to sync.
     */
    _markDescendantTextToSync(viewNode) {
        if (!viewNode) {
            return;
        }
        if (viewNode.is('$text')) {
            this.markedTexts.add(viewNode);
        }
        else if (viewNode.is('element')) {
            for (const child of viewNode.getChildren()) {
                this._markDescendantTextToSync(child);
            }
        }
    }
    /**
     * Checks if the selection needs to be updated and possibly updates it.
     *
     * @private
     */
    _updateSelection() {
        // Block updating DOM selection in (non-Android) Blink while the user is selecting to prevent accidental selection collapsing.
        // Note: Structural changes in DOM must trigger selection rendering, though. Nodes the selection was anchored
        // to, may disappear in DOM which would break the selection (e.g. in real-time collaboration scenarios).
        // https://github.com/ckeditor/ckeditor5/issues/10562, https://github.com/ckeditor/ckeditor5/issues/10723
        if (src_env.isBlink && !src_env.isAndroid && this.isSelecting && !this.markedChildren.size) {
            return;
        }
        // If there is no selection - remove DOM and fake selections.
        if (this.selection.rangeCount === 0) {
            this._removeDomSelection();
            this._removeFakeSelection();
            return;
        }
        const domRoot = this.domConverter.mapViewToDom(this.selection.editableElement);
        // Do nothing if there is no focus, or there is no DOM element corresponding to selection's editable element.
        if (!this.isFocused || !domRoot) {
            return;
        }
        // Render fake selection - create the fake selection container (if needed) and move DOM selection to it.
        if (this.selection.isFake) {
            this._updateFakeSelection(domRoot);
        }
        // There was a fake selection so remove it and update the DOM selection.
        // This is especially important on Android because otherwise IME will try to compose over the fake selection container.
        else if (this._fakeSelectionContainer && this._fakeSelectionContainer.isConnected) {
            this._removeFakeSelection();
            this._updateDomSelection(domRoot);
        }
        // Update the DOM selection in case of a plain selection change (no fake selection is involved).
        // On non-Android the whole rendering is disabled in composition mode (including DOM selection update),
        // but updating DOM selection should be also disabled on Android if in the middle of the composition
        // (to not interrupt it).
        else if (!(this.isComposing && src_env.isAndroid)) {
            this._updateDomSelection(domRoot);
        }
    }
    /**
     * Updates the fake selection.
     *
     * @private
     * @param {HTMLElement} domRoot A valid DOM root where the fake selection container should be added.
     */
    _updateFakeSelection(domRoot) {
        const domDocument = domRoot.ownerDocument;
        if (!this._fakeSelectionContainer) {
            this._fakeSelectionContainer = createFakeSelectionContainer(domDocument);
        }
        const container = this._fakeSelectionContainer;
        // Bind fake selection container with the current selection *position*.
        this.domConverter.bindFakeSelection(container, this.selection);
        if (!this._fakeSelectionNeedsUpdate(domRoot)) {
            return;
        }
        if (!container.parentElement || container.parentElement != domRoot) {
            domRoot.appendChild(container);
        }
        container.textContent = this.selection.fakeSelectionLabel || '\u00A0';
        const domSelection = domDocument.getSelection();
        const domRange = domDocument.createRange();
        domSelection.removeAllRanges();
        domRange.selectNodeContents(container);
        domSelection.addRange(domRange);
    }
    /**
     * Updates the DOM selection.
     *
     * @private
     * @param {HTMLElement} domRoot A valid DOM root where the DOM selection should be rendered.
     */
    _updateDomSelection(domRoot) {
        const domSelection = domRoot.ownerDocument.defaultView.getSelection();
        // Let's check whether DOM selection needs updating at all.
        if (!this._domSelectionNeedsUpdate(domSelection)) {
            return;
        }
        // Multi-range selection is not available in most browsers, and, at least in Chrome, trying to
        // set such selection, that is not continuous, throws an error. Because of that, we will just use anchor
        // and focus of view selection.
        // Since we are not supporting multi-range selection, we also do not need to check if proper editable is
        // selected. If there is any editable selected, it is okay (editable is taken from selection anchor).
        const anchor = this.domConverter.viewPositionToDom(this.selection.anchor);
        const focus = this.domConverter.viewPositionToDom(this.selection.focus);
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.info( '%c[Renderer]%c Update DOM selection:',
        // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', '', anchor, focus
        // @if CK_DEBUG_TYPING // 	);
        // @if CK_DEBUG_TYPING // }
        domSelection.collapse(anchor.parent, anchor.offset);
        domSelection.extend(focus.parent, focus.offset);
        // Firefox–specific hack (https://github.com/ckeditor/ckeditor5-engine/issues/1439).
        if (src_env.isGecko) {
            fixGeckoSelectionAfterBr(focus, domSelection);
        }
    }
    /**
     * Checks whether a given DOM selection needs to be updated.
     *
     * @private
     * @param {Selection} domSelection The DOM selection to check.
     * @returns {Boolean}
     */
    _domSelectionNeedsUpdate(domSelection) {
        if (!this.domConverter.isDomSelectionCorrect(domSelection)) {
            // Current DOM selection is in incorrect position. We need to update it.
            return true;
        }
        const oldViewSelection = domSelection && this.domConverter.domSelectionToView(domSelection);
        if (oldViewSelection && this.selection.isEqual(oldViewSelection)) {
            return false;
        }
        // If selection is not collapsed, it does not need to be updated if it is similar.
        if (!this.selection.isCollapsed && this.selection.isSimilar(oldViewSelection)) {
            // Selection did not changed and is correct, do not update.
            return false;
        }
        // Selections are not similar.
        return true;
    }
    /**
     * Checks whether the fake selection needs to be updated.
     *
     * @private
     * @param {HTMLElement} domRoot A valid DOM root where a new fake selection container should be added.
     * @returns {Boolean}
     */
    _fakeSelectionNeedsUpdate(domRoot) {
        const container = this._fakeSelectionContainer;
        const domSelection = domRoot.ownerDocument.getSelection();
        // Fake selection needs to be updated if there's no fake selection container, or the container currently sits
        // in a different root.
        if (!container || container.parentElement !== domRoot) {
            return true;
        }
        // Make sure that the selection actually is within the fake selection.
        if (domSelection.anchorNode !== container && !container.contains(domSelection.anchorNode)) {
            return true;
        }
        return container.textContent !== this.selection.fakeSelectionLabel;
    }
    /**
     * Removes the DOM selection.
     *
     * @private
     */
    _removeDomSelection() {
        for (const doc of this.domDocuments) {
            const domSelection = doc.getSelection();
            if (domSelection.rangeCount) {
                const activeDomElement = doc.activeElement;
                const viewElement = this.domConverter.mapDomToView(activeDomElement);
                if (activeDomElement && viewElement) {
                    domSelection.removeAllRanges();
                }
            }
        }
    }
    /**
     * Removes the fake selection.
     *
     * @private
     */
    _removeFakeSelection() {
        const container = this._fakeSelectionContainer;
        if (container) {
            container.remove();
        }
    }
    /**
     * Checks if focus needs to be updated and possibly updates it.
     *
     * @private
     */
    _updateFocus() {
        if (this.isFocused) {
            const editable = this.selection.editableElement;
            if (editable) {
                this.domConverter.focus(editable);
            }
        }
    }
}
// Checks if provided element is editable.
//
// @private
// @param {module:engine/view/element~Element} element
// @returns {Boolean}
function isEditable(element) {
    if (element.getAttribute('contenteditable') == 'false') {
        return false;
    }
    const parent = element.findAncestor(element => element.hasAttribute('contenteditable'));
    return !parent || parent.getAttribute('contenteditable') == 'true';
}
// Adds inline filler at a given position.
//
// The position can be given as an array of DOM nodes and an offset in that array,
// or a DOM parent element and an offset in that element.
//
// @private
// @param {Document} domDocument
// @param {Element|Array.<ViewNode>} domParentOrArray
// @param {Number} offset
// @returns {Text} The DOM text node that contains an inline filler.
function addInlineFiller(domDocument, domParentOrArray, offset) {
    const childNodes = domParentOrArray instanceof Array ? domParentOrArray : domParentOrArray.childNodes;
    const nodeAfterFiller = childNodes[offset];
    if (isText(nodeAfterFiller)) {
        nodeAfterFiller.data = INLINE_FILLER + nodeAfterFiller.data;
        return nodeAfterFiller;
    }
    else {
        const fillerNode = domDocument.createTextNode(INLINE_FILLER);
        if (Array.isArray(domParentOrArray)) {
            childNodes.splice(offset, 0, fillerNode);
        }
        else {
            insertAt(domParentOrArray, offset, fillerNode);
        }
        return fillerNode;
    }
}
// Whether two DOM nodes should be considered as similar.
// Nodes are considered similar if they have the same tag name.
//
// @private
// @param {ViewNode} node1
// @param {ViewNode} node2
// @returns {Boolean}
function areSimilar(node1, node2) {
    return isNode(node1) && isNode(node2) &&
        !isText(node1) && !isText(node2) &&
        !isComment(node1) && !isComment(node2) &&
        node1.tagName.toLowerCase() === node2.tagName.toLowerCase();
}
// Whether two DOM nodes are text nodes.
function areTextNodes(node1, node2) {
    return isNode(node1) && isNode(node2) &&
        isText(node1) && isText(node2);
}
// Whether two dom nodes should be considered as the same.
// Two nodes which are considered the same are:
//
//		* Text nodes with the same text.
//		* Element nodes represented by the same object.
//		* Two block filler elements.
//
// @private
// @param {String} blockFillerMode Block filler mode, see {@link module:engine/view/domconverter~DomConverter#blockFillerMode}.
// @param {ViewNode} node1
// @param {ViewNode} node2
// @returns {Boolean}
function sameNodes(domConverter, actualDomChild, expectedDomChild) {
    // Elements.
    if (actualDomChild === expectedDomChild) {
        return true;
    }
    // Texts.
    else if (isText(actualDomChild) && isText(expectedDomChild)) {
        return actualDomChild.data === expectedDomChild.data;
    }
    // Block fillers.
    else if (domConverter.isBlockFiller(actualDomChild) &&
        domConverter.isBlockFiller(expectedDomChild)) {
        return true;
    }
    // Not matching types.
    return false;
}
// The following is a Firefox–specific hack (https://github.com/ckeditor/ckeditor5-engine/issues/1439).
// When the native DOM selection is at the end of the block and preceded by <br /> e.g.
//
//		<p>foo<br/>[]</p>
//
// which happens a lot when using the soft line break, the browser fails to (visually) move the
// caret to the new line. A quick fix is as simple as force–refreshing the selection with the same range.
function fixGeckoSelectionAfterBr(focus, domSelection) {
    const parent = focus.parent;
    // This fix works only when the focus point is at the very end of an element.
    // There is no point in running it in cases unrelated to the browser bug.
    if (parent.nodeType != Node.ELEMENT_NODE || focus.offset != parent.childNodes.length - 1) {
        return;
    }
    const childAtOffset = parent.childNodes[focus.offset];
    // To stay on the safe side, the fix being as specific as possible, it targets only the
    // selection which is at the very end of the element and preceded by <br />.
    if (childAtOffset && childAtOffset.tagName == 'BR') {
        domSelection.addRange(domSelection.getRangeAt(0));
    }
}
function filterOutFakeSelectionContainer(domChildList, fakeSelectionContainer) {
    const childList = Array.from(domChildList);
    if (childList.length == 0 || !fakeSelectionContainer) {
        return childList;
    }
    const last = childList[childList.length - 1];
    if (last == fakeSelectionContainer) {
        childList.pop();
    }
    return childList;
}
// Creates a fake selection container for a given document.
//
// @private
// @param {Document} domDocument
// @returns {HTMLElement}
function createFakeSelectionContainer(domDocument) {
    const container = domDocument.createElement('div');
    container.className = 'ck-fake-selection-container';
    Object.assign(container.style, {
        position: 'fixed',
        top: 0,
        left: '-9999px',
        // See https://github.com/ckeditor/ckeditor5/issues/752.
        width: '42px'
    });
    // Fill it with a text node so we can update it later.
    container.textContent = '\u00A0';
    return container;
}
// Checks if text needs to be updated and possibly updates it by removing and inserting only parts
// of the data from the existing text node to reduce impact on the IME composition.
//
// @param {Text} domText DOM text node to update.
// @param {String} expectedText The expected data of a text node.
function updateTextNode(domText, expectedText) {
    const actualText = domText.data;
    if (actualText == expectedText) {
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.info( '%c[Renderer]%c Text node does not need update:',
        // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', '',
        // @if CK_DEBUG_TYPING // 		`"${ domText.data }" (${ domText.data.length })`
        // @if CK_DEBUG_TYPING // 	);
        // @if CK_DEBUG_TYPING // }
        return;
    }
    // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
    // @if CK_DEBUG_TYPING // 	console.info( '%c[Renderer]%c Update text node:',
    // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', '',
    // @if CK_DEBUG_TYPING // 		`"${ domText.data }" (${ domText.data.length }) -> "${ expectedText }" (${ expectedText.length })`
    // @if CK_DEBUG_TYPING // 	);
    // @if CK_DEBUG_TYPING // }
    const actions = fastDiff(actualText, expectedText);
    for (const action of actions) {
        if (action.type === 'insert') {
            domText.insertData(action.index, action.values.join(''));
        }
        else { // 'delete'
            domText.deleteData(action.index, action.howMany);
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/indexof.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/indexof
 */
/**
 * Returns index of the node in the parent element.
 *
 * @param {Node} node Node which index is tested.
 * @returns {Number} Index of the node in the parent element. Returns 0 if node has no parent.
 */
function indexOf(node) {
    let index = 0;
    while (node.previousSibling) {
        node = node.previousSibling;
        index++;
    }
    return index;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/getancestors.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* globals Node */
/**
 * @module utils/dom/getancestors
 */
/**
 * Returns all ancestors of given DOM node, starting from the top-most (root). Includes the given node itself. If the
 * node is a part of `DocumentFragment` that `DocumentFragment` will be returned. In contrary, if the node is
 * appended to a `Document`, that `Document` will not be returned (algorithms operating on DOM tree care for `Document#documentElement`
 * at most, which will be returned).
 *
 * @param {Node} node DOM node.
 * @returns {Array.<Node|DocumentFragment>} Array of given `node` parents.
 */
function getAncestors(node) {
    const nodes = [];
    let currentNode = node;
    // We are interested in `Node`s `DocumentFragment`s only.
    while (currentNode && currentNode.nodeType != Node.DOCUMENT_NODE) {
        nodes.unshift(currentNode);
        currentNode = currentNode.parentNode;
    }
    return nodes;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/domconverter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/domconverter
 */
/* globals Node, NodeFilter, DOMParser, Text */
















const BR_FILLER_REF = BR_FILLER(dom_global.document); // eslint-disable-line new-cap
const NBSP_FILLER_REF = NBSP_FILLER(dom_global.document); // eslint-disable-line new-cap
const MARKED_NBSP_FILLER_REF = MARKED_NBSP_FILLER(dom_global.document); // eslint-disable-line new-cap
const UNSAFE_ATTRIBUTE_NAME_PREFIX = 'data-ck-unsafe-attribute-';
const UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE = 'data-ck-unsafe-element';
/**
 * `DomConverter` is a set of tools to do transformations between DOM nodes and view nodes. It also handles
 * {@link module:engine/view/domconverter~DomConverter#bindElements bindings} between these nodes.
 *
 * An instance of the DOM converter is available under
 * {@link module:engine/view/view~View#domConverter `editor.editing.view.domConverter`}.
 *
 * The DOM converter does not check which nodes should be rendered (use {@link module:engine/view/renderer~Renderer}), does not keep the
 * state of a tree nor keeps the synchronization between the tree view and the DOM tree (use {@link module:engine/view/document~Document}).
 *
 * The DOM converter keeps DOM elements to view element bindings, so when the converter gets destroyed, the bindings are lost.
 * Two converters will keep separate binding maps, so one tree view can be bound with two DOM trees.
 */
class DomConverter {
    /**
     * Creates a DOM converter.
     *
     * @param {module:engine/view/document~Document} document The view document instance.
     * @param {Object} options An object with configuration options.
     * @param {module:engine/view/filler~BlockFillerMode} [options.blockFillerMode] The type of the block filler to use.
     * Default value depends on the options.renderingMode:
     *  'nbsp' when options.renderingMode == 'data',
     *  'br' when options.renderingMode == 'editing'.
     * @param {'data'|'editing'} [options.renderingMode='editing'] Whether to leave the View-to-DOM conversion result unchanged
     * or improve editing experience by filtering out interactive data.
     */
    constructor(document, options = {}) {
        /**
         * @readonly
         * @type {module:engine/view/document~Document}
         */
        this.document = document;
        /**
         * Whether to leave the View-to-DOM conversion result unchanged or improve editing experience by filtering out interactive data.
         *
         * @member {'data'|'editing'} module:engine/view/domconverter~DomConverter#renderingMode
         */
        this.renderingMode = options.renderingMode || 'editing';
        /**
         * The mode of a block filler used by the DOM converter.
         *
         * @member {'br'|'nbsp'|'markedNbsp'} module:engine/view/domconverter~DomConverter#blockFillerMode
         */
        this.blockFillerMode = options.blockFillerMode || (this.renderingMode === 'editing' ? 'br' : 'nbsp');
        /**
         * Elements which are considered pre-formatted elements.
         *
         * @readonly
         * @member {Array.<String>} module:engine/view/domconverter~DomConverter#preElements
         */
        this.preElements = ['pre'];
        /**
         * Elements which are considered block elements (and hence should be filled with a
         * {@link #isBlockFiller block filler}).
         *
         * Whether an element is considered a block element also affects handling of trailing whitespaces.
         *
         * You can extend this array if you introduce support for block elements which are not yet recognized here.
         *
         * @readonly
         * @member {Array.<String>} module:engine/view/domconverter~DomConverter#blockElements
         */
        this.blockElements = [
            'address', 'article', 'aside', 'blockquote', 'caption', 'center', 'dd', 'details', 'dir', 'div',
            'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header',
            'hgroup', 'legend', 'li', 'main', 'menu', 'nav', 'ol', 'p', 'pre', 'section', 'summary', 'table', 'tbody',
            'td', 'tfoot', 'th', 'thead', 'tr', 'ul'
        ];
        /**
         * A list of elements that exist inline (in text) but their inner structure cannot be edited because
         * of the way they are rendered by the browser. They are mostly HTML form elements but there are other
         * elements such as `<img>` or `<iframe>` that also have non-editable children or no children whatsoever.
         *
         * Whether an element is considered an inline object has an impact on white space rendering (trimming)
         * around (and inside of it). In short, white spaces in text nodes next to inline objects are not trimmed.
         *
         * You can extend this array if you introduce support for inline object elements which are not yet recognized here.
         *
         * @readonly
         * @member {Array.<String>} module:engine/view/domconverter~DomConverter#inlineObjectElements
         */
        this.inlineObjectElements = [
            'object', 'iframe', 'input', 'button', 'textarea', 'select', 'option', 'video', 'embed', 'audio', 'img', 'canvas'
        ];
        /**
         * A list of elements which may affect the editing experience. To avoid this, those elements are replaced with
         * `<span data-ck-unsafe-element="[element name]"></span>` while rendering in the editing mode.
         *
         * @readonly
         * @member {Array.<String>} module:engine/view/domconverter~DomConverter#unsafeElements
         */
        this.unsafeElements = ['script', 'style'];
        /**
         * The DOM Document used to create DOM nodes.
         *
         * @type {Document}
         * @private
         */
        this._domDocument = this.renderingMode === 'editing' ? dom_global.document : dom_global.document.implementation.createHTMLDocument('');
        /**
         * The DOM-to-view mapping.
         *
         * @private
         * @member {WeakMap} module:engine/view/domconverter~DomConverter#_domToViewMapping
         */
        this._domToViewMapping = new WeakMap();
        /**
         * The view-to-DOM mapping.
         *
         * @private
         * @member {WeakMap} module:engine/view/domconverter~DomConverter#_viewToDomMapping
         */
        this._viewToDomMapping = new WeakMap();
        /**
         * Holds the mapping between fake selection containers and corresponding view selections.
         *
         * @private
         * @member {WeakMap} module:engine/view/domconverter~DomConverter#_fakeSelectionMapping
         */
        this._fakeSelectionMapping = new WeakMap();
        /**
         * Matcher for view elements whose content should be treated as raw data
         * and not processed during the conversion from DOM nodes to view elements.
         *
         * @private
         * @type {module:engine/view/matcher~Matcher}
         */
        this._rawContentElementMatcher = new Matcher();
        /**
         * A set of encountered raw content DOM nodes. It is used for preventing left trimming of the following text node.
         *
         * @private
         * @type {WeakSet.<Node>}
         */
        this._encounteredRawContentDomNodes = new WeakSet();
    }
    /**
     * Binds a given DOM element that represents fake selection to a **position** of a
     * {@link module:engine/view/documentselection~DocumentSelection document selection}.
     * Document selection copy is stored and can be retrieved by the
     * {@link module:engine/view/domconverter~DomConverter#fakeSelectionToView} method.
     *
     * @param {HTMLElement} domElement
     * @param {module:engine/view/documentselection~DocumentSelection} viewDocumentSelection
     */
    bindFakeSelection(domElement, viewDocumentSelection) {
        this._fakeSelectionMapping.set(domElement, new Selection(viewDocumentSelection));
    }
    /**
     * Returns a {@link module:engine/view/selection~Selection view selection} instance corresponding to a given
     * DOM element that represents fake selection. Returns `undefined` if binding to the given DOM element does not exist.
     *
     * @param {HTMLElement} domElement
     * @returns {module:engine/view/selection~Selection|undefined}
     */
    fakeSelectionToView(domElement) {
        return this._fakeSelectionMapping.get(domElement);
    }
    /**
     * Binds DOM and view elements, so it will be possible to get corresponding elements using
     * {@link module:engine/view/domconverter~DomConverter#mapDomToView} and
     * {@link module:engine/view/domconverter~DomConverter#mapViewToDom}.
     *
     * @param {HTMLElement} domElement The DOM element to bind.
     * @param {module:engine/view/element~Element} viewElement The view element to bind.
     */
    bindElements(domElement, viewElement) {
        this._domToViewMapping.set(domElement, viewElement);
        this._viewToDomMapping.set(viewElement, domElement);
    }
    /**
     * Unbinds a given DOM element from the view element it was bound to. Unbinding is deep, meaning that all children of
     * the DOM element will be unbound too.
     *
     * @param {HTMLElement} domElement The DOM element to unbind.
     */
    unbindDomElement(domElement) {
        const viewElement = this._domToViewMapping.get(domElement);
        if (viewElement) {
            this._domToViewMapping.delete(domElement);
            this._viewToDomMapping.delete(viewElement);
            for (const child of Array.from(domElement.children)) {
                this.unbindDomElement(child);
            }
        }
    }
    /**
     * Binds DOM and view document fragments, so it will be possible to get corresponding document fragments using
     * {@link module:engine/view/domconverter~DomConverter#mapDomToView} and
     * {@link module:engine/view/domconverter~DomConverter#mapViewToDom}.
     *
     * @param {DocumentFragment} domFragment The DOM document fragment to bind.
     * @param {module:engine/view/documentfragment~DocumentFragment} viewFragment The view document fragment to bind.
     */
    bindDocumentFragments(domFragment, viewFragment) {
        this._domToViewMapping.set(domFragment, viewFragment);
        this._viewToDomMapping.set(viewFragment, domFragment);
    }
    /**
     * Decides whether a given pair of attribute key and value should be passed further down the pipeline.
     *
     * @param {String} attributeKey
     * @param {String} attributeValue
     * @param {String} elementName Element name in lower case.
     * @returns {Boolean}
     */
    shouldRenderAttribute(attributeKey, attributeValue, elementName) {
        if (this.renderingMode === 'data') {
            return true;
        }
        attributeKey = attributeKey.toLowerCase();
        if (attributeKey.startsWith('on')) {
            return false;
        }
        if (attributeKey === 'srcdoc' &&
            attributeValue.match(/\bon\S+\s*=|javascript:|<\s*\/*script/i)) {
            return false;
        }
        if (elementName === 'img' &&
            (attributeKey === 'src' || attributeKey === 'srcset')) {
            return true;
        }
        if (elementName === 'source' && attributeKey === 'srcset') {
            return true;
        }
        if (attributeValue.match(/^\s*(javascript:|data:(image\/svg|text\/x?html))/i)) {
            return false;
        }
        return true;
    }
    /**
     * Set `domElement`'s content using provided `html` argument. Apply necessary filtering for the editing pipeline.
     *
     * @param {Element} domElement DOM element that should have `html` set as its content.
     * @param {String} html Textual representation of the HTML that will be set on `domElement`.
     */
    setContentOf(domElement, html) {
        // For data pipeline we pass the HTML as-is.
        if (this.renderingMode === 'data') {
            domElement.innerHTML = html;
            return;
        }
        const document = new DOMParser().parseFromString(html, 'text/html');
        const fragment = document.createDocumentFragment();
        const bodyChildNodes = document.body.childNodes;
        while (bodyChildNodes.length > 0) {
            fragment.appendChild(bodyChildNodes[0]);
        }
        const treeWalker = document.createTreeWalker(fragment, NodeFilter.SHOW_ELEMENT);
        const nodes = [];
        let currentNode;
        // eslint-disable-next-line no-cond-assign
        while (currentNode = treeWalker.nextNode()) {
            nodes.push(currentNode);
        }
        for (const currentNode of nodes) {
            // Go through nodes to remove those that are prohibited in editing pipeline.
            for (const attributeName of currentNode.getAttributeNames()) {
                this.setDomElementAttribute(currentNode, attributeName, currentNode.getAttribute(attributeName));
            }
            const elementName = currentNode.tagName.toLowerCase();
            // There are certain nodes, that should be renamed to <span> in editing pipeline.
            if (this._shouldRenameElement(elementName)) {
                _logUnsafeElement(elementName);
                currentNode.replaceWith(this._createReplacementDomElement(elementName, currentNode));
            }
        }
        // Empty the target element.
        while (domElement.firstChild) {
            domElement.firstChild.remove();
        }
        domElement.append(fragment);
    }
    /**
     * Converts the view to the DOM. For all text nodes, not bound elements and document fragments new items will
     * be created. For bound elements and document fragments the method will return corresponding items.
     *
     * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} viewNode
     * View node or document fragment to transform.
     * @param {Object} [options] Conversion options.
     * @param {Boolean} [options.bind=false] Determines whether new elements will be bound.
     * @param {Boolean} [options.withChildren=true] If `true`, node's and document fragment's children will be converted too.
     * @returns {Node|DocumentFragment} Converted node or DocumentFragment.
     */
    viewToDom(viewNode, options = {}) {
        if (viewNode.is('$text')) {
            const textData = this._processDataFromViewText(viewNode);
            return this._domDocument.createTextNode(textData);
        }
        else {
            if (this.mapViewToDom(viewNode)) {
                return this.mapViewToDom(viewNode);
            }
            let domElement;
            if (viewNode.is('documentFragment')) {
                // Create DOM document fragment.
                domElement = this._domDocument.createDocumentFragment();
                if (options.bind) {
                    this.bindDocumentFragments(domElement, viewNode);
                }
            }
            else if (viewNode.is('uiElement')) {
                if (viewNode.name === '$comment') {
                    domElement = this._domDocument.createComment(viewNode.getCustomProperty('$rawContent'));
                }
                else {
                    // UIElement has its own render() method (see #799).
                    domElement = viewNode.render(this._domDocument, this);
                }
                if (options.bind) {
                    this.bindElements(domElement, viewNode);
                }
                return domElement;
            }
            else {
                // Create DOM element.
                if (this._shouldRenameElement(viewNode.name)) {
                    _logUnsafeElement(viewNode.name);
                    domElement = this._createReplacementDomElement(viewNode.name);
                }
                else if (viewNode.hasAttribute('xmlns')) {
                    domElement = this._domDocument.createElementNS(viewNode.getAttribute('xmlns'), viewNode.name);
                }
                else {
                    domElement = this._domDocument.createElement(viewNode.name);
                }
                // RawElement take care of their children in RawElement#render() method which can be customized
                // (see https://github.com/ckeditor/ckeditor5/issues/4469).
                if (viewNode.is('rawElement')) {
                    viewNode.render(domElement, this);
                }
                if (options.bind) {
                    this.bindElements(domElement, viewNode);
                }
                // Copy element's attributes.
                for (const key of viewNode.getAttributeKeys()) {
                    this.setDomElementAttribute(domElement, key, viewNode.getAttribute(key), viewNode);
                }
            }
            if (options.withChildren !== false) {
                for (const child of this.viewChildrenToDom(viewNode, options)) {
                    domElement.appendChild(child);
                }
            }
            return domElement;
        }
    }
    /**
     * Sets the attribute on a DOM element.
     *
     * **Note**: To remove the attribute, use {@link #removeDomElementAttribute}.
     *
     * @param {HTMLElement} domElement The DOM element the attribute should be set on.
     * @param {String} key The name of the attribute.
     * @param {String} value The value of the attribute.
     * @param {module:engine/view/element~Element} [relatedViewElement] The view element related to the `domElement` (if there is any).
     * It helps decide whether the attribute set is unsafe. For instance, view elements created via the
     * {@link module:engine/view/downcastwriter~DowncastWriter} methods can allow certain attributes that would normally be filtered out.
     */
    setDomElementAttribute(domElement, key, value, relatedViewElement) {
        const shouldRenderAttribute = this.shouldRenderAttribute(key, value, domElement.tagName.toLowerCase()) ||
            relatedViewElement && relatedViewElement.shouldRenderUnsafeAttribute(key);
        if (!shouldRenderAttribute) {
            logWarning('domconverter-unsafe-attribute-detected', { domElement, key, value });
        }
        // The old value was safe but the new value is unsafe.
        if (domElement.hasAttribute(key) && !shouldRenderAttribute) {
            domElement.removeAttribute(key);
        }
        // The old value was unsafe (but prefixed) but the new value will be safe (will be unprefixed).
        else if (domElement.hasAttribute(UNSAFE_ATTRIBUTE_NAME_PREFIX + key) && shouldRenderAttribute) {
            domElement.removeAttribute(UNSAFE_ATTRIBUTE_NAME_PREFIX + key);
        }
        // If the attribute should not be rendered, rename it (instead of removing) to give developers some idea of what
        // is going on (https://github.com/ckeditor/ckeditor5/issues/10801).
        domElement.setAttribute(shouldRenderAttribute ? key : UNSAFE_ATTRIBUTE_NAME_PREFIX + key, value);
    }
    /**
     * Removes an attribute from a DOM element.
     *
     * **Note**: To set the attribute, use {@link #setDomElementAttribute}.
     *
     * @param {HTMLElement} domElement The DOM element the attribute should be removed from.
     * @param {String} key The name of the attribute.
     */
    removeDomElementAttribute(domElement, key) {
        // See #_createReplacementDomElement() to learn what this is.
        if (key == UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE) {
            return;
        }
        domElement.removeAttribute(key);
        // See setDomElementAttribute() to learn what this is.
        domElement.removeAttribute(UNSAFE_ATTRIBUTE_NAME_PREFIX + key);
    }
    /**
     * Converts children of the view element to DOM using the
     * {@link module:engine/view/domconverter~DomConverter#viewToDom} method.
     * Additionally, this method adds block {@link module:engine/view/filler filler} to the list of children, if needed.
     *
     * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} viewElement Parent view element.
     * @param {Object} options See {@link module:engine/view/domconverter~DomConverter#viewToDom} options parameter.
     * @returns {Iterable.<Node>} DOM nodes.
     */
    *viewChildrenToDom(viewElement, options = {}) {
        const fillerPositionOffset = viewElement.getFillerOffset && viewElement.getFillerOffset();
        let offset = 0;
        for (const childView of viewElement.getChildren()) {
            if (fillerPositionOffset === offset) {
                yield this._getBlockFiller();
            }
            const transparentRendering = childView.is('element') &&
                childView.getCustomProperty('dataPipeline:transparentRendering');
            if (transparentRendering && this.renderingMode == 'data') {
                yield* this.viewChildrenToDom(childView, options);
            }
            else {
                if (transparentRendering) {
                    /**
                     * The `dataPipeline:transparentRendering` flag is supported only in the data pipeline.
                     *
                     * @error domconverter-transparent-rendering-unsupported-in-editing-pipeline
                     */
                    logWarning('domconverter-transparent-rendering-unsupported-in-editing-pipeline', { viewElement: childView });
                }
                yield this.viewToDom(childView, options);
            }
            offset++;
        }
        if (fillerPositionOffset === offset) {
            yield this._getBlockFiller();
        }
    }
    /**
     * Converts view {@link module:engine/view/range~Range} to DOM range.
     * Inline and block {@link module:engine/view/filler fillers} are handled during the conversion.
     *
     * @param {module:engine/view/range~Range} viewRange View range.
     * @returns {Range} DOM range.
     */
    viewRangeToDom(viewRange) {
        const domStart = this.viewPositionToDom(viewRange.start);
        const domEnd = this.viewPositionToDom(viewRange.end);
        const domRange = this._domDocument.createRange();
        domRange.setStart(domStart.parent, domStart.offset);
        domRange.setEnd(domEnd.parent, domEnd.offset);
        return domRange;
    }
    /**
     * Converts view {@link module:engine/view/position~Position} to DOM parent and offset.
     *
     * Inline and block {@link module:engine/view/filler fillers} are handled during the conversion.
     * If the converted position is directly before inline filler it is moved inside the filler.
     *
     * @param {module:engine/view/position~Position} viewPosition View position.
     * @returns {Object|null} position DOM position or `null` if view position could not be converted to DOM.
     * @returns {Node} position.parent DOM position parent.
     * @returns {Number} position.offset DOM position offset.
     */
    viewPositionToDom(viewPosition) {
        const viewParent = viewPosition.parent;
        if (viewParent.is('$text')) {
            const domParent = this.findCorrespondingDomText(viewParent);
            if (!domParent) {
                // Position is in a view text node that has not been rendered to DOM yet.
                return null;
            }
            let offset = viewPosition.offset;
            if (startsWithFiller(domParent)) {
                offset += INLINE_FILLER_LENGTH;
            }
            return { parent: domParent, offset };
        }
        else {
            // viewParent is instance of ViewElement.
            let domParent, domBefore, domAfter;
            if (viewPosition.offset === 0) {
                domParent = this.mapViewToDom(viewParent);
                if (!domParent) {
                    // Position is in a view element that has not been rendered to DOM yet.
                    return null;
                }
                domAfter = domParent.childNodes[0];
            }
            else {
                const nodeBefore = viewPosition.nodeBefore;
                domBefore = nodeBefore.is('$text') ?
                    this.findCorrespondingDomText(nodeBefore) :
                    this.mapViewToDom(nodeBefore);
                if (!domBefore) {
                    // Position is after a view element that has not been rendered to DOM yet.
                    return null;
                }
                domParent = domBefore.parentNode;
                domAfter = domBefore.nextSibling;
            }
            // If there is an inline filler at position return position inside the filler. We should never return
            // the position before the inline filler.
            if (isText(domAfter) && startsWithFiller(domAfter)) {
                return { parent: domAfter, offset: INLINE_FILLER_LENGTH };
            }
            const offset = domBefore ? indexOf(domBefore) + 1 : 0;
            return { parent: domParent, offset };
        }
    }
    /**
     * Converts DOM to view. For all text nodes, not bound elements and document fragments new items will
     * be created. For bound elements and document fragments function will return corresponding items. For
     * {@link module:engine/view/filler fillers} `null` will be returned.
     * For all DOM elements rendered by {@link module:engine/view/uielement~UIElement} that UIElement will be returned.
     *
     * @param {Node|DocumentFragment} domNode DOM node or document fragment to transform.
     * @param {Object} [options] Conversion options.
     * @param {Boolean} [options.bind=false] Determines whether new elements will be bound.
     * @param {Boolean} [options.withChildren=true] If `true`, node's and document fragment's children will be converted too.
     * @param {Boolean} [options.keepOriginalCase=false] If `false`, node's tag name will be converted to lower case.
     * @param {Boolean} [options.skipComments=false] If `false`, comment nodes will be converted to `$comment`
     * {@link module:engine/view/uielement~UIElement view UI elements}.
     * @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment|null} Converted node or document fragment
     * or `null` if DOM node is a {@link module:engine/view/filler filler} or the given node is an empty text node.
     */
    domToView(domNode, options = {}) {
        if (this.isBlockFiller(domNode)) {
            return null;
        }
        // When node is inside a UIElement or a RawElement return that parent as it's view representation.
        const hostElement = this.getHostViewElement(domNode);
        if (hostElement) {
            return hostElement;
        }
        if (isComment(domNode) && options.skipComments) {
            return null;
        }
        if (isText(domNode)) {
            if (isInlineFiller(domNode)) {
                return null;
            }
            else {
                const textData = this._processDataFromDomText(domNode);
                return textData === '' ? null : new text_Text(this.document, textData);
            }
        }
        else {
            if (this.mapDomToView(domNode)) {
                return this.mapDomToView(domNode);
            }
            let viewElement;
            if (this.isDocumentFragment(domNode)) {
                // Create view document fragment.
                viewElement = new DocumentFragment(this.document);
                if (options.bind) {
                    this.bindDocumentFragments(domNode, viewElement);
                }
            }
            else {
                // Create view element.
                viewElement = this._createViewElement(domNode, options);
                if (options.bind) {
                    this.bindElements(domNode, viewElement);
                }
                // Copy element's attributes.
                const attrs = domNode.attributes;
                if (attrs) {
                    for (let l = attrs.length, i = 0; i < l; i++) {
                        viewElement._setAttribute(attrs[i].name, attrs[i].value);
                    }
                }
                // Treat this element's content as a raw data if it was registered as such.
                // Comment node is also treated as an element with raw data.
                if (this._isViewElementWithRawContent(viewElement, options) || isComment(domNode)) {
                    const rawContent = isComment(domNode) ? domNode.data : domNode.innerHTML;
                    viewElement._setCustomProperty('$rawContent', rawContent);
                    // Store a DOM node to prevent left trimming of the following text node.
                    this._encounteredRawContentDomNodes.add(domNode);
                    return viewElement;
                }
            }
            if (options.withChildren !== false) {
                for (const child of this.domChildrenToView(domNode, options)) {
                    viewElement._appendChild(child);
                }
            }
            return viewElement;
        }
    }
    /**
     * Converts children of the DOM element to view nodes using
     * the {@link module:engine/view/domconverter~DomConverter#domToView} method.
     * Additionally this method omits block {@link module:engine/view/filler filler}, if it exists in the DOM parent.
     *
     * @param {HTMLElement} domElement Parent DOM element.
     * @param {Object} options See {@link module:engine/view/domconverter~DomConverter#domToView} options parameter.
     * @returns {Iterable.<module:engine/view/node~Node>} View nodes.
     */
    *domChildrenToView(domElement, options) {
        for (let i = 0; i < domElement.childNodes.length; i++) {
            const domChild = domElement.childNodes[i];
            const viewChild = this.domToView(domChild, options);
            if (viewChild !== null) {
                yield viewChild;
            }
        }
    }
    /**
     * Converts DOM selection to view {@link module:engine/view/selection~Selection}.
     * Ranges which cannot be converted will be omitted.
     *
     * @param {Selection} domSelection DOM selection.
     * @returns {module:engine/view/selection~Selection} View selection.
     */
    domSelectionToView(domSelection) {
        // DOM selection might be placed in fake selection container.
        // If container contains fake selection - return corresponding view selection.
        if (domSelection.rangeCount === 1) {
            let container = domSelection.getRangeAt(0).startContainer;
            // The DOM selection might be moved to the text node inside the fake selection container.
            if (isText(container)) {
                container = container.parentNode;
            }
            const viewSelection = this.fakeSelectionToView(container);
            if (viewSelection) {
                return viewSelection;
            }
        }
        const isBackward = this.isDomSelectionBackward(domSelection);
        const viewRanges = [];
        for (let i = 0; i < domSelection.rangeCount; i++) {
            // DOM Range have correct start and end, no matter what is the DOM Selection direction. So we don't have to fix anything.
            const domRange = domSelection.getRangeAt(i);
            const viewRange = this.domRangeToView(domRange);
            if (viewRange) {
                viewRanges.push(viewRange);
            }
        }
        return new Selection(viewRanges, { backward: isBackward });
    }
    /**
     * Converts DOM Range to view {@link module:engine/view/range~Range}.
     * If the start or end position can not be converted `null` is returned.
     *
     * @param {Range} domRange DOM range.
     * @returns {module:engine/view/range~Range|null} View range.
     */
    domRangeToView(domRange) {
        const viewStart = this.domPositionToView(domRange.startContainer, domRange.startOffset);
        const viewEnd = this.domPositionToView(domRange.endContainer, domRange.endOffset);
        if (viewStart && viewEnd) {
            return new Range(viewStart, viewEnd);
        }
        return null;
    }
    /**
     * Converts DOM parent and offset to view {@link module:engine/view/position~Position}.
     *
     * If the position is inside a {@link module:engine/view/filler filler} which has no corresponding view node,
     * position of the filler will be converted and returned.
     *
     * If the position is inside DOM element rendered by {@link module:engine/view/uielement~UIElement}
     * that position will be converted to view position before that UIElement.
     *
     * If structures are too different and it is not possible to find corresponding position then `null` will be returned.
     *
     * @param {Node} domParent DOM position parent.
     * @param {Number} [domOffset=0] DOM position offset. You can skip it when converting the inline filler node.
     * @returns {module:engine/view/position~Position} viewPosition View position.
     */
    domPositionToView(domParent, domOffset = 0) {
        if (this.isBlockFiller(domParent)) {
            return this.domPositionToView(domParent.parentNode, indexOf(domParent));
        }
        // If position is somewhere inside UIElement or a RawElement - return position before that element.
        const viewElement = this.mapDomToView(domParent);
        if (viewElement && (viewElement.is('uiElement') || viewElement.is('rawElement'))) {
            return Position._createBefore(viewElement);
        }
        if (isText(domParent)) {
            if (isInlineFiller(domParent)) {
                return this.domPositionToView(domParent.parentNode, indexOf(domParent));
            }
            const viewParent = this.findCorrespondingViewText(domParent);
            let offset = domOffset;
            if (!viewParent) {
                return null;
            }
            if (startsWithFiller(domParent)) {
                offset -= INLINE_FILLER_LENGTH;
                offset = offset < 0 ? 0 : offset;
            }
            return new Position(viewParent, offset);
        }
        // domParent instanceof HTMLElement.
        else {
            if (domOffset === 0) {
                const viewParent = this.mapDomToView(domParent);
                if (viewParent) {
                    return new Position(viewParent, 0);
                }
            }
            else {
                const domBefore = domParent.childNodes[domOffset - 1];
                if (isText(domBefore) && isInlineFiller(domBefore)) {
                    return this.domPositionToView(domBefore.parentNode, indexOf(domBefore));
                }
                const viewBefore = isText(domBefore) ?
                    this.findCorrespondingViewText(domBefore) :
                    this.mapDomToView(domBefore);
                // TODO #663
                if (viewBefore && viewBefore.parent) {
                    return new Position(viewBefore.parent, viewBefore.index + 1);
                }
            }
            return null;
        }
    }
    /**
     * Returns corresponding view {@link module:engine/view/element~Element Element} or
     * {@link module:engine/view/documentfragment~DocumentFragment} for provided DOM element or
     * document fragment. If there is no view item {@link module:engine/view/domconverter~DomConverter#bindElements bound}
     * to the given DOM - `undefined` is returned.
     *
     * For all DOM elements rendered by a {@link module:engine/view/uielement~UIElement} or
     * a {@link module:engine/view/rawelement~RawElement}, the parent `UIElement` or `RawElement` will be returned.
     *
     * @param {DocumentFragment|Element} domElementOrDocumentFragment DOM element or document fragment.
     * @returns {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment|undefined}
     * Corresponding view element, document fragment or `undefined` if no element was bound.
     */
    mapDomToView(domElementOrDocumentFragment) {
        const hostElement = this.getHostViewElement(domElementOrDocumentFragment);
        return hostElement || this._domToViewMapping.get(domElementOrDocumentFragment);
    }
    /**
     * Finds corresponding text node. Text nodes are not {@link module:engine/view/domconverter~DomConverter#bindElements bound},
     * corresponding text node is returned based on the sibling or parent.
     *
     * If the directly previous sibling is a {@link module:engine/view/domconverter~DomConverter#bindElements bound} element, it is used
     * to find the corresponding text node.
     *
     * If this is a first child in the parent and the parent is a {@link module:engine/view/domconverter~DomConverter#bindElements bound}
     * element, it is used to find the corresponding text node.
     *
     * For all text nodes rendered by a {@link module:engine/view/uielement~UIElement} or
     * a {@link module:engine/view/rawelement~RawElement}, the parent `UIElement` or `RawElement` will be returned.
     *
     * Otherwise `null` is returned.
     *
     * Note that for the block or inline {@link module:engine/view/filler filler} this method returns `null`.
     *
     * @param {Text} domText DOM text node.
     * @returns {module:engine/view/text~Text|null} Corresponding view text node or `null`, if it was not possible to find a
     * corresponding node.
     */
    findCorrespondingViewText(domText) {
        if (isInlineFiller(domText)) {
            return null;
        }
        // If DOM text was rendered by a UIElement or a RawElement - return this parent element.
        const hostElement = this.getHostViewElement(domText);
        if (hostElement) {
            return hostElement;
        }
        const previousSibling = domText.previousSibling;
        // Try to use previous sibling to find the corresponding text node.
        if (previousSibling) {
            if (!(this.isElement(previousSibling))) {
                // The previous is text or comment.
                return null;
            }
            const viewElement = this.mapDomToView(previousSibling);
            if (viewElement) {
                const nextSibling = viewElement.nextSibling;
                // It might be filler which has no corresponding view node.
                if (nextSibling instanceof text_Text) {
                    return nextSibling;
                }
                else {
                    return null;
                }
            }
        }
        // Try to use parent to find the corresponding text node.
        else {
            const viewElement = this.mapDomToView(domText.parentNode);
            if (viewElement) {
                const firstChild = viewElement.getChild(0);
                // It might be filler which has no corresponding view node.
                if (firstChild instanceof text_Text) {
                    return firstChild;
                }
                else {
                    return null;
                }
            }
        }
        return null;
    }
    /**
     * Returns corresponding DOM item for provided {@link module:engine/view/element~Element Element} or
     * {@link module:engine/view/documentfragment~DocumentFragment DocumentFragment}.
     * To find a corresponding text for {@link module:engine/view/text~Text view Text instance}
     * use {@link #findCorrespondingDomText}.
     *
     * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} viewNode
     * View element or document fragment.
     * @returns {Node|DocumentFragment|undefined} Corresponding DOM node or document fragment.
     */
    mapViewToDom(documentFragmentOrElement) {
        return this._viewToDomMapping.get(documentFragmentOrElement);
    }
    /**
     * Finds corresponding text node. Text nodes are not {@link module:engine/view/domconverter~DomConverter#bindElements bound},
     * corresponding text node is returned based on the sibling or parent.
     *
     * If the directly previous sibling is a {@link module:engine/view/domconverter~DomConverter#bindElements bound} element, it is used
     * to find the corresponding text node.
     *
     * If this is a first child in the parent and the parent is a {@link module:engine/view/domconverter~DomConverter#bindElements bound}
     * element, it is used to find the corresponding text node.
     *
     * Otherwise `null` is returned.
     *
     * @param {module:engine/view/text~Text} viewText View text node.
     * @returns {Text|null} Corresponding DOM text node or `null`, if it was not possible to find a corresponding node.
     */
    findCorrespondingDomText(viewText) {
        const previousSibling = viewText.previousSibling;
        // Try to use previous sibling to find the corresponding text node.
        if (previousSibling && this.mapViewToDom(previousSibling)) {
            return this.mapViewToDom(previousSibling).nextSibling;
        }
        // If this is a first node, try to use parent to find the corresponding text node.
        if (!previousSibling && viewText.parent && this.mapViewToDom(viewText.parent)) {
            return this.mapViewToDom(viewText.parent).childNodes[0];
        }
        return null;
    }
    /**
     * Focuses DOM editable that is corresponding to provided {@link module:engine/view/editableelement~EditableElement}.
     *
     * @param {module:engine/view/editableelement~EditableElement} viewEditable
     */
    focus(viewEditable) {
        const domEditable = this.mapViewToDom(viewEditable);
        if (domEditable && domEditable.ownerDocument.activeElement !== domEditable) {
            // Save the scrollX and scrollY positions before the focus.
            const { scrollX, scrollY } = dom_global.window;
            const scrollPositions = [];
            // Save all scrollLeft and scrollTop values starting from domEditable up to
            // document#documentElement.
            forEachDomElementAncestor(domEditable, node => {
                const { scrollLeft, scrollTop } = node;
                scrollPositions.push([scrollLeft, scrollTop]);
            });
            domEditable.focus();
            // Restore scrollLeft and scrollTop values starting from domEditable up to
            // document#documentElement.
            // https://github.com/ckeditor/ckeditor5-engine/issues/951
            // https://github.com/ckeditor/ckeditor5-engine/issues/957
            forEachDomElementAncestor(domEditable, node => {
                const [scrollLeft, scrollTop] = scrollPositions.shift();
                node.scrollLeft = scrollLeft;
                node.scrollTop = scrollTop;
            });
            // Restore the scrollX and scrollY positions after the focus.
            // https://github.com/ckeditor/ckeditor5-engine/issues/951
            dom_global.window.scrollTo(scrollX, scrollY);
        }
    }
    /**
     * Returns `true` when `node.nodeType` equals `Node.ELEMENT_NODE`.
     *
     * @param {Node} node Node to check.
     * @returns {Boolean}
     */
    isElement(node) {
        return node && node.nodeType == Node.ELEMENT_NODE;
    }
    /**
     * Returns `true` when `node.nodeType` equals `Node.DOCUMENT_FRAGMENT_NODE`.
     *
     * @param {Node} node Node to check.
     * @returns {Boolean}
     */
    isDocumentFragment(node) {
        return node && node.nodeType == Node.DOCUMENT_FRAGMENT_NODE;
    }
    /**
     * Checks if the node is an instance of the block filler for this DOM converter.
     *
     *		const converter = new DomConverter( viewDocument, { blockFillerMode: 'br' } );
     *
     *		converter.isBlockFiller( BR_FILLER( document ) ); // true
     *		converter.isBlockFiller( NBSP_FILLER( document ) ); // false
     *
     * **Note:**: For the `'nbsp'` mode the method also checks context of a node so it cannot be a detached node.
     *
     * **Note:** A special case in the `'nbsp'` mode exists where the `<br>` in `<p><br></p>` is treated as a block filler.
     *
     * @param {Node} domNode DOM node to check.
     * @returns {Boolean} True if a node is considered a block filler for given mode.
     */
    isBlockFiller(domNode) {
        if (this.blockFillerMode == 'br') {
            return domNode.isEqualNode(BR_FILLER_REF);
        }
        // Special case for <p><br></p> in which <br> should be treated as filler even when we are not in the 'br' mode. See ckeditor5#5564.
        if (domNode.tagName === 'BR' &&
            hasBlockParent(domNode, this.blockElements) &&
            domNode.parentNode.childNodes.length === 1) {
            return true;
        }
        // If not in 'br' mode, try recognizing both marked and regular nbsp block fillers.
        return domNode.isEqualNode(MARKED_NBSP_FILLER_REF) || isNbspBlockFiller(domNode, this.blockElements);
    }
    /**
     * Returns `true` if given selection is a backward selection, that is, if it's `focus` is before `anchor`.
     *
     * @param {Selection} DOM Selection instance to check.
     * @returns {Boolean}
     */
    isDomSelectionBackward(selection) {
        if (selection.isCollapsed) {
            return false;
        }
        // Since it takes multiple lines of code to check whether a "DOM Position" is before/after another "DOM Position",
        // we will use the fact that range will collapse if it's end is before it's start.
        const range = this._domDocument.createRange();
        try {
            range.setStart(selection.anchorNode, selection.anchorOffset);
            range.setEnd(selection.focusNode, selection.focusOffset);
        }
        catch (e) {
            // Safari sometimes gives us a selection that makes Range.set{Start,End} throw.
            // See https://github.com/ckeditor/ckeditor5/issues/12375.
            return false;
        }
        const backward = range.collapsed;
        range.detach();
        return backward;
    }
    /**
     * Returns a parent {@link module:engine/view/uielement~UIElement} or {@link module:engine/view/rawelement~RawElement}
     * that hosts the provided DOM node. Returns `null` if there is no such parent.
     *
     * @param {Node} domNode
     * @returns {module:engine/view/uielement~UIElement|module:engine/view/rawelement~RawElement|null}
     */
    getHostViewElement(domNode) {
        const ancestors = getAncestors(domNode);
        // Remove domNode from the list.
        ancestors.pop();
        while (ancestors.length) {
            const domNode = ancestors.pop();
            const viewNode = this._domToViewMapping.get(domNode);
            if (viewNode && (viewNode.is('uiElement') || viewNode.is('rawElement'))) {
                return viewNode;
            }
        }
        return null;
    }
    /**
     * Checks if the given selection's boundaries are at correct places.
     *
     * The following places are considered as incorrect for selection boundaries:
     *
     * * before or in the middle of an inline filler sequence,
     * * inside a DOM element which represents {@link module:engine/view/uielement~UIElement a view UI element},
     * * inside a DOM element which represents {@link module:engine/view/rawelement~RawElement a view raw element}.
     *
     * @param {Selection} domSelection The DOM selection object to be checked.
     * @returns {Boolean} `true` if the given selection is at a correct place, `false` otherwise.
     */
    isDomSelectionCorrect(domSelection) {
        return this._isDomSelectionPositionCorrect(domSelection.anchorNode, domSelection.anchorOffset) &&
            this._isDomSelectionPositionCorrect(domSelection.focusNode, domSelection.focusOffset);
    }
    /**
     * Registers a {@link module:engine/view/matcher~MatcherPattern} for view elements whose content should be treated as raw data
     * and not processed during the conversion from DOM nodes to view elements.
     *
     * This is affecting how {@link module:engine/view/domconverter~DomConverter#domToView} and
     * {@link module:engine/view/domconverter~DomConverter#domChildrenToView} process DOM nodes.
     *
     * The raw data can be later accessed by a
     * {@link module:engine/view/element~Element#getCustomProperty custom property of a view element} called `"$rawContent"`.
     *
     * @param {module:engine/view/matcher~MatcherPattern} pattern Pattern matching a view element whose content should
     * be treated as raw data.
     */
    registerRawContentMatcher(pattern) {
        this._rawContentElementMatcher.add(pattern);
    }
    /**
     * Returns the block {@link module:engine/view/filler filler} node based on the current {@link #blockFillerMode} setting.
     *
     * @private
     * @returns {Node} filler
     */
    _getBlockFiller() {
        switch (this.blockFillerMode) {
            case 'nbsp':
                return NBSP_FILLER(this._domDocument); // eslint-disable-line new-cap
            case 'markedNbsp':
                return MARKED_NBSP_FILLER(this._domDocument); // eslint-disable-line new-cap
            case 'br':
                return BR_FILLER(this._domDocument); // eslint-disable-line new-cap
        }
    }
    /**
     * Checks if the given DOM position is a correct place for selection boundary. See {@link #isDomSelectionCorrect}.
     *
     * @private
     * @param {Element} domParent Position parent.
     * @param {Number} offset Position offset.
     * @returns {Boolean} `true` if given position is at a correct place for selection boundary, `false` otherwise.
     */
    _isDomSelectionPositionCorrect(domParent, offset) {
        // If selection is before or in the middle of inline filler string, it is incorrect.
        if (isText(domParent) && startsWithFiller(domParent) && offset < INLINE_FILLER_LENGTH) {
            // Selection in a text node, at wrong position (before or in the middle of filler).
            return false;
        }
        if (this.isElement(domParent) && startsWithFiller(domParent.childNodes[offset])) {
            // Selection in an element node, before filler text node.
            return false;
        }
        const viewParent = this.mapDomToView(domParent);
        // The position is incorrect when anchored inside a UIElement or a RawElement.
        // Note: In case of UIElement and RawElement, mapDomToView() returns a parent element for any DOM child
        // so there's no need to perform any additional checks.
        if (viewParent && (viewParent.is('uiElement') || viewParent.is('rawElement'))) {
            return false;
        }
        return true;
    }
    /**
     * Takes text data from a given {@link module:engine/view/text~Text#data} and processes it so
     * it is correctly displayed in the DOM.
     *
     * Following changes are done:
     *
     * * a space at the beginning is changed to `&nbsp;` if this is the first text node in its container
     * element or if a previous text node ends with a space character,
     * * space at the end of the text node is changed to `&nbsp;` if there are two spaces at the end of a node or if next node
     * starts with a space or if it is the last text node in its container,
     * * remaining spaces are replaced to a chain of spaces and `&nbsp;` (e.g. `'x   x'` becomes `'x &nbsp; x'`).
     *
     * Content of {@link #preElements} is not processed.
     *
     * @private
     * @param {module:engine/view/text~Text} node View text node to process.
     * @returns {String} Processed text data.
     */
    _processDataFromViewText(node) {
        let data = node.data;
        // If any of node ancestors has a name which is in `preElements` array, then currently processed
        // view text node is (will be) in preformatted element. We should not change whitespaces then.
        if (node.getAncestors().some(parent => this.preElements.includes(parent.name))) {
            return data;
        }
        // 1. Replace the first space with a nbsp if the previous node ends with a space or there is no previous node
        // (container element boundary).
        if (data.charAt(0) == ' ') {
            const prevNode = this._getTouchingInlineViewNode(node, false);
            const prevEndsWithSpace = prevNode && prevNode.is('$textProxy') && this._nodeEndsWithSpace(prevNode);
            if (prevEndsWithSpace || !prevNode) {
                data = '\u00A0' + data.substr(1);
            }
        }
        // 2. Replace the last space with nbsp if there are two spaces at the end or if the next node starts with space or there is no
        // next node (container element boundary).
        //
        // Keep in mind that Firefox prefers $nbsp; before tag, not inside it:
        //
        // Foo <span>&nbsp;bar</span>  <-- bad.
        // Foo&nbsp;<span> bar</span>  <-- good.
        //
        // More here: https://github.com/ckeditor/ckeditor5-engine/issues/1747.
        if (data.charAt(data.length - 1) == ' ') {
            const nextNode = this._getTouchingInlineViewNode(node, true);
            const nextStartsWithSpace = nextNode && nextNode.is('$textProxy') && nextNode.data.charAt(0) == ' ';
            if (data.charAt(data.length - 2) == ' ' || !nextNode || nextStartsWithSpace) {
                data = data.substr(0, data.length - 1) + '\u00A0';
            }
        }
        // 3. Create space+nbsp pairs.
        return data.replace(/ {2}/g, ' \u00A0');
    }
    /**
     * Checks whether given node ends with a space character after changing appropriate space characters to `&nbsp;`s.
     *
     * @private
     * @param {module:engine/view/text~Text} node Node to check.
     * @returns {Boolean} `true` if given `node` ends with space, `false` otherwise.
     */
    _nodeEndsWithSpace(node) {
        if (node.getAncestors().some(parent => this.preElements.includes(parent.name))) {
            return false;
        }
        const data = this._processDataFromViewText(node);
        return data.charAt(data.length - 1) == ' ';
    }
    /**
     * Takes text data from native `Text` node and processes it to a correct {@link module:engine/view/text~Text view text node} data.
     *
     * Following changes are done:
     *
     * * multiple whitespaces are replaced to a single space,
     * * space at the beginning of a text node is removed if it is the first text node in its container
     * element or if the previous text node ends with a space character,
     * * space at the end of the text node is removed if there are two spaces at the end of a node or if next node
     * starts with a space or if it is the last text node in its container
     * * nbsps are converted to spaces.
     *
     * @param {Node} node DOM text node to process.
     * @returns {String} Processed data.
     * @private
     */
    _processDataFromDomText(node) {
        let data = node.data;
        if (_hasDomParentOfType(node, this.preElements)) {
            return getDataWithoutFiller(node);
        }
        // Change all consecutive whitespace characters (from the [ \n\t\r] set –
        // see https://github.com/ckeditor/ckeditor5-engine/issues/822#issuecomment-311670249) to a single space character.
        // That's how multiple whitespaces are treated when rendered, so we normalize those whitespaces.
        // We're replacing 1+ (and not 2+) to also normalize singular \n\t\r characters (#822).
        data = data.replace(/[ \n\t\r]{1,}/g, ' ');
        const prevNode = this._getTouchingInlineDomNode(node, false);
        const nextNode = this._getTouchingInlineDomNode(node, true);
        const shouldLeftTrim = this._checkShouldLeftTrimDomText(node, prevNode);
        const shouldRightTrim = this._checkShouldRightTrimDomText(node, nextNode);
        // If the previous dom text node does not exist or it ends by whitespace character, remove space character from the beginning
        // of this text node. Such space character is treated as a whitespace.
        if (shouldLeftTrim) {
            data = data.replace(/^ /, '');
        }
        // If the next text node does not exist remove space character from the end of this text node.
        if (shouldRightTrim) {
            data = data.replace(/ $/, '');
        }
        // At the beginning and end of a block element, Firefox inserts normal space + <br> instead of non-breaking space.
        // This means that the text node starts/end with normal space instead of non-breaking space.
        // This causes a problem because the normal space would be removed in `.replace` calls above. To prevent that,
        // the inline filler is removed only after the data is initially processed (by the `.replace` above). See ckeditor5#692.
        data = getDataWithoutFiller(new Text(data));
        // At this point we should have removed all whitespaces from DOM text data.
        //
        // Now, We will reverse the process that happens in `_processDataFromViewText`.
        //
        // We have to change &nbsp; chars, that were in DOM text data because of rendering reasons, to spaces.
        // First, change all ` \u00A0` pairs (space + &nbsp;) to two spaces. DOM converter changes two spaces from model/view to
        // ` \u00A0` to ensure proper rendering. Since here we convert back, we recognize those pairs and change them back to `  `.
        data = data.replace(/ \u00A0/g, '  ');
        const isNextNodeInlineObjectElement = nextNode && this.isElement(nextNode) && nextNode.tagName != 'BR';
        const isNextNodeStartingWithSpace = nextNode && isText(nextNode) && nextNode.data.charAt(0) == ' ';
        // Then, let's change the last nbsp to a space.
        if (/( |\u00A0)\u00A0$/.test(data) || !nextNode || isNextNodeInlineObjectElement || isNextNodeStartingWithSpace) {
            data = data.replace(/\u00A0$/, ' ');
        }
        // Then, change &nbsp; character that is at the beginning of the text node to space character.
        // We do that replacement only if this is the first node or the previous node ends on whitespace character.
        if (shouldLeftTrim || prevNode && this.isElement(prevNode) && prevNode.tagName != 'BR') {
            data = data.replace(/^\u00A0/, ' ');
        }
        // At this point, all whitespaces should be removed and all &nbsp; created for rendering reasons should be
        // changed to normal space. All left &nbsp; are &nbsp; inserted intentionally.
        return data;
    }
    /**
     * Helper function which checks if a DOM text node, preceded by the given `prevNode` should
     * be trimmed from the left side.
     *
     * @private
     * @param {Node} node
     * @param {Node} prevNode Either DOM text or `<br>` or one of `#inlineObjectElements`.
     */
    _checkShouldLeftTrimDomText(node, prevNode) {
        if (!prevNode) {
            return true;
        }
        if (this.isElement(prevNode)) {
            return prevNode.tagName === 'BR';
        }
        // Shouldn't left trim if previous node is a node that was encountered as a raw content node.
        if (this._encounteredRawContentDomNodes.has(node.previousSibling)) {
            return false;
        }
        return /[^\S\u00A0]/.test(prevNode.data.charAt(prevNode.data.length - 1));
    }
    /**
     * Helper function which checks if a DOM text node, succeeded by the given `nextNode` should
     * be trimmed from the right side.
     *
     * @private
     * @param {Node} node
     * @param {Node} nextNode Either DOM text or `<br>` or one of `#inlineObjectElements`.
     */
    _checkShouldRightTrimDomText(node, nextNode) {
        if (nextNode) {
            return false;
        }
        return !startsWithFiller(node);
    }
    /**
     * Helper function. For given {@link module:engine/view/text~Text view text node}, it finds previous or next sibling
     * that is contained in the same container element. If there is no such sibling, `null` is returned.
     *
     * @private
     * @param {module:engine/view/text~Text} node Reference node.
     * @param {Boolean} getNext
     * @returns {module:engine/view/text~Text|module:engine/view/element~Element|null} Touching text node, an inline object
     * or `null` if there is no next or previous touching text node.
     */
    _getTouchingInlineViewNode(node, getNext) {
        const treeWalker = new treewalker_TreeWalker({
            startPosition: getNext ? Position._createAfter(node) : Position._createBefore(node),
            direction: getNext ? 'forward' : 'backward'
        });
        for (const value of treeWalker) {
            // Found an inline object (for example an image).
            if (value.item.is('element') && this.inlineObjectElements.includes(value.item.name)) {
                return value.item;
            }
            // ViewContainerElement is found on a way to next ViewText node, so given `node` was first/last
            // text node in its container element.
            else if (value.item.is('containerElement')) {
                return null;
            }
            // <br> found – it works like a block boundary, so do not scan further.
            else if (value.item.is('element', 'br')) {
                return null;
            }
            // Found a text node in the same container element.
            else if (value.item.is('$textProxy')) {
                return value.item;
            }
        }
        return null;
    }
    /**
     * Helper function. For the given text node, it finds the closest touching node which is either
     * a text, `<br>` or an {@link #inlineObjectElements inline object}.
     *
     * If no such node is found, `null` is returned.
     *
     * For instance, in the following DOM structure:
     *
     *		<p>foo<b>bar</b><br>bom</p>
     *
     * * `foo` doesn't have its previous touching inline node (`null` is returned),
     * * `foo`'s next touching inline node is `bar`
     * * `bar`'s next touching inline node is `<br>`
     *
     * This method returns text nodes and `<br>` elements because these types of nodes affect how
     * spaces in the given text node need to be converted.
     *
     * @private
     * @param {Text} node
     * @param {Boolean} getNext
     * @returns {Text|Element|null}
     */
    _getTouchingInlineDomNode(node, getNext) {
        if (!node.parentNode) {
            return null;
        }
        const stepInto = getNext ? 'firstChild' : 'lastChild';
        const stepOver = getNext ? 'nextSibling' : 'previousSibling';
        let skipChildren = true;
        let returnNode = node;
        do {
            if (!skipChildren && returnNode[stepInto]) {
                returnNode = returnNode[stepInto];
            }
            else if (returnNode[stepOver]) {
                returnNode = returnNode[stepOver];
                skipChildren = false;
            }
            else {
                returnNode = returnNode.parentNode;
                skipChildren = true;
            }
            if (!returnNode || this._isBlockElement(returnNode)) {
                return null;
            }
        } while (!(isText(returnNode) || returnNode.tagName == 'BR' || this._isInlineObjectElement(returnNode)));
        return returnNode;
    }
    /**
     * Returns `true` if a DOM node belongs to {@link #blockElements}. `false` otherwise.
     *
     * @private
     * @param {Node} node
     * @returns {Boolean}
     */
    _isBlockElement(node) {
        return this.isElement(node) && this.blockElements.includes(node.tagName.toLowerCase());
    }
    /**
     * Returns `true` if a DOM node belongs to {@link #inlineObjectElements}. `false` otherwise.
     *
     * @private
     * @param {Node} node
     * @returns {Boolean}
     */
    _isInlineObjectElement(node) {
        return this.isElement(node) && this.inlineObjectElements.includes(node.tagName.toLowerCase());
    }
    /**
     * Creates view element basing on the node type.
     *
     * @private
     * @param {Node} node DOM node to check.
     * @param {Object} options Conversion options. See {@link module:engine/view/domconverter~DomConverter#domToView} options parameter.
     * @returns {Element}
     */
    _createViewElement(node, options) {
        if (isComment(node)) {
            return new UIElement(this.document, '$comment');
        }
        const viewName = options.keepOriginalCase ? node.tagName : node.tagName.toLowerCase();
        return new Element(this.document, viewName);
    }
    /**
     * Checks if view element's content should be treated as a raw data.
     *
     * @private
     * @param {Element} viewElement View element to check.
     * @param {Object} options Conversion options. See {@link module:engine/view/domconverter~DomConverter#domToView} options parameter.
     * @returns {Boolean}
     */
    _isViewElementWithRawContent(viewElement, options) {
        return options.withChildren !== false && !!this._rawContentElementMatcher.match(viewElement);
    }
    /**
     * Checks whether a given element name should be renamed in a current rendering mode.
     *
     * @private
     * @param {String} elementName The name of view element.
     * @returns {Boolean}
     */
    _shouldRenameElement(elementName) {
        const name = elementName.toLowerCase();
        return this.renderingMode === 'editing' && this.unsafeElements.includes(name);
    }
    /**
     * Return a <span> element with a special attribute holding the name of the original element.
     * Optionally, copy all the attributes of the original element if that element is provided.
     *
     * @private
     * @param {String} elementName The name of view element.
     * @param {Element} [originalDomElement] The original DOM element to copy attributes and content from.
     * @returns {Element}
     */
    _createReplacementDomElement(elementName, originalDomElement) {
        const newDomElement = this._domDocument.createElement('span');
        // Mark the span replacing a script as hidden.
        newDomElement.setAttribute(UNSAFE_ELEMENT_REPLACEMENT_ATTRIBUTE, elementName);
        if (originalDomElement) {
            while (originalDomElement.firstChild) {
                newDomElement.appendChild(originalDomElement.firstChild);
            }
            for (const attributeName of originalDomElement.getAttributeNames()) {
                newDomElement.setAttribute(attributeName, originalDomElement.getAttribute(attributeName));
            }
        }
        return newDomElement;
    }
}
// Helper function.
// Used to check if given native `Element` or `Text` node has parent with tag name from `types` array.
//
// @param {Node} node
// @param {Array.<String>} types
// @returns {Boolean} `true` if such parent exists or `false` if it does not.
function _hasDomParentOfType(node, types) {
    const parents = getAncestors(node);
    return parents.some(parent => parent.tagName && types.includes(parent.tagName.toLowerCase()));
}
// A helper that executes given callback for each DOM node's ancestor, starting from the given node
// and ending in document#documentElement.
//
// @param {Node} node
// @param {Function} callback A callback to be executed for each ancestor.
function forEachDomElementAncestor(element, callback) {
    let node = element;
    while (node) {
        callback(node);
        node = node.parentElement;
    }
}
// Checks if given node is a nbsp block filler.
//
// A &nbsp; is a block filler only if it is a single child of a block element.
//
// @param {Node} domNode DOM node.
// @param {Array.<String>} blockElements
// @returns {Boolean}
function isNbspBlockFiller(domNode, blockElements) {
    const isNBSP = domNode.isEqualNode(NBSP_FILLER_REF);
    return isNBSP && hasBlockParent(domNode, blockElements) && domNode.parentNode.childNodes.length === 1;
}
// Checks if domNode has block parent.
//
// @param {Node} domNode DOM node.
// @param {Array.<String>} blockElements
// @returns {Boolean}
function hasBlockParent(domNode, blockElements) {
    const parent = domNode.parentNode;
    return !!parent && !!parent.tagName && blockElements.includes(parent.tagName.toLowerCase());
}
// Log to console the information about element that was replaced.
// Check UNSAFE_ELEMENTS for all recognized unsafe elements.
//
// @param {String} elementName The name of the view element
function _logUnsafeElement(elementName) {
    if (elementName === 'script') {
        logWarning('domconverter-unsafe-script-element-detected');
    }
    if (elementName === 'style') {
        logWarning('domconverter-unsafe-style-element-detected');
    }
}
/**
 * While rendering the editor content, the {@link module:engine/view/domconverter~DomConverter} detected a `<script>` element that may
 * disrupt the editing experience. To avoid this, the `<script>` element was replaced with `<span data-ck-unsafe-element="script"></span>`.
 *
 * @error domconverter-unsafe-script-element-detected
 */
/**
 * While rendering the editor content, the {@link module:engine/view/domconverter~DomConverter} detected a `<style>` element that may affect
 * the editing experience. To avoid this, the `<style>` element was replaced with `<span data-ck-unsafe-element="style"></span>`.
 *
 * @error domconverter-unsafe-style-element-detected
 */
/**
 * The {@link module:engine/view/domconverter~DomConverter} detected an interactive attribute in the
 * {@glink framework/guides/architecture/editing-engine#editing-pipeline editing pipeline}. For the best
 * editing experience, the attribute was renamed to `data-ck-unsafe-attribute-[original attribute name]`.
 *
 * If you are the author of the plugin that generated this attribute and you want it to be preserved
 * in the editing pipeline, you can configure this when creating the element
 * using {@link module:engine/view/downcastwriter~DowncastWriter} during the
 * {@glink framework/guides/architecture/editing-engine#conversion model–view conversion}. Methods such as
 * {@link module:engine/view/downcastwriter~DowncastWriter#createContainerElement},
 * {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement}, or
 * {@link module:engine/view/downcastwriter~DowncastWriter#createEmptyElement}
 * accept an option that will disable filtering of specific attributes:
 *
 *		const paragraph = writer.createContainerElement( 'p',
 *			{
 *				class: 'clickable-paragraph',
 *				onclick: 'alert( "Paragraph clicked!" )'
 *			},
 *			{
 *				// Make sure the "onclick" attribute will pass through.
 *				renderUnsafeAttributes: [ 'onclick' ]
 *			}
 *		);
 *
 * @error domconverter-unsafe-attribute-detected
 * @param {HTMLElement} domElement The DOM element the attribute was set on.
 * @param {String} key The original name of the attribute
 * @param {String} value The value of the original attribute
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/iswindow.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/iswindow
 */
/**
 * Checks if the object is a native DOM Window.
 *
 * @param {*} obj
 * @returns {Boolean}
 */
function isWindow(obj) {
    const stringifiedObject = Object.prototype.toString.apply(obj);
    // Returns `true` for the `window` object in browser environments.
    if (stringifiedObject == '[object Window]') {
        return true;
    }
    // Returns `true` for the `window` object in the Electron environment.
    if (stringifiedObject == '[object global]') {
        return true;
    }
    return false;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/emittermixin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module utils/dom/emittermixin
 */




/**
 * Mixin that injects the DOM events API into its host. It provides the API
 * compatible with {@link module:utils/emittermixin~EmitterMixin}.
 *
 * DOM emitter mixin is by default available in the {@link module:ui/view~View} class,
 * but it can also be mixed into any other class:
 *
 *		import mix from '../utils/mix.js';
 *		import DomEmitterMixin from '../utils/dom/emittermixin.js';
 *		import { Emitter } from '../utils/emittermixin.js';
 *
 *		class SomeView extends DomEmitterMixin( Emitter ) {}
 *
 *		const view = new SomeView();
 *		view.listenTo( domElement, ( evt, domEvt ) => {
 *			console.log( evt, domEvt );
 *		} );
 *
 * @mixin EmitterMixin
 * @mixes module:utils/emittermixin~EmitterMixin
 * @implements module:utils/dom/emittermixin~Emitter
 */
function DomEmitterMixin(base) {
    class Mixin extends base {
        listenTo(emitter, event, callback, options = {}) {
            // Check if emitter is an instance of DOM Node. If so, use corresponding ProxyEmitter (or create one if not existing).
            if (isNode(emitter) || isWindow(emitter)) {
                const proxyOptions = {
                    capture: !!options.useCapture,
                    passive: !!options.usePassive
                };
                const proxyEmitter = this._getProxyEmitter(emitter, proxyOptions) || new ProxyEmitter(emitter, proxyOptions);
                this.listenTo(proxyEmitter, event, callback, options);
            }
            else {
                // Execute parent class method with Emitter (or ProxyEmitter) instance.
                Emitter.prototype.listenTo.call(this, emitter, event, callback, options);
            }
        }
        stopListening(emitter, event, callback) {
            // Check if the emitter is an instance of DOM Node. If so, forward the call to the corresponding ProxyEmitters.
            if (isNode(emitter) || isWindow(emitter)) {
                const proxyEmitters = this._getAllProxyEmitters(emitter);
                for (const proxy of proxyEmitters) {
                    this.stopListening(proxy, event, callback);
                }
            }
            else {
                // Execute parent class method with Emitter (or ProxyEmitter) instance.
                Emitter.prototype.stopListening.call(this, emitter, event, callback);
            }
        }
        /**
         * Retrieves ProxyEmitter instance for given DOM Node residing in this Host and given options.
         *
         * @private
         * @param {Node|Window} node DOM Node of the ProxyEmitter.
         * @param {Object} [options] Additional options.
         * @param {Boolean} [options.useCapture=false] Indicates that events of this type will be dispatched to the registered
         * listener before being dispatched to any EventTarget beneath it in the DOM tree.
         * @param {Boolean} [options.usePassive=false] Indicates that the function specified by listener will never call preventDefault()
         * and prevents blocking browser's main thread by this event handler.
         * @returns {module:utils/dom/emittermixin~ProxyEmitter|null} ProxyEmitter instance bound to the DOM Node.
         */
        _getProxyEmitter(node, options) {
            return _getEmitterListenedTo(this, getProxyEmitterId(node, options));
        }
        /**
         * Retrieves all the ProxyEmitter instances for given DOM Node residing in this Host.
         *
         * @private
         * @param {Node|Window} node DOM Node of the ProxyEmitter.
         * @returns {Array.<module:utils/dom/emittermixin~ProxyEmitter>}
         */
        _getAllProxyEmitters(node) {
            return [
                { capture: false, passive: false },
                { capture: false, passive: true },
                { capture: true, passive: false },
                { capture: true, passive: true }
            ].map(options => this._getProxyEmitter(node, options)).filter(proxy => !!proxy);
        }
    }
    return Mixin;
}
const emittermixin_Emitter = DomEmitterMixin(Emitter);
// Backward compatibility with `mix`
([
    '_getProxyEmitter', '_getAllProxyEmitters',
    'on', 'once', 'off', 'listenTo',
    'stopListening', 'fire', 'delegate', 'stopDelegating',
    '_addEventListener', '_removeEventListener'
]).forEach(key => {
    DomEmitterMixin[key] = emittermixin_Emitter.prototype[key];
});
/**
 * Creates a ProxyEmitter instance. Such an instance is a bridge between a DOM Node firing events
 * and any Host listening to them. It is backwards compatible with {@link module:utils/emittermixin~EmitterMixin#on}.
 * There is a separate instance for each combination of modes (useCapture & usePassive). The mode is concatenated with
 * UID stored in HTMLElement to give each instance unique identifier.
 *
 *                                  listenTo( click, ... )
 *                    +-----------------------------------------+
 *                    |              stopListening( ... )       |
 *     +----------------------------+                           |             addEventListener( click, ... )
 *     | Host                       |                           |   +---------------------------------------------+
 *     +----------------------------+                           |   |       removeEventListener( click, ... )     |
 *     | _listeningTo: {            |                +----------v-------------+                                   |
 *     |   UID+mode: {              |                | ProxyEmitter           |                                   |
 *     |     emitter: ProxyEmitter, |                +------------------------+                      +------------v----------+
 *     |     callbacks: {           |                | events: {              |                      | Node (HTMLElement)    |
 *     |       click: [ callbacks ] |                |   click: [ callbacks ] |                      +-----------------------+
 *     |     }                      |                | },                     |                      | data-ck-expando: UID  |
 *     |   }                        |                | _domNode: Node,        |                      +-----------------------+
 *     | }                          |                | _domListeners: {},     |                                   |
 *     | +------------------------+ |                | _emitterId: UID+mode   |                                   |
 *     | | DomEmitterMixin        | |                +--------------^---------+                                   |
 *     | +------------------------+ |                           |   |                                             |
 *     +--------------^-------------+                           |   +---------------------------------------------+
 *                    |                                         |                  click (DOM Event)
 *                    +-----------------------------------------+
 *                                fire( click, DOM Event )
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 * @implements module:utils/dom/emittermixin~Emitter
 * @private
 */
class ProxyEmitter extends Emitter {
    /**
     * @param {Node|Window} node DOM Node that fires events.
     * @param {Object} [options] Additional options.
     * @param {Boolean} [options.useCapture=false] Indicates that events of this type will be dispatched to the registered
     * listener before being dispatched to any EventTarget beneath it in the DOM tree.
     * @param {Boolean} [options.usePassive=false] Indicates that the function specified by listener will never call preventDefault()
     * and prevents blocking browser's main thread by this event handler.
     */
    constructor(node, options) {
        super();
        // Set emitter ID to match DOM Node "expando" property.
        _setEmitterId(this, getProxyEmitterId(node, options));
        // Remember the DOM Node this ProxyEmitter is bound to.
        this._domNode = node;
        // And given options.
        this._options = options;
    }
    /**
     * Registers a callback function to be executed when an event is fired.
     *
     * It attaches a native DOM listener to the DOM Node. When fired,
     * a corresponding Emitter event will also fire with DOM Event object as an argument.
     *
     * **Note**: This is automatically called by the
     * {@link module:utils/emittermixin~EmitterMixin#listenTo `EmitterMixin#listenTo()`}.
     *
     * @method module:utils/dom/emittermixin~ProxyEmitter#attach
     * @param {String} event The name of the event.
     */
    attach(event) {
        // If the DOM Listener for given event already exist it is pointless
        // to attach another one.
        if (this._domListeners && this._domListeners[event]) {
            return;
        }
        const domListener = this._createDomListener(event);
        // Attach the native DOM listener to DOM Node.
        this._domNode.addEventListener(event, domListener, this._options);
        if (!this._domListeners) {
            this._domListeners = {};
        }
        // Store the native DOM listener in this ProxyEmitter. It will be helpful
        // when stopping listening to the event.
        this._domListeners[event] = domListener;
    }
    /**
     * Stops executing the callback on the given event.
     *
     * **Note**: This is automatically called by the
     * {@link module:utils/emittermixin~EmitterMixin#stopListening `EmitterMixin#stopListening()`}.
     *
     * @method module:utils/dom/emittermixin~ProxyEmitter#detach
     * @param {String} event The name of the event.
     */
    detach(event) {
        let events;
        // Remove native DOM listeners which are orphans. If no callbacks
        // are awaiting given event, detach native DOM listener from DOM Node.
        // See: {@link attach}.
        if (this._domListeners[event] && (!(events = this._events[event]) || !events.callbacks.length)) {
            this._domListeners[event].removeListener();
        }
    }
    /**
     * Adds callback to emitter for given event.
     *
     * @protected
     * @method module:utils/dom/emittermixin~ProxyEmitter#_addEventListener
     * @param {String} event The name of the event.
     * @param {Function} callback The function to be called on event.
     * @param {Object} [options={}] Additional options.
     * @param {module:utils/priorities~PriorityString} [options.priority='normal'] The priority of this event callback. The higher
     * the priority value the sooner the callback will be fired. Events having the same priority are called in the
     * order they were added.
     */
    _addEventListener(event, callback, options) {
        this.attach(event);
        Emitter.prototype._addEventListener.call(this, event, callback, options);
    }
    /**
     * Removes callback from emitter for given event.
     *
     * @protected
     * @method module:utils/dom/emittermixin~ProxyEmitter#_removeEventListener
     * @param {String} event The name of the event.
     * @param {Function} callback The function to stop being called.
     */
    _removeEventListener(event, callback) {
        Emitter.prototype._removeEventListener.call(this, event, callback);
        this.detach(event);
    }
    /**
     * Creates a native DOM listener callback. When the native DOM event
     * is fired it will fire corresponding event on this ProxyEmitter.
     * Note: A native DOM Event is passed as an argument.
     *
     * @private
     * @method module:utils/dom/emittermixin~ProxyEmitter#_createDomListener
     * @param {String} event The name of the event.
     * @returns {Function} The DOM listener callback.
     */
    _createDomListener(event) {
        const domListener = (domEvt) => {
            this.fire(event, domEvt);
        };
        // Supply the DOM listener callback with a function that will help
        // detach it from the DOM Node, when it is no longer necessary.
        // See: {@link detach}.
        domListener.removeListener = () => {
            this._domNode.removeEventListener(event, domListener, this._options);
            delete this._domListeners[event];
        };
        return domListener;
    }
}
// Gets an unique DOM Node identifier. The identifier will be set if not defined.
//
// @private
// @param {Node} node
// @returns {String} UID for given DOM Node.
function getNodeUID(node) {
    return node['data-ck-expando'] || (node['data-ck-expando'] = uid());
}
// Gets id of the ProxyEmitter for the given node.
//
// Combines DOM Node identifier and additional options.
//
// @private
// @param {Node} node
// @param {Object} options Additional options.
// @returns {String} ProxyEmitter id.
function getProxyEmitterId(node, options) {
    let id = getNodeUID(node);
    for (const option of Object.keys(options).sort()) {
        if (options[option]) {
            id += '-' + option;
        }
    }
    return id;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/observer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/observer
 */

/**
 * Abstract base observer class. Observers are classes which listen to DOM events, do the preliminary
 * processing and fire events on the {@link module:engine/view/document~Document} objects.
 * Observers can also add features to the view, for instance by updating its status or marking elements
 * which need a refresh on DOM events.
 *
 * @abstract
 */
class Observer extends emittermixin_Emitter {
    /**
     * Creates an instance of the observer.
     *
     * @param {module:engine/view/view~View} view
     */
    constructor(view) {
        super();
        /**
         * An instance of the view controller.
         *
         * @readonly
         * @member {module:engine/view/view~View}
         */
        this.view = view;
        /**
         * A reference to the {@link module:engine/view/document~Document} object.
         *
         * @readonly
         * @member {module:engine/view/document~Document}
         */
        this.document = view.document;
        /**
         * The state of the observer. If it is disabled, no events will be fired.
         *
         * @readonly
         * @member {Boolean}
         */
        this.isEnabled = false;
    }
    /**
     * Enables the observer. This method is called when the observer is registered to the
     * {@link module:engine/view/view~View} and after {@link module:engine/view/view~View#forceRender rendering}
     * (all observers are {@link #disable disabled} before rendering).
     *
     * A typical use case for disabling observers is that mutation observers need to be disabled for the rendering.
     * However, a child class may not need to be disabled, so it can implement an empty method.
     *
     * @see module:engine/view/observer/observer~Observer#disable
     */
    enable() {
        this.isEnabled = true;
    }
    /**
     * Disables the observer. This method is called before
     * {@link module:engine/view/view~View#forceRender rendering} to prevent firing events during rendering.
     *
     * @see module:engine/view/observer/observer~Observer#enable
     */
    disable() {
        this.isEnabled = false;
    }
    /**
     * Disables and destroys the observer, among others removes event listeners created by the observer.
     */
    destroy() {
        this.disable();
        this.stopListening();
    }
    /**
     * Checks whether a given DOM event should be ignored (should not be turned into a synthetic view document event).
     *
     * Currently, an event will be ignored only if its target or any of its ancestors has the `data-cke-ignore-events` attribute.
     * This attribute can be used inside the structures generated by
     * {@link module:engine/view/downcastwriter~DowncastWriter#createUIElement `DowncastWriter#createUIElement()`} to ignore events
     * fired within a UI that should be excluded from CKEditor 5's realms.
     *
     * @param {Node} domTarget The DOM event target to check (usually an element, sometimes a text node and
     * potentially sometimes a document, too).
     * @returns {Boolean} Whether this event should be ignored by the observer.
     */
    checkShouldIgnoreEventFromTarget(domTarget) {
        if (domTarget && domTarget.nodeType === 3) {
            domTarget = domTarget.parentNode;
        }
        if (!domTarget || domTarget.nodeType !== 1) {
            return false;
        }
        return domTarget.matches('[data-cke-ignore-events], [data-cke-ignore-events] *');
    }
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/assignIn.js




/**
 * This method is like `_.assign` except that it iterates over own and
 * inherited source properties.
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @alias extend
 * @category Object
 * @param {Object} object The destination object.
 * @param {...Object} [sources] The source objects.
 * @returns {Object} Returns `object`.
 * @see _.assign
 * @example
 *
 * function Foo() {
 *   this.a = 1;
 * }
 *
 * function Bar() {
 *   this.c = 3;
 * }
 *
 * Foo.prototype.b = 2;
 * Bar.prototype.d = 4;
 *
 * _.assignIn({ 'a': 0 }, new Foo, new Bar);
 * // => { 'a': 1, 'b': 2, 'c': 3, 'd': 4 }
 */
var assignIn = _createAssigner(function(object, source) {
  _copyObject(source, lodash_es_keysIn(source), object);
});

/* harmony default export */ const lodash_es_assignIn = (assignIn);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/domeventdata.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/domeventdata
 */

/**
 * Information about a DOM event in context of the {@link module:engine/view/document~Document}.
 * It wraps the native event, which usually should not be used as the wrapper contains
 * additional data (like key code for keyboard events).
 */
class DomEventData {
    /**
     * @param {module:engine/view/view~View} view The instance of the view controller.
     * @param {Event} domEvent The DOM event.
     * @param {Object} [additionalData] Additional properties that the instance should contain.
     */
    constructor(view, domEvent, additionalData) {
        /**
         * Instance of the view controller.
         *
         * @readonly
         * @member {module:engine/view/view~View} module:engine/view/observer/observer~Observer.DomEvent#view
         */
        this.view = view;
        /**
         * The instance of the document.
         *
         * @readonly
         * @member {module:engine/view/document~Document} module:engine/view/observer/observer~Observer.DomEvent#document
         */
        this.document = view.document;
        /**
         * The DOM event.
         *
         * @readonly
         * @member {Event} module:engine/view/observer/observer~Observer.DomEvent#domEvent
         */
        this.domEvent = domEvent;
        /**
         * The DOM target.
         *
         * @readonly
         * @member {HTMLElement} module:engine/view/observer/observer~Observer.DomEvent#target
         */
        this.domTarget = domEvent.target;
        lodash_es_assignIn(this, additionalData);
    }
    /**
     * The tree view element representing the target.
     *
     * @readonly
     * @type module:engine/view/element~Element
     */
    get target() {
        return this.view.domConverter.mapDomToView(this.domTarget);
    }
    /**
     * Prevents the native's event default action.
     */
    preventDefault() {
        this.domEvent.preventDefault();
    }
    /**
     * Stops native event propagation.
     */
    stopPropagation() {
        this.domEvent.stopPropagation();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/domeventobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/domeventobserver
 */


/**
 * Base class for DOM event observers. This class handles
 * {@link module:engine/view/observer/observer~Observer#observe adding} listeners to DOM elements,
 * {@link module:engine/view/observer/observer~Observer#disable disabling} and
 * {@link module:engine/view/observer/observer~Observer#enable re-enabling} events.
 * Child class needs to define
 * {@link module:engine/view/observer/domeventobserver~DomEventObserver#domEventType DOM event type} and
 * {@link module:engine/view/observer/domeventobserver~DomEventObserver#onDomEvent callback}.
 *
 * For instance:
 *
 *		class ClickObserver extends DomEventObserver {
 *			// It can also be defined as a normal property in the constructor.
 *			get domEventType() {
 *				return 'click';
 *			}
 *
 *			onDomEvent( domEvent ) {
 *				this.fire( 'click', domEvent );
 *			}
 *		}
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class DomEventObserver extends Observer {
    /**
     * @inheritDoc
     */
    constructor(view) {
        super(view);
        /**
         * If set to `true` DOM events will be listened on the capturing phase.
         * Default value is `false`.
         *
         * @member {Boolean}
         */
        this.useCapture = false;
    }
    /**
     * @inheritDoc
     */
    observe(domElement) {
        const types = typeof this.domEventType == 'string' ? [this.domEventType] : this.domEventType;
        types.forEach(type => {
            this.listenTo(domElement, type, (eventInfo, domEvent) => {
                if (this.isEnabled && !this.checkShouldIgnoreEventFromTarget(domEvent.target)) {
                    this.onDomEvent(domEvent);
                }
            }, { useCapture: this.useCapture });
        });
    }
    /**
     * Calls `Document#fire()` if observer {@link #isEnabled is enabled}.
     *
     * @see module:utils/emittermixin~EmitterMixin#fire
     * @param {String} eventType The event type (name).
     * @param {Event} domEvent The DOM event.
     * @param {Object} [additionalData] The additional data which should extend the
     * {@link module:engine/view/observer/domeventdata~DomEventData event data} object.
     */
    fire(eventType, domEvent, additionalData) {
        if (this.isEnabled) {
            this.document.fire(eventType, new DomEventData(this.view, domEvent, additionalData));
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/keyobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/keyobserver
 */


/**
 * Observer for events connected with pressing keyboard keys.
 *
 * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class KeyObserver extends DomEventObserver {
    constructor(view) {
        super(view);
        this.domEventType = ['keydown', 'keyup'];
    }
    onDomEvent(domEvt) {
        const data = {
            keyCode: domEvt.keyCode,
            altKey: domEvt.altKey,
            ctrlKey: domEvt.ctrlKey,
            shiftKey: domEvt.shiftKey,
            metaKey: domEvt.metaKey,
            get keystroke() {
                return getCode(this);
            }
        };
        this.fire(domEvt.type, domEvt, data);
    }
}
/**
 * Fired when a key has been pressed.
 *
 * Introduced by {@link module:engine/view/observer/keyobserver~KeyObserver}.
 *
 * Note that because {@link module:engine/view/observer/keyobserver~KeyObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/keyobserver~KeyObserver
 * @event module:engine/view/document~Document#event:keydown
 * @param {module:engine/view/observer/keyobserver~KeyEventData} keyEventData
 */
/**
 * Fired when a key has been released.
 *
 * Introduced by {@link module:engine/view/observer/keyobserver~KeyObserver}.
 *
 * Note that because {@link module:engine/view/observer/keyobserver~KeyObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/keyobserver~KeyObserver
 * @event module:engine/view/document~Document#event:keyup
 * @param {module:engine/view/observer/keyobserver~KeyEventData} keyEventData
 */
/**
 * The value of both events - {@link module:engine/view/document~Document#event:keydown} and
 * {@link module:engine/view/document~Document#event:keyup}.
 *
 * @class module:engine/view/observer/keyobserver~KeyEventData
 * @extends module:engine/view/observer/domeventdata~DomEventData
 * @implements module:utils/keyboard~KeystrokeInfo
 */
/**
 * Code of the whole keystroke. See {@link module:utils/keyboard~getCode}.
 *
 * @readonly
 * @member {Number} module:engine/view/observer/keyobserver~KeyEventData#keystroke
 */

;// CONCATENATED MODULE: ./node_modules/lodash-es/now.js


/**
 * Gets the timestamp of the number of milliseconds that have elapsed since
 * the Unix epoch (1 January 1970 00:00:00 UTC).
 *
 * @static
 * @memberOf _
 * @since 2.4.0
 * @category Date
 * @returns {number} Returns the timestamp.
 * @example
 *
 * _.defer(function(stamp) {
 *   console.log(_.now() - stamp);
 * }, _.now());
 * // => Logs the number of milliseconds it took for the deferred invocation.
 */
var now = function() {
  return _root.Date.now();
};

/* harmony default export */ const lodash_es_now = (now);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_trimmedEndIndex.js
/** Used to match a single whitespace character. */
var reWhitespace = /\s/;

/**
 * Used by `_.trim` and `_.trimEnd` to get the index of the last non-whitespace
 * character of `string`.
 *
 * @private
 * @param {string} string The string to inspect.
 * @returns {number} Returns the index of the last non-whitespace character.
 */
function trimmedEndIndex(string) {
  var index = string.length;

  while (index-- && reWhitespace.test(string.charAt(index))) {}
  return index;
}

/* harmony default export */ const _trimmedEndIndex = (trimmedEndIndex);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseTrim.js


/** Used to match leading whitespace. */
var reTrimStart = /^\s+/;

/**
 * The base implementation of `_.trim`.
 *
 * @private
 * @param {string} string The string to trim.
 * @returns {string} Returns the trimmed string.
 */
function baseTrim(string) {
  return string
    ? string.slice(0, _trimmedEndIndex(string) + 1).replace(reTrimStart, '')
    : string;
}

/* harmony default export */ const _baseTrim = (baseTrim);

;// CONCATENATED MODULE: ./node_modules/lodash-es/toNumber.js




/** Used as references for various `Number` constants. */
var NAN = 0 / 0;

/** Used to detect bad signed hexadecimal string values. */
var reIsBadHex = /^[-+]0x[0-9a-f]+$/i;

/** Used to detect binary string values. */
var reIsBinary = /^0b[01]+$/i;

/** Used to detect octal string values. */
var reIsOctal = /^0o[0-7]+$/i;

/** Built-in method references without a dependency on `root`. */
var freeParseInt = parseInt;

/**
 * Converts `value` to a number.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to process.
 * @returns {number} Returns the number.
 * @example
 *
 * _.toNumber(3.2);
 * // => 3.2
 *
 * _.toNumber(Number.MIN_VALUE);
 * // => 5e-324
 *
 * _.toNumber(Infinity);
 * // => Infinity
 *
 * _.toNumber('3.2');
 * // => 3.2
 */
function toNumber(value) {
  if (typeof value == 'number') {
    return value;
  }
  if (lodash_es_isSymbol(value)) {
    return NAN;
  }
  if (lodash_es_isObject(value)) {
    var other = typeof value.valueOf == 'function' ? value.valueOf() : value;
    value = lodash_es_isObject(other) ? (other + '') : other;
  }
  if (typeof value != 'string') {
    return value === 0 ? value : +value;
  }
  value = _baseTrim(value);
  var isBinary = reIsBinary.test(value);
  return (isBinary || reIsOctal.test(value))
    ? freeParseInt(value.slice(2), isBinary ? 2 : 8)
    : (reIsBadHex.test(value) ? NAN : +value);
}

/* harmony default export */ const lodash_es_toNumber = (toNumber);

;// CONCATENATED MODULE: ./node_modules/lodash-es/debounce.js




/** Error message constants. */
var debounce_FUNC_ERROR_TEXT = 'Expected a function';

/* Built-in method references for those with the same name as other `lodash` methods. */
var debounce_nativeMax = Math.max,
    nativeMin = Math.min;

/**
 * Creates a debounced function that delays invoking `func` until after `wait`
 * milliseconds have elapsed since the last time the debounced function was
 * invoked. The debounced function comes with a `cancel` method to cancel
 * delayed `func` invocations and a `flush` method to immediately invoke them.
 * Provide `options` to indicate whether `func` should be invoked on the
 * leading and/or trailing edge of the `wait` timeout. The `func` is invoked
 * with the last arguments provided to the debounced function. Subsequent
 * calls to the debounced function return the result of the last `func`
 * invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the debounced function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.debounce` and `_.throttle`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to debounce.
 * @param {number} [wait=0] The number of milliseconds to delay.
 * @param {Object} [options={}] The options object.
 * @param {boolean} [options.leading=false]
 *  Specify invoking on the leading edge of the timeout.
 * @param {number} [options.maxWait]
 *  The maximum time `func` is allowed to be delayed before it's invoked.
 * @param {boolean} [options.trailing=true]
 *  Specify invoking on the trailing edge of the timeout.
 * @returns {Function} Returns the new debounced function.
 * @example
 *
 * // Avoid costly calculations while the window size is in flux.
 * jQuery(window).on('resize', _.debounce(calculateLayout, 150));
 *
 * // Invoke `sendMail` when clicked, debouncing subsequent calls.
 * jQuery(element).on('click', _.debounce(sendMail, 300, {
 *   'leading': true,
 *   'trailing': false
 * }));
 *
 * // Ensure `batchLog` is invoked once after 1 second of debounced calls.
 * var debounced = _.debounce(batchLog, 250, { 'maxWait': 1000 });
 * var source = new EventSource('/stream');
 * jQuery(source).on('message', debounced);
 *
 * // Cancel the trailing debounced invocation.
 * jQuery(window).on('popstate', debounced.cancel);
 */
function debounce(func, wait, options) {
  var lastArgs,
      lastThis,
      maxWait,
      result,
      timerId,
      lastCallTime,
      lastInvokeTime = 0,
      leading = false,
      maxing = false,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(debounce_FUNC_ERROR_TEXT);
  }
  wait = lodash_es_toNumber(wait) || 0;
  if (lodash_es_isObject(options)) {
    leading = !!options.leading;
    maxing = 'maxWait' in options;
    maxWait = maxing ? debounce_nativeMax(lodash_es_toNumber(options.maxWait) || 0, wait) : maxWait;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }

  function invokeFunc(time) {
    var args = lastArgs,
        thisArg = lastThis;

    lastArgs = lastThis = undefined;
    lastInvokeTime = time;
    result = func.apply(thisArg, args);
    return result;
  }

  function leadingEdge(time) {
    // Reset any `maxWait` timer.
    lastInvokeTime = time;
    // Start the timer for the trailing edge.
    timerId = setTimeout(timerExpired, wait);
    // Invoke the leading edge.
    return leading ? invokeFunc(time) : result;
  }

  function remainingWait(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime,
        timeWaiting = wait - timeSinceLastCall;

    return maxing
      ? nativeMin(timeWaiting, maxWait - timeSinceLastInvoke)
      : timeWaiting;
  }

  function shouldInvoke(time) {
    var timeSinceLastCall = time - lastCallTime,
        timeSinceLastInvoke = time - lastInvokeTime;

    // Either this is the first call, activity has stopped and we're at the
    // trailing edge, the system time has gone backwards and we're treating
    // it as the trailing edge, or we've hit the `maxWait` limit.
    return (lastCallTime === undefined || (timeSinceLastCall >= wait) ||
      (timeSinceLastCall < 0) || (maxing && timeSinceLastInvoke >= maxWait));
  }

  function timerExpired() {
    var time = lodash_es_now();
    if (shouldInvoke(time)) {
      return trailingEdge(time);
    }
    // Restart the timer.
    timerId = setTimeout(timerExpired, remainingWait(time));
  }

  function trailingEdge(time) {
    timerId = undefined;

    // Only invoke if we have `lastArgs` which means `func` has been
    // debounced at least once.
    if (trailing && lastArgs) {
      return invokeFunc(time);
    }
    lastArgs = lastThis = undefined;
    return result;
  }

  function cancel() {
    if (timerId !== undefined) {
      clearTimeout(timerId);
    }
    lastInvokeTime = 0;
    lastArgs = lastCallTime = lastThis = timerId = undefined;
  }

  function flush() {
    return timerId === undefined ? result : trailingEdge(lodash_es_now());
  }

  function debounced() {
    var time = lodash_es_now(),
        isInvoking = shouldInvoke(time);

    lastArgs = arguments;
    lastThis = this;
    lastCallTime = time;

    if (isInvoking) {
      if (timerId === undefined) {
        return leadingEdge(lastCallTime);
      }
      if (maxing) {
        // Handle invocations in a tight loop.
        clearTimeout(timerId);
        timerId = setTimeout(timerExpired, wait);
        return invokeFunc(lastCallTime);
      }
    }
    if (timerId === undefined) {
      timerId = setTimeout(timerExpired, wait);
    }
    return result;
  }
  debounced.cancel = cancel;
  debounced.flush = flush;
  return debounced;
}

/* harmony default export */ const lodash_es_debounce = (debounce);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/fakeselectionobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/fakeselectionobserver
 */




/**
 * Fake selection observer class. If view selection is fake it is placed in dummy DOM container. This observer listens
 * on {@link module:engine/view/document~Document#event:keydown keydown} events and handles moving fake view selection to the correct place
 * if arrow keys are pressed.
 * Fires {@link module:engine/view/document~Document#event:selectionChange selectionChange event} simulating natural behaviour of
 * {@link module:engine/view/observer/selectionobserver~SelectionObserver SelectionObserver}.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class FakeSelectionObserver extends Observer {
    /**
     * Creates new FakeSelectionObserver instance.
     *
     * @param {module:engine/view/view~View} view
     */
    constructor(view) {
        super(view);
        /**
         * Fires debounced event `selectionChangeDone`. It uses `lodash#debounce` method to delay function call.
         *
         * @private
         * @param {Object} data Selection change data.
         * @method #_fireSelectionChangeDoneDebounced
         */
        this._fireSelectionChangeDoneDebounced = lodash_es_debounce(data => {
            this.document.fire('selectionChangeDone', data);
        }, 200);
    }
    /**
     * @inheritDoc
     */
    observe() {
        const document = this.document;
        document.on('arrowKey', (eventInfo, data) => {
            const selection = document.selection;
            if (selection.isFake && this.isEnabled) {
                // Prevents default key down handling - no selection change will occur.
                data.preventDefault();
            }
        }, { context: '$capture' });
        document.on('arrowKey', (eventInfo, data) => {
            const selection = document.selection;
            if (selection.isFake && this.isEnabled) {
                this._handleSelectionMove(data.keyCode);
            }
        }, { priority: 'lowest' });
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this._fireSelectionChangeDoneDebounced.cancel();
    }
    /**
     * Handles collapsing view selection according to given key code. If left or up key is provided - new selection will be
     * collapsed to left. If right or down key is pressed - new selection will be collapsed to right.
     *
     * This method fires {@link module:engine/view/document~Document#event:selectionChange} and
     * {@link module:engine/view/document~Document#event:selectionChangeDone} events imitating behaviour of
     * {@link module:engine/view/observer/selectionobserver~SelectionObserver}.
     *
     * @private
     * @param {Number} keyCode
     * @fires module:engine/view/document~Document#event:selectionChange
     * @fires module:engine/view/document~Document#event:selectionChangeDone
     */
    _handleSelectionMove(keyCode) {
        const selection = this.document.selection;
        const newSelection = new Selection(selection.getRanges(), { backward: selection.isBackward, fake: false });
        // Left or up arrow pressed - move selection to start.
        if (keyCode == keyCodes.arrowleft || keyCode == keyCodes.arrowup) {
            newSelection.setTo(newSelection.getFirstPosition());
        }
        // Right or down arrow pressed - move selection to end.
        if (keyCode == keyCodes.arrowright || keyCode == keyCodes.arrowdown) {
            newSelection.setTo(newSelection.getLastPosition());
        }
        const data = {
            oldSelection: selection,
            newSelection,
            domSelection: null
        };
        // Fire dummy selection change event.
        this.document.fire('selectionChange', data);
        // Call` #_fireSelectionChangeDoneDebounced` every time when `selectionChange` event is fired.
        // This function is debounced what means that `selectionChangeDone` event will be fired only when
        // defined int the function time will elapse since the last time the function was called.
        // So `selectionChangeDone` will be fired when selection will stop changing.
        this._fireSelectionChangeDoneDebounced(data);
    }
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/_setCacheAdd.js
/** Used to stand-in for `undefined` hash values. */
var _setCacheAdd_HASH_UNDEFINED = '__lodash_hash_undefined__';

/**
 * Adds `value` to the array cache.
 *
 * @private
 * @name add
 * @memberOf SetCache
 * @alias push
 * @param {*} value The value to cache.
 * @returns {Object} Returns the cache instance.
 */
function setCacheAdd(value) {
  this.__data__.set(value, _setCacheAdd_HASH_UNDEFINED);
  return this;
}

/* harmony default export */ const _setCacheAdd = (setCacheAdd);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_setCacheHas.js
/**
 * Checks if `value` is in the array cache.
 *
 * @private
 * @name has
 * @memberOf SetCache
 * @param {*} value The value to search for.
 * @returns {number} Returns `true` if `value` is found, else `false`.
 */
function setCacheHas(value) {
  return this.__data__.has(value);
}

/* harmony default export */ const _setCacheHas = (setCacheHas);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_SetCache.js




/**
 *
 * Creates an array cache object to store unique values.
 *
 * @private
 * @constructor
 * @param {Array} [values] The values to cache.
 */
function SetCache(values) {
  var index = -1,
      length = values == null ? 0 : values.length;

  this.__data__ = new _MapCache;
  while (++index < length) {
    this.add(values[index]);
  }
}

// Add methods to `SetCache`.
SetCache.prototype.add = SetCache.prototype.push = _setCacheAdd;
SetCache.prototype.has = _setCacheHas;

/* harmony default export */ const _SetCache = (SetCache);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_arraySome.js
/**
 * A specialized version of `_.some` for arrays without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} [array] The array to iterate over.
 * @param {Function} predicate The function invoked per iteration.
 * @returns {boolean} Returns `true` if any element passes the predicate check,
 *  else `false`.
 */
function arraySome(array, predicate) {
  var index = -1,
      length = array == null ? 0 : array.length;

  while (++index < length) {
    if (predicate(array[index], index, array)) {
      return true;
    }
  }
  return false;
}

/* harmony default export */ const _arraySome = (arraySome);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_cacheHas.js
/**
 * Checks if a `cache` value for `key` exists.
 *
 * @private
 * @param {Object} cache The cache to query.
 * @param {string} key The key of the entry to check.
 * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
 */
function cacheHas(cache, key) {
  return cache.has(key);
}

/* harmony default export */ const _cacheHas = (cacheHas);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_equalArrays.js




/** Used to compose bitmasks for value comparisons. */
var COMPARE_PARTIAL_FLAG = 1,
    COMPARE_UNORDERED_FLAG = 2;

/**
 * A specialized version of `baseIsEqualDeep` for arrays with support for
 * partial deep comparisons.
 *
 * @private
 * @param {Array} array The array to compare.
 * @param {Array} other The other array to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `array` and `other` objects.
 * @returns {boolean} Returns `true` if the arrays are equivalent, else `false`.
 */
function equalArrays(array, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & COMPARE_PARTIAL_FLAG,
      arrLength = array.length,
      othLength = other.length;

  if (arrLength != othLength && !(isPartial && othLength > arrLength)) {
    return false;
  }
  // Check that cyclic values are equal.
  var arrStacked = stack.get(array);
  var othStacked = stack.get(other);
  if (arrStacked && othStacked) {
    return arrStacked == other && othStacked == array;
  }
  var index = -1,
      result = true,
      seen = (bitmask & COMPARE_UNORDERED_FLAG) ? new _SetCache : undefined;

  stack.set(array, other);
  stack.set(other, array);

  // Ignore non-index properties.
  while (++index < arrLength) {
    var arrValue = array[index],
        othValue = other[index];

    if (customizer) {
      var compared = isPartial
        ? customizer(othValue, arrValue, index, other, array, stack)
        : customizer(arrValue, othValue, index, array, other, stack);
    }
    if (compared !== undefined) {
      if (compared) {
        continue;
      }
      result = false;
      break;
    }
    // Recursively compare arrays (susceptible to call stack limits).
    if (seen) {
      if (!_arraySome(other, function(othValue, othIndex) {
            if (!_cacheHas(seen, othIndex) &&
                (arrValue === othValue || equalFunc(arrValue, othValue, bitmask, customizer, stack))) {
              return seen.push(othIndex);
            }
          })) {
        result = false;
        break;
      }
    } else if (!(
          arrValue === othValue ||
            equalFunc(arrValue, othValue, bitmask, customizer, stack)
        )) {
      result = false;
      break;
    }
  }
  stack['delete'](array);
  stack['delete'](other);
  return result;
}

/* harmony default export */ const _equalArrays = (equalArrays);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_mapToArray.js
/**
 * Converts `map` to its key-value pairs.
 *
 * @private
 * @param {Object} map The map to convert.
 * @returns {Array} Returns the key-value pairs.
 */
function mapToArray(map) {
  var index = -1,
      result = Array(map.size);

  map.forEach(function(value, key) {
    result[++index] = [key, value];
  });
  return result;
}

/* harmony default export */ const _mapToArray = (mapToArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_setToArray.js
/**
 * Converts `set` to an array of its values.
 *
 * @private
 * @param {Object} set The set to convert.
 * @returns {Array} Returns the values.
 */
function setToArray(set) {
  var index = -1,
      result = Array(set.size);

  set.forEach(function(value) {
    result[++index] = value;
  });
  return result;
}

/* harmony default export */ const _setToArray = (setToArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_equalByTag.js







/** Used to compose bitmasks for value comparisons. */
var _equalByTag_COMPARE_PARTIAL_FLAG = 1,
    _equalByTag_COMPARE_UNORDERED_FLAG = 2;

/** `Object#toString` result references. */
var _equalByTag_boolTag = '[object Boolean]',
    _equalByTag_dateTag = '[object Date]',
    _equalByTag_errorTag = '[object Error]',
    _equalByTag_mapTag = '[object Map]',
    _equalByTag_numberTag = '[object Number]',
    _equalByTag_regexpTag = '[object RegExp]',
    _equalByTag_setTag = '[object Set]',
    _equalByTag_stringTag = '[object String]',
    _equalByTag_symbolTag = '[object Symbol]';

var _equalByTag_arrayBufferTag = '[object ArrayBuffer]',
    _equalByTag_dataViewTag = '[object DataView]';

/** Used to convert symbols to primitives and strings. */
var _equalByTag_symbolProto = _Symbol ? _Symbol.prototype : undefined,
    _equalByTag_symbolValueOf = _equalByTag_symbolProto ? _equalByTag_symbolProto.valueOf : undefined;

/**
 * A specialized version of `baseIsEqualDeep` for comparing objects of
 * the same `toStringTag`.
 *
 * **Note:** This function only supports comparing values with tags of
 * `Boolean`, `Date`, `Error`, `Number`, `RegExp`, or `String`.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {string} tag The `toStringTag` of the objects to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function equalByTag(object, other, tag, bitmask, customizer, equalFunc, stack) {
  switch (tag) {
    case _equalByTag_dataViewTag:
      if ((object.byteLength != other.byteLength) ||
          (object.byteOffset != other.byteOffset)) {
        return false;
      }
      object = object.buffer;
      other = other.buffer;

    case _equalByTag_arrayBufferTag:
      if ((object.byteLength != other.byteLength) ||
          !equalFunc(new _Uint8Array(object), new _Uint8Array(other))) {
        return false;
      }
      return true;

    case _equalByTag_boolTag:
    case _equalByTag_dateTag:
    case _equalByTag_numberTag:
      // Coerce booleans to `1` or `0` and dates to milliseconds.
      // Invalid dates are coerced to `NaN`.
      return lodash_es_eq(+object, +other);

    case _equalByTag_errorTag:
      return object.name == other.name && object.message == other.message;

    case _equalByTag_regexpTag:
    case _equalByTag_stringTag:
      // Coerce regexes to strings and treat strings, primitives and objects,
      // as equal. See http://www.ecma-international.org/ecma-262/7.0/#sec-regexp.prototype.tostring
      // for more details.
      return object == (other + '');

    case _equalByTag_mapTag:
      var convert = _mapToArray;

    case _equalByTag_setTag:
      var isPartial = bitmask & _equalByTag_COMPARE_PARTIAL_FLAG;
      convert || (convert = _setToArray);

      if (object.size != other.size && !isPartial) {
        return false;
      }
      // Assume cyclic values are equal.
      var stacked = stack.get(object);
      if (stacked) {
        return stacked == other;
      }
      bitmask |= _equalByTag_COMPARE_UNORDERED_FLAG;

      // Recursively compare objects (susceptible to call stack limits).
      stack.set(object, other);
      var result = _equalArrays(convert(object), convert(other), bitmask, customizer, equalFunc, stack);
      stack['delete'](object);
      return result;

    case _equalByTag_symbolTag:
      if (_equalByTag_symbolValueOf) {
        return _equalByTag_symbolValueOf.call(object) == _equalByTag_symbolValueOf.call(other);
      }
  }
  return false;
}

/* harmony default export */ const _equalByTag = (equalByTag);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_equalObjects.js


/** Used to compose bitmasks for value comparisons. */
var _equalObjects_COMPARE_PARTIAL_FLAG = 1;

/** Used for built-in method references. */
var _equalObjects_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _equalObjects_hasOwnProperty = _equalObjects_objectProto.hasOwnProperty;

/**
 * A specialized version of `baseIsEqualDeep` for objects with support for
 * partial deep comparisons.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} stack Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function equalObjects(object, other, bitmask, customizer, equalFunc, stack) {
  var isPartial = bitmask & _equalObjects_COMPARE_PARTIAL_FLAG,
      objProps = _getAllKeys(object),
      objLength = objProps.length,
      othProps = _getAllKeys(other),
      othLength = othProps.length;

  if (objLength != othLength && !isPartial) {
    return false;
  }
  var index = objLength;
  while (index--) {
    var key = objProps[index];
    if (!(isPartial ? key in other : _equalObjects_hasOwnProperty.call(other, key))) {
      return false;
    }
  }
  // Check that cyclic values are equal.
  var objStacked = stack.get(object);
  var othStacked = stack.get(other);
  if (objStacked && othStacked) {
    return objStacked == other && othStacked == object;
  }
  var result = true;
  stack.set(object, other);
  stack.set(other, object);

  var skipCtor = isPartial;
  while (++index < objLength) {
    key = objProps[index];
    var objValue = object[key],
        othValue = other[key];

    if (customizer) {
      var compared = isPartial
        ? customizer(othValue, objValue, key, other, object, stack)
        : customizer(objValue, othValue, key, object, other, stack);
    }
    // Recursively compare objects (susceptible to call stack limits).
    if (!(compared === undefined
          ? (objValue === othValue || equalFunc(objValue, othValue, bitmask, customizer, stack))
          : compared
        )) {
      result = false;
      break;
    }
    skipCtor || (skipCtor = key == 'constructor');
  }
  if (result && !skipCtor) {
    var objCtor = object.constructor,
        othCtor = other.constructor;

    // Non `Object` object instances with different constructors are not equal.
    if (objCtor != othCtor &&
        ('constructor' in object && 'constructor' in other) &&
        !(typeof objCtor == 'function' && objCtor instanceof objCtor &&
          typeof othCtor == 'function' && othCtor instanceof othCtor)) {
      result = false;
    }
  }
  stack['delete'](object);
  stack['delete'](other);
  return result;
}

/* harmony default export */ const _equalObjects = (equalObjects);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsEqualDeep.js









/** Used to compose bitmasks for value comparisons. */
var _baseIsEqualDeep_COMPARE_PARTIAL_FLAG = 1;

/** `Object#toString` result references. */
var _baseIsEqualDeep_argsTag = '[object Arguments]',
    _baseIsEqualDeep_arrayTag = '[object Array]',
    _baseIsEqualDeep_objectTag = '[object Object]';

/** Used for built-in method references. */
var _baseIsEqualDeep_objectProto = Object.prototype;

/** Used to check objects for own properties. */
var _baseIsEqualDeep_hasOwnProperty = _baseIsEqualDeep_objectProto.hasOwnProperty;

/**
 * A specialized version of `baseIsEqual` for arrays and objects which performs
 * deep comparisons and tracks traversed objects enabling objects with circular
 * references to be compared.
 *
 * @private
 * @param {Object} object The object to compare.
 * @param {Object} other The other object to compare.
 * @param {number} bitmask The bitmask flags. See `baseIsEqual` for more details.
 * @param {Function} customizer The function to customize comparisons.
 * @param {Function} equalFunc The function to determine equivalents of values.
 * @param {Object} [stack] Tracks traversed `object` and `other` objects.
 * @returns {boolean} Returns `true` if the objects are equivalent, else `false`.
 */
function baseIsEqualDeep(object, other, bitmask, customizer, equalFunc, stack) {
  var objIsArr = lodash_es_isArray(object),
      othIsArr = lodash_es_isArray(other),
      objTag = objIsArr ? _baseIsEqualDeep_arrayTag : _getTag(object),
      othTag = othIsArr ? _baseIsEqualDeep_arrayTag : _getTag(other);

  objTag = objTag == _baseIsEqualDeep_argsTag ? _baseIsEqualDeep_objectTag : objTag;
  othTag = othTag == _baseIsEqualDeep_argsTag ? _baseIsEqualDeep_objectTag : othTag;

  var objIsObj = objTag == _baseIsEqualDeep_objectTag,
      othIsObj = othTag == _baseIsEqualDeep_objectTag,
      isSameTag = objTag == othTag;

  if (isSameTag && lodash_es_isBuffer(object)) {
    if (!lodash_es_isBuffer(other)) {
      return false;
    }
    objIsArr = true;
    objIsObj = false;
  }
  if (isSameTag && !objIsObj) {
    stack || (stack = new _Stack);
    return (objIsArr || lodash_es_isTypedArray(object))
      ? _equalArrays(object, other, bitmask, customizer, equalFunc, stack)
      : _equalByTag(object, other, objTag, bitmask, customizer, equalFunc, stack);
  }
  if (!(bitmask & _baseIsEqualDeep_COMPARE_PARTIAL_FLAG)) {
    var objIsWrapped = objIsObj && _baseIsEqualDeep_hasOwnProperty.call(object, '__wrapped__'),
        othIsWrapped = othIsObj && _baseIsEqualDeep_hasOwnProperty.call(other, '__wrapped__');

    if (objIsWrapped || othIsWrapped) {
      var objUnwrapped = objIsWrapped ? object.value() : object,
          othUnwrapped = othIsWrapped ? other.value() : other;

      stack || (stack = new _Stack);
      return equalFunc(objUnwrapped, othUnwrapped, bitmask, customizer, stack);
    }
  }
  if (!isSameTag) {
    return false;
  }
  stack || (stack = new _Stack);
  return _equalObjects(object, other, bitmask, customizer, equalFunc, stack);
}

/* harmony default export */ const _baseIsEqualDeep = (baseIsEqualDeep);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsEqual.js



/**
 * The base implementation of `_.isEqual` which supports partial comparisons
 * and tracks traversed objects.
 *
 * @private
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @param {boolean} bitmask The bitmask flags.
 *  1 - Unordered comparison
 *  2 - Partial comparison
 * @param {Function} [customizer] The function to customize comparisons.
 * @param {Object} [stack] Tracks traversed `value` and `other` objects.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 */
function baseIsEqual(value, other, bitmask, customizer, stack) {
  if (value === other) {
    return true;
  }
  if (value == null || other == null || (!lodash_es_isObjectLike(value) && !lodash_es_isObjectLike(other))) {
    return value !== value && other !== other;
  }
  return _baseIsEqualDeep(value, other, bitmask, customizer, baseIsEqual, stack);
}

/* harmony default export */ const _baseIsEqual = (baseIsEqual);

;// CONCATENATED MODULE: ./node_modules/lodash-es/isEqualWith.js


/**
 * This method is like `_.isEqual` except that it accepts `customizer` which
 * is invoked to compare values. If `customizer` returns `undefined`, comparisons
 * are handled by the method instead. The `customizer` is invoked with up to
 * six arguments: (objValue, othValue [, index|key, object, other, stack]).
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Lang
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @param {Function} [customizer] The function to customize comparisons.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 * @example
 *
 * function isGreeting(value) {
 *   return /^h(?:i|ello)$/.test(value);
 * }
 *
 * function customizer(objValue, othValue) {
 *   if (isGreeting(objValue) && isGreeting(othValue)) {
 *     return true;
 *   }
 * }
 *
 * var array = ['hello', 'goodbye'];
 * var other = ['hi', 'goodbye'];
 *
 * _.isEqualWith(array, other, customizer);
 * // => true
 */
function isEqualWith(value, other, customizer) {
  customizer = typeof customizer == 'function' ? customizer : undefined;
  var result = customizer ? customizer(value, other) : undefined;
  return result === undefined ? _baseIsEqual(value, other, undefined, customizer) : !!result;
}

/* harmony default export */ const lodash_es_isEqualWith = (isEqualWith);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/mutationobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/mutationobserver
 */
/* globals window */



/**
 * Mutation observer's role is to watch for any DOM changes inside the editor that weren't
 * done by the editor's {@link module:engine/view/renderer~Renderer} itself and reverting these changes.
 *
 * It does this by observing all mutations in the DOM, marking related view elements as changed and calling
 * {@link module:engine/view/renderer~Renderer#render}. Because all mutated nodes are marked as
 * "to be rendered" and the {@link module:engine/view/renderer~Renderer#render `render()`} method is called,
 * all changes are reverted in the DOM (the DOM is synced with the editor's view structure).
 *
 * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class MutationObserver extends Observer {
    constructor(view) {
        super(view);
        /**
         * Native mutation observer config.
         *
         * @private
         * @member {Object}
         */
        this._config = {
            childList: true,
            characterData: true,
            subtree: true
        };
        /**
         * Reference to the {@link module:engine/view/view~View#domConverter}.
         *
         * @member {module:engine/view/domconverter~DomConverter}
         */
        this.domConverter = view.domConverter;
        /**
         * Reference to the {@link module:engine/view/view~View#_renderer}.
         *
         * @member {module:engine/view/renderer~Renderer}
         */
        this.renderer = view._renderer;
        /**
         * Observed DOM elements.
         *
         * @private
         * @member {Array.<HTMLElement>}
         */
        this._domElements = [];
        /**
         * Native mutation observer.
         *
         * @private
         * @member {MutationObserver}
         */
        this._mutationObserver = new window.MutationObserver(this._onMutations.bind(this));
    }
    /**
     * Synchronously handles mutations and empties the queue.
     */
    flush() {
        this._onMutations(this._mutationObserver.takeRecords());
    }
    /**
     * @inheritDoc
     */
    observe(domElement) {
        this._domElements.push(domElement);
        if (this.isEnabled) {
            this._mutationObserver.observe(domElement, this._config);
        }
    }
    /**
     * @inheritDoc
     */
    enable() {
        super.enable();
        for (const domElement of this._domElements) {
            this._mutationObserver.observe(domElement, this._config);
        }
    }
    /**
     * @inheritDoc
     */
    disable() {
        super.disable();
        this._mutationObserver.disconnect();
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this._mutationObserver.disconnect();
    }
    /**
     * Handles mutations. Mark view elements to sync and call render.
     *
     * @private
     * @param {Array.<Object>} domMutations Array of native mutations.
     */
    _onMutations(domMutations) {
        // As a result of this.flush() we can have an empty collection.
        if (domMutations.length === 0) {
            return;
        }
        const domConverter = this.domConverter;
        // Use map and set for deduplication.
        const mutatedTextNodes = new Set();
        const elementsWithMutatedChildren = new Set();
        // Handle `childList` mutations first, so we will be able to check if the `characterData` mutation is in the
        // element with changed structure anyway.
        for (const mutation of domMutations) {
            const element = domConverter.mapDomToView(mutation.target);
            if (!element) {
                continue;
            }
            // Do not collect mutations from UIElements and RawElements.
            if (element.is('uiElement') || element.is('rawElement')) {
                continue;
            }
            if (mutation.type === 'childList' && !this._isBogusBrMutation(mutation)) {
                elementsWithMutatedChildren.add(element);
            }
        }
        // Handle `characterData` mutations later, when we have the full list of nodes which changed structure.
        for (const mutation of domMutations) {
            const element = domConverter.mapDomToView(mutation.target);
            // Do not collect mutations from UIElements and RawElements.
            if (element && (element.is('uiElement') || element.is('rawElement'))) {
                continue;
            }
            if (mutation.type === 'characterData') {
                const text = domConverter.findCorrespondingViewText(mutation.target);
                if (text && !elementsWithMutatedChildren.has(text.parent)) {
                    mutatedTextNodes.add(text);
                }
                // When we added first letter to the text node which had only inline filler, for the DOM it is mutation
                // on text, but for the view, where filler text node did not exist, new text node was created, so we
                // need to handle it as a 'children' mutation instead of 'text'.
                else if (!text && startsWithFiller(mutation.target)) {
                    elementsWithMutatedChildren.add(domConverter.mapDomToView(mutation.target.parentNode));
                }
            }
        }
        // Now we build the list of mutations to mark elements. We did not do it earlier to avoid marking the
        // same node multiple times in case of duplication.
        let hasMutations = false;
        for (const textNode of mutatedTextNodes) {
            hasMutations = true;
            this.renderer.markToSync('text', textNode);
        }
        for (const viewElement of elementsWithMutatedChildren) {
            const domElement = domConverter.mapViewToDom(viewElement);
            const viewChildren = Array.from(viewElement.getChildren());
            const newViewChildren = Array.from(domConverter.domChildrenToView(domElement, { withChildren: false }));
            // It may happen that as a result of many changes (sth was inserted and then removed),
            // both elements haven't really changed. #1031
            if (!lodash_es_isEqualWith(viewChildren, newViewChildren, mutationobserver_sameNodes)) {
                hasMutations = true;
                this.renderer.markToSync('children', viewElement);
            }
        }
        // In case only non-relevant mutations were recorded it skips the event and force render (#5600).
        if (hasMutations) {
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.group( '%c[MutationObserver]%c Mutations detected',
            // @if CK_DEBUG_TYPING // 		'font-weight:bold;color:green', ''
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            // At this point we have "dirty DOM" (changed) and de-synched view (which has not been changed).
            // In order to "reset DOM" we render the view again.
            this.view.forceRender();
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.groupEnd();
            // @if CK_DEBUG_TYPING // }
        }
    }
    /**
     * Checks if mutation was generated by the browser inserting bogus br on the end of the block element.
     * Such mutations are generated while pressing space or performing native spellchecker correction
     * on the end of the block element in Firefox browser.
     *
     * @private
     * @param {Object} mutation Native mutation object.
     * @returns {Boolean}
     */
    _isBogusBrMutation(mutation) {
        let addedNode = null;
        // Check if mutation added only one node on the end of its parent.
        if (mutation.nextSibling === null && mutation.removedNodes.length === 0 && mutation.addedNodes.length == 1) {
            addedNode = this.domConverter.domToView(mutation.addedNodes[0], {
                withChildren: false
            });
        }
        return addedNode && addedNode.is('element', 'br');
    }
}
function mutationobserver_sameNodes(child1, child2) {
    // First level of comparison (array of children vs array of children) – use the Lodash's default behavior.
    if (Array.isArray(child1)) {
        return;
    }
    // Elements.
    if (child1 === child2) {
        return true;
    }
    // Texts.
    else if (child1.is('$text') && child2.is('$text')) {
        return child1.data === child2.data;
    }
    // Not matching types.
    return false;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/selectionobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/selectionobserver
 */
/* global setInterval, clearInterval */




/**
 * Selection observer class observes selection changes in the document. If a selection changes on the document this
 * observer checks if the DOM selection is different from the {@link module:engine/view/document~Document#selection view selection}.
 * The selection observer fires {@link module:engine/view/document~Document#event:selectionChange} event only if
 * a selection change was the only change in the document and the DOM selection is different from the view selection.
 *
 * This observer also manages the {@link module:engine/view/document~Document#isSelecting} property of the view document.
 *
 * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class SelectionObserver extends Observer {
    constructor(view) {
        super(view);
        /**
         * Instance of the mutation observer. Selection observer calls
         * {@link module:engine/view/observer/mutationobserver~MutationObserver#flush} to ensure that the mutations will be handled
         * before the {@link module:engine/view/document~Document#event:selectionChange} event is fired.
         *
         * @readonly
         * @member {module:engine/view/observer/mutationobserver~MutationObserver}
         * module:engine/view/observer/selectionobserver~SelectionObserver#mutationObserver
         */
        this.mutationObserver = view.getObserver(MutationObserver);
        /**
         * Reference to the view {@link module:engine/view/documentselection~DocumentSelection} object used to compare
         * new selection with it.
         *
         * @readonly
         * @member {module:engine/view/documentselection~DocumentSelection}
         * module:engine/view/observer/selectionobserver~SelectionObserver#selection
         */
        this.selection = this.document.selection;
        /* eslint-disable max-len */
        /**
         * Reference to the {@link module:engine/view/view~View#domConverter}.
         *
         * @readonly
         * @member {module:engine/view/domconverter~DomConverter} module:engine/view/observer/selectionobserver~SelectionObserver#domConverter
         */
        /* eslint-enable max-len */
        this.domConverter = view.domConverter;
        /**
         * A set of documents which have added `selectionchange` listener to avoid adding a listener twice to the same
         * document.
         *
         * @private
         * @member {WeakSet.<Document>} module:engine/view/observer/selectionobserver~SelectionObserver#_documents
         */
        this._documents = new WeakSet();
        /**
         * Fires debounced event `selectionChangeDone`. It uses `lodash#debounce` method to delay function call.
         *
         * @private
         * @param {Object} data Selection change data.
         * @method #_fireSelectionChangeDoneDebounced
         */
        this._fireSelectionChangeDoneDebounced = lodash_es_debounce(data => {
            this.document.fire('selectionChangeDone', data);
        }, 200);
        /**
         * When called, starts clearing the {@link #_loopbackCounter} counter in time intervals. When the number of selection
         * changes exceeds a certain limit within the interval of time, the observer will not fire `selectionChange` but warn about
         * possible infinite selection loop.
         *
         * @private
         * @member {Number} #_clearInfiniteLoopInterval
         */
        this._clearInfiniteLoopInterval = setInterval(() => this._clearInfiniteLoop(), 1000);
        /**
         * Unlocks the `isSelecting` state of the view document in case the selection observer did not record this fact
         * correctly (for whatever reason). It is a safeguard (paranoid check), that returns document to the normal state
         * after a certain period of time (debounced, postponed by each selectionchange event).
         *
         * @private
         * @method #_documentIsSelectingInactivityTimeoutDebounced
         */
        this._documentIsSelectingInactivityTimeoutDebounced = lodash_es_debounce(() => (this.document.isSelecting = false), 5000);
        /**
         * Private property to check if the code does not enter infinite loop.
         *
         * @private
         * @member {Number} module:engine/view/observer/selectionobserver~SelectionObserver#_loopbackCounter
         */
        this._loopbackCounter = 0;
    }
    /**
     * @inheritDoc
     */
    observe(domElement) {
        const domDocument = domElement.ownerDocument;
        const startDocumentIsSelecting = () => {
            this.document.isSelecting = true;
            // Let's activate the safety timeout each time the document enters the "is selecting" state.
            this._documentIsSelectingInactivityTimeoutDebounced();
        };
        const endDocumentIsSelecting = () => {
            if (!this.document.isSelecting) {
                return;
            }
            // Make sure that model selection is up-to-date at the end of selecting process.
            // Sometimes `selectionchange` events could arrive after the `mouseup` event and that selection could be already outdated.
            this._handleSelectionChange(null, domDocument);
            this.document.isSelecting = false;
            // The safety timeout can be canceled when the document leaves the "is selecting" state.
            this._documentIsSelectingInactivityTimeoutDebounced.cancel();
        };
        // The document has the "is selecting" state while the user keeps making (extending) the selection
        // (e.g. by holding the mouse button and moving the cursor). The state resets when they either released
        // the mouse button or interrupted the process by pressing or releasing any key.
        this.listenTo(domElement, 'selectstart', startDocumentIsSelecting, { priority: 'highest' });
        this.listenTo(domElement, 'keydown', endDocumentIsSelecting, { priority: 'highest', useCapture: true });
        this.listenTo(domElement, 'keyup', endDocumentIsSelecting, { priority: 'highest', useCapture: true });
        // Add document-wide listeners only once. This method could be called for multiple editing roots.
        if (this._documents.has(domDocument)) {
            return;
        }
        // This listener is using capture mode to make sure that selection is upcasted before any other
        // handler would like to check it and update (for example table multi cell selection).
        this.listenTo(domDocument, 'mouseup', endDocumentIsSelecting, { priority: 'highest', useCapture: true });
        this.listenTo(domDocument, 'selectionchange', (evt, domEvent) => {
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	const domSelection = domDocument.defaultView.getSelection();
            // @if CK_DEBUG_TYPING // 	console.group( '%c[SelectionObserver]%c selectionchange', 'color:green', ''
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // 	console.info( '%c[SelectionObserver]%c DOM Selection:', 'font-weight:bold;color:green', '',
            // @if CK_DEBUG_TYPING // 		{ node: domSelection.anchorNode, offset: domSelection.anchorOffset },
            // @if CK_DEBUG_TYPING // 		{ node: domSelection.focusNode, offset: domSelection.focusOffset }
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            // The Renderer is disabled while composing on non-android browsers, so we can't update the view selection
            // because the DOM and view tree drifted apart. Position mapping could fail because of it.
            if (this.document.isComposing && !src_env.isAndroid) {
                // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
                // @if CK_DEBUG_TYPING // 	console.info( '%c[SelectionObserver]%c Selection change ignored (isComposing)',
                // @if CK_DEBUG_TYPING // 		'font-weight:bold;color:green', ''
                // @if CK_DEBUG_TYPING // 	);
                // @if CK_DEBUG_TYPING // 	console.groupEnd();
                // @if CK_DEBUG_TYPING // }
                return;
            }
            this._handleSelectionChange(domEvent, domDocument);
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.groupEnd();
            // @if CK_DEBUG_TYPING // }
            // Defer the safety timeout when the selection changes (e.g. the user keeps extending the selection
            // using their mouse).
            this._documentIsSelectingInactivityTimeoutDebounced();
        });
        this._documents.add(domDocument);
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        clearInterval(this._clearInfiniteLoopInterval);
        this._fireSelectionChangeDoneDebounced.cancel();
        this._documentIsSelectingInactivityTimeoutDebounced.cancel();
    }
    /**
     * Selection change listener. {@link module:engine/view/observer/mutationobserver~MutationObserver#flush Flush} mutations, check if
     * a selection changes and fires {@link module:engine/view/document~Document#event:selectionChange} event on every change
     * and {@link module:engine/view/document~Document#event:selectionChangeDone} when a selection stop changing.
     *
     * @private
     * @param {Event} domEvent DOM event.
     * @param {Document} domDocument DOM document.
     */
    _handleSelectionChange(domEvent, domDocument) {
        if (!this.isEnabled) {
            return;
        }
        const domSelection = domDocument.defaultView.getSelection();
        if (this.checkShouldIgnoreEventFromTarget(domSelection.anchorNode)) {
            return;
        }
        // Ensure the mutation event will be before selection event on all browsers.
        this.mutationObserver.flush();
        const newViewSelection = this.domConverter.domSelectionToView(domSelection);
        // Do not convert selection change if the new view selection has no ranges in it.
        //
        // It means that the DOM selection is in some way incorrect. Ranges that were in the DOM selection could not be
        // converted to the view. This happens when the DOM selection was moved outside of the editable element.
        if (newViewSelection.rangeCount == 0) {
            this.view.hasDomSelection = false;
            return;
        }
        this.view.hasDomSelection = true;
        if (this.selection.isEqual(newViewSelection) && this.domConverter.isDomSelectionCorrect(domSelection)) {
            return;
        }
        // Ensure we are not in the infinite loop (#400).
        // This counter is reset each second. 60 selection changes in 1 second is enough high number
        // to be very difficult (impossible) to achieve using just keyboard keys (during normal editor use).
        if (++this._loopbackCounter > 60) {
            // Selection change observer detected an infinite rendering loop.
            // Most probably you try to put the selection in the position which is not allowed
            // by the browser and browser fixes it automatically what causes `selectionchange` event on
            // which a loopback through a model tries to re-render the wrong selection and again.
            //
            // @if CK_DEBUG // console.warn( 'Selection change observer detected an infinite rendering loop.' );
            return;
        }
        if (this.selection.isSimilar(newViewSelection)) {
            // If selection was equal and we are at this point of algorithm, it means that it was incorrect.
            // Just re-render it, no need to fire any events, etc.
            this.view.forceRender();
        }
        else {
            const data = {
                oldSelection: this.selection,
                newSelection: newViewSelection,
                domSelection
            };
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.info( '%c[SelectionObserver]%c Fire selection change:',
            // @if CK_DEBUG_TYPING // 		'font-weight:bold;color:green', '',
            // @if CK_DEBUG_TYPING // 		newViewSelection.getFirstRange()
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            // Prepare data for new selection and fire appropriate events.
            this.document.fire('selectionChange', data);
            // Call `#_fireSelectionChangeDoneDebounced` every time when `selectionChange` event is fired.
            // This function is debounced what means that `selectionChangeDone` event will be fired only when
            // defined int the function time will elapse since the last time the function was called.
            // So `selectionChangeDone` will be fired when selection will stop changing.
            this._fireSelectionChangeDoneDebounced(data);
        }
    }
    /**
     * Clears `SelectionObserver` internal properties connected with preventing infinite loop.
     *
     * @protected
     */
    _clearInfiniteLoop() {
        this._loopbackCounter = 0;
    }
}
/**
 * Fired when selection stops changing.
 *
 * Introduced by {@link module:engine/view/observer/selectionobserver~SelectionObserver}.
 *
 * Note that because {@link module:engine/view/observer/selectionobserver~SelectionObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/selectionobserver~SelectionObserver
 * @event module:engine/view/document~Document#event:selectionChangeDone
 * @param {Object} data
 * @param {module:engine/view/documentselection~DocumentSelection} data.oldSelection Old View selection which is
 * {@link module:engine/view/document~Document#selection}.
 * @param {module:engine/view/selection~Selection} data.newSelection New View selection which is converted DOM selection.
 * @param {Selection} data.domSelection Native DOM selection.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/focusobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/focusobserver
 */
/* globals setTimeout, clearTimeout */

/**
 * {@link module:engine/view/document~Document#event:focus Focus}
 * and {@link module:engine/view/document~Document#event:blur blur} events observer.
 * Focus observer handle also {@link module:engine/view/rooteditableelement~RootEditableElement#isFocused isFocused} property of the
 * {@link module:engine/view/rooteditableelement~RootEditableElement root elements}.
 *
 * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class FocusObserver extends DomEventObserver {
    constructor(view) {
        super(view);
        this.domEventType = ['focus', 'blur'];
        this.useCapture = true;
        const document = this.document;
        document.on('focus', () => {
            document.isFocused = true;
            // Unfortunately native `selectionchange` event is fired asynchronously.
            // We need to wait until `SelectionObserver` handle the event and then render. Otherwise rendering will
            // overwrite new DOM selection with selection from the view.
            // See https://github.com/ckeditor/ckeditor5-engine/issues/795 for more details.
            // Long timeout is needed to solve #676 and https://github.com/ckeditor/ckeditor5-engine/issues/1157 issues.
            //
            // Using `view.change()` instead of `view.forceRender()` to prevent double rendering
            // in a situation where `selectionchange` already caused selection change.
            this._renderTimeoutId = setTimeout(() => view.change(() => { }), 50);
        });
        document.on('blur', (evt, data) => {
            const selectedEditable = document.selection.editableElement;
            if (selectedEditable === null || selectedEditable === data.target) {
                document.isFocused = false;
                // Re-render the document to update view elements
                // (changing document.isFocused already marked view as changed since last rendering).
                view.change(() => { });
            }
        });
        /**
         * Identifier of the timeout currently used by focus listener to delay rendering execution.
         *
         * @private
         * @member {Number} #_renderTimeoutId
         */
    }
    onDomEvent(domEvent) {
        this.fire(domEvent.type, domEvent);
    }
    /**
     * @inheritDoc
     */
    destroy() {
        if (this._renderTimeoutId) {
            clearTimeout(this._renderTimeoutId);
        }
        super.destroy();
    }
}
/**
 * Fired when one of the editables gets focus.
 *
 * Introduced by {@link module:engine/view/observer/focusobserver~FocusObserver}.
 *
 * Note that because {@link module:engine/view/observer/focusobserver~FocusObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/focusobserver~FocusObserver
 * @event module:engine/view/document~Document#event:focus
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */
/**
 * Fired when one of the editables loses focus.
 *
 * Introduced by {@link module:engine/view/observer/focusobserver~FocusObserver}.
 *
 * Note that because {@link module:engine/view/observer/focusobserver~FocusObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/focusobserver~FocusObserver
 * @event module:engine/view/document~Document#event:blur
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/compositionobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/compositionobserver
 */

/**
 * {@link module:engine/view/document~Document#event:compositionstart Compositionstart},
 * {@link module:engine/view/document~Document#event:compositionupdate compositionupdate} and
 * {@link module:engine/view/document~Document#event:compositionend compositionend} events observer.
 *
 * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class CompositionObserver extends DomEventObserver {
    constructor(view) {
        super(view);
        this.domEventType = ['compositionstart', 'compositionupdate', 'compositionend'];
        const document = this.document;
        document.on('compositionstart', () => {
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.log( '%c[CompositionObserver] ' +
            // @if CK_DEBUG_TYPING // 		'┌───────────────────────────── isComposing = true ─────────────────────────────┐',
            // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green'
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            document.isComposing = true;
        }, { priority: 'low' });
        document.on('compositionend', () => {
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.log( '%c[CompositionObserver] ' +
            // @if CK_DEBUG_TYPING // 		'└───────────────────────────── isComposing = false ─────────────────────────────┘',
            // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green'
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            document.isComposing = false;
        }, { priority: 'low' });
    }
    onDomEvent(domEvent) {
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.group( `%c[CompositionObserver]%c ${ domEvent.type }`, 'color: green', '' );
        // @if CK_DEBUG_TYPING // }
        this.fire(domEvent.type, domEvent, {
            data: domEvent.data
        });
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.groupEnd();
        // @if CK_DEBUG_TYPING // }
    }
}
/**
 * Fired when composition starts inside one of the editables.
 *
 * Introduced by {@link module:engine/view/observer/compositionobserver~CompositionObserver}.
 *
 * Note that because {@link module:engine/view/observer/compositionobserver~CompositionObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/compositionobserver~CompositionObserver
 * @event module:engine/view/document~Document#event:compositionstart
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */
/**
 * Fired when composition is updated inside one of the editables.
 *
 * Introduced by {@link module:engine/view/observer/compositionobserver~CompositionObserver}.
 *
 * Note that because {@link module:engine/view/observer/compositionobserver~CompositionObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/compositionobserver~CompositionObserver
 * @event module:engine/view/document~Document#event:compositionupdate
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */
/**
 * Fired when composition ends inside one of the editables.
 *
 * Introduced by {@link module:engine/view/observer/compositionobserver~CompositionObserver}.
 *
 * Note that because {@link module:engine/view/observer/compositionobserver~CompositionObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @see module:engine/view/observer/compositionobserver~CompositionObserver
 * @event module:engine/view/document~Document#event:compositionend
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/datatransfer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * A facade over the native [`DataTransfer`](https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer) object.
 */
class DataTransfer {
    constructor(nativeDataTransfer) {
        /**
         * The array of files created from the native `DataTransfer#files` or `DataTransfer#items`.
         *
         * @readonly
         * @member {Array.<File>} #files
         */
        this.files = getFiles(nativeDataTransfer);
        /**
         * The native DataTransfer object.
         *
         * @private
         * @member {DataTransfer} #_native
         */
        this._native = nativeDataTransfer;
    }
    /**
     * Returns an array of available native content types.
     *
     * @returns {Array.<String>}
     */
    get types() {
        return this._native.types;
    }
    /**
     * Gets the data from the data transfer by its MIME type.
     *
     *		dataTransfer.getData( 'text/plain' );
     *
     * @param {String} type The MIME type. E.g. `text/html` or `text/plain`.
     * @returns {String}
     */
    getData(type) {
        return this._native.getData(type);
    }
    /**
     * Sets the data in the data transfer.
     *
     * @param {String} type The MIME type. E.g. `text/html` or `text/plain`.
     * @param {String} data
     */
    setData(type, data) {
        this._native.setData(type, data);
    }
    /**
     * The effect that is allowed for a drag operation.
     *
     * @param {String} value
     */
    set effectAllowed(value) {
        this._native.effectAllowed = value;
    }
    get effectAllowed() {
        return this._native.effectAllowed;
    }
    /**
     * The actual drop effect.
     *
     * @param {String} value
     */
    set dropEffect(value) {
        this._native.dropEffect = value;
    }
    get dropEffect() {
        return this._native.dropEffect;
    }
    /**
     * Whether the dragging operation was canceled.
     *
     * @returns {Boolean}
     */
    get isCanceled() {
        return this._native.dropEffect == 'none' || !!this._native.mozUserCancelled;
    }
}
function getFiles(nativeDataTransfer) {
    // DataTransfer.files and items are array-like and might not have an iterable interface.
    const files = Array.from(nativeDataTransfer.files || []);
    const items = Array.from(nativeDataTransfer.items || []);
    if (files.length) {
        return files;
    }
    // Chrome has empty DataTransfer.files, but allows getting files through the items interface.
    return items
        .filter(item => item.kind === 'file')
        .map(item => item.getAsFile());
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/inputobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/inputobserver
 */



/**
 * Observer for events connected with data input.
 *
 * **Note**: This observer is attached by {@link module:engine/view/view~View} and available by default in all
 * editor instances.
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class InputObserver extends DomEventObserver {
    constructor(view) {
        super(view);
        this.domEventType = ['beforeinput'];
    }
    onDomEvent(domEvent) {
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.group( `%c[InputObserver]%c ${ domEvent.type }: ${ domEvent.inputType }`,
        // @if CK_DEBUG_TYPING // 		'color: green', 'color: default'
        // @if CK_DEBUG_TYPING // 	);
        // @if CK_DEBUG_TYPING // }
        const domTargetRanges = domEvent.getTargetRanges();
        const view = this.view;
        const viewDocument = view.document;
        let dataTransfer = null;
        let data = null;
        let targetRanges = [];
        if (domEvent.dataTransfer) {
            dataTransfer = new DataTransfer(domEvent.dataTransfer);
        }
        if (domEvent.data !== null) {
            data = domEvent.data;
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.info( `%c[InputObserver]%c event data: %c${ JSON.stringify( data ) }`,
            // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', 'font-weight:bold', 'color: blue;'
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
        }
        else if (dataTransfer) {
            data = dataTransfer.getData('text/plain');
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.info( `%c[InputObserver]%c event data transfer: %c${ JSON.stringify( data ) }`,
            // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', 'font-weight:bold', 'color: blue;'
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
        }
        // If the editor selection is fake (an object is selected), the DOM range does not make sense because it is anchored
        // in the fake selection container.
        if (viewDocument.selection.isFake) {
            // Future-proof: in case of multi-range fake selections being possible.
            targetRanges = Array.from(viewDocument.selection.getRanges());
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.info( '%c[InputObserver]%c using fake selection:',
            // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', 'font-weight:bold', targetRanges,
            // @if CK_DEBUG_TYPING // 		viewDocument.selection.isFake ? 'fake view selection' : 'fake DOM parent'
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
        }
        else if (domTargetRanges.length) {
            targetRanges = domTargetRanges.map(domRange => {
                return view.domConverter.domRangeToView(domRange);
            });
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.info( '%c[InputObserver]%c using target ranges:',
            // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', 'font-weight:bold', targetRanges
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
        }
        // For Android devices we use a fallback to the current DOM selection, Android modifies it according
        // to the expected target ranges of input event.
        else if (src_env.isAndroid) {
            const domSelection = domEvent.target.ownerDocument.defaultView.getSelection();
            targetRanges = Array.from(view.domConverter.domSelectionToView(domSelection).getRanges());
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.info( '%c[InputObserver]%c using selection ranges:',
            // @if CK_DEBUG_TYPING // 		'color: green;font-weight: bold', 'font-weight:bold', targetRanges
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
        }
        // Android sometimes fires insertCompositionText with a new-line character at the end of the data
        // instead of firing insertParagraph beforeInput event.
        // Fire the correct type of beforeInput event and ignore the replaced fragment of text because
        // it wants to replace "test" with "test\n".
        // https://github.com/ckeditor/ckeditor5/issues/12368.
        if (src_env.isAndroid && domEvent.inputType == 'insertCompositionText' && data && data.endsWith('\n')) {
            this.fire(domEvent.type, domEvent, {
                inputType: 'insertParagraph',
                targetRanges: [view.createRange(targetRanges[0].end)]
            });
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.groupEnd();
            // @if CK_DEBUG_TYPING // }
            return;
        }
        // Normalize the insertText data that includes new-line characters.
        // https://github.com/ckeditor/ckeditor5/issues/2045.
        if (domEvent.inputType == 'insertText' && data && data.includes('\n')) {
            // There might be a single new-line or double for new paragraph, but we translate
            // it to paragraphs as it is our default action for enter handling.
            const parts = data.split(/\n{1,2}/g);
            let partTargetRanges = targetRanges;
            for (let i = 0; i < parts.length; i++) {
                const dataPart = parts[i];
                if (dataPart != '') {
                    this.fire(domEvent.type, domEvent, {
                        data: dataPart,
                        dataTransfer,
                        targetRanges: partTargetRanges,
                        inputType: domEvent.inputType,
                        isComposing: domEvent.isComposing
                    });
                    // Use the result view selection so following events will be added one after another.
                    partTargetRanges = [viewDocument.selection.getFirstRange()];
                }
                if (i + 1 < parts.length) {
                    this.fire(domEvent.type, domEvent, {
                        inputType: 'insertParagraph',
                        targetRanges: partTargetRanges
                    });
                    // Use the result view selection so following events will be added one after another.
                    partTargetRanges = [viewDocument.selection.getFirstRange()];
                }
            }
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.groupEnd();
            // @if CK_DEBUG_TYPING // }
            return;
        }
        // Fire the normalized beforeInput event.
        this.fire(domEvent.type, domEvent, {
            data,
            dataTransfer,
            targetRanges,
            inputType: domEvent.inputType,
            isComposing: domEvent.isComposing
        });
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.groupEnd();
        // @if CK_DEBUG_TYPING // }
    }
}
/**
 * The data transfer instance of the input event. Corresponds to native `InputEvent#dataTransfer`.
 *
 * The value is `null` when no `dataTransfer` was passed along with the input event.
 *
 * @readonly
 * @member {module:engine/view/datatransfer~DataTransfer|null} module:engine/view/observer/inputobserver~InputEventData#dataTransfer
 */
/**
 * A flag indicating that the `beforeinput` event was fired during composition.
 *
 * Corresponds to the
 * {@link module:engine/view/document~Document#event:compositionstart},
 * {@link module:engine/view/document~Document#event:compositionupdate},
 * and {@link module:engine/view/document~Document#event:compositionend } trio.
 *
 * @readonly
 * @member {Boolean} module:engine/view/observer/inputobserver~InputEventData#isComposing
 */
/**
 * The type of the input event (e.g. "insertText" or "deleteWordBackward"). Corresponds to native `InputEvent#inputType`.
 *
 * @readonly
 * @member {String} module:engine/view/observer/inputobserver~InputEventData#inputType
 */
/**
 * Editing {@link module:engine/view/range~Range view ranges} corresponding to DOM ranges provided by the web browser
 * (as returned by `InputEvent#getTargetRanges()`).
 *
 * @readonly
 * @member {Array.<module:engine/view/range~Range>} module:engine/view/observer/inputobserver~InputEventData#targetRanges
 */
/**
 * A unified text data passed along with the input event. Depending on:
 *
 * * the web browser and input events implementation (for instance [Level 1](https://www.w3.org/TR/input-events-1/) or
 * [Level 2](https://www.w3.org/TR/input-events-2/)),
 * * {@link module:engine/view/observer/inputobserver~InputEventData#inputType input type}
 *
 * text data is sometimes passed in the `data` and sometimes in the `dataTransfer` property.
 *
 * * If `InputEvent#data` was set, this property reflects its value.
 * * If `InputEvent#data` is unavailable, this property contains the `'text/plain'` data from
 * {@link module:engine/view/observer/inputobserver~InputEventData#dataTransfer}.
 * * If the event ({@link module:engine/view/observer/inputobserver~InputEventData#inputType input type})
 * provides no data whatsoever, this property is `null`.
 *
 * @readonly
 * @member {String|null} module:engine/view/observer/inputobserver~InputEventData#data
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/mix.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/mix
 */
/**
 * Copies enumerable properties and symbols from the objects given as 2nd+ parameters to the
 * prototype of first object (a constructor).
 *
 *		class Editor {
 *			...
 *		}
 *
 *		const SomeMixin = {
 *			a() {
 *				return 'a';
 *			}
 *		};
 *
 *		mix( Editor, SomeMixin, ... );
 *
 *		new Editor().a(); // -> 'a'
 *
 * Note: Properties which already exist in the base class will not be overriden.
 *
 * @depreciated Use mixin pattern, see: https://www.typescriptlang.org/docs/handbook/mixins.html.
 * @param {Function} [baseClass] Class which prototype will be extended.
 * @param {Object} [...mixins] Objects from which to get properties.
 */
function mix(baseClass, ...mixins) {
    mixins.forEach(mixin => {
        const propertyNames = Object.getOwnPropertyNames(mixin);
        const propertySymbols = Object.getOwnPropertySymbols(mixin);
        propertyNames.concat(propertySymbols).forEach(key => {
            if (key in baseClass.prototype) {
                return;
            }
            if (typeof mixin == 'function' && (key == 'length' || key == 'name' || key == 'prototype')) {
                return;
            }
            const sourceDescriptor = Object.getOwnPropertyDescriptor(mixin, key);
            sourceDescriptor.enumerable = false;
            Object.defineProperty(baseClass.prototype, key, sourceDescriptor);
        });
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/elementreplacer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/elementreplacer
 */
/**
 * Utility class allowing to hide existing HTML elements or replace them with given ones in a way that doesn't remove
 * the original elements from the DOM.
 */
class ElementReplacer {
    constructor() {
        this._replacedElements = [];
    }
    /**
     * Hides the `element` and, if specified, inserts the the given element next to it.
     *
     * The effect of this method can be reverted by {@link #restore}.
     *
     * @param {HTMLElement} element The element to replace.
     * @param {HTMLElement} [newElement] The replacement element. If not passed, then the `element` will just be hidden.
     */
    replace(element, newElement) {
        this._replacedElements.push({ element, newElement });
        element.style.display = 'none';
        if (newElement) {
            element.parentNode.insertBefore(newElement, element.nextSibling);
        }
    }
    /**
     * Restores what {@link #replace} did.
     */
    restore() {
        this._replacedElements.forEach(({ element, newElement }) => {
            element.style.display = '';
            if (newElement) {
                newElement.remove();
            }
        });
        this._replacedElements = [];
    }
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/isString.js




/** `Object#toString` result references. */
var isString_stringTag = '[object String]';

/**
 * Checks if `value` is classified as a `String` primitive or object.
 *
 * @static
 * @since 0.1.0
 * @memberOf _
 * @category Lang
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is a string, else `false`.
 * @example
 *
 * _.isString('abc');
 * // => true
 *
 * _.isString(1);
 * // => false
 */
function isString(value) {
  return typeof value == 'string' ||
    (!lodash_es_isArray(value) && lodash_es_isObjectLike(value) && _baseGetTag(value) == isString_stringTag);
}

/* harmony default export */ const lodash_es_isString = (isString);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/createelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/createelement
 */


/**
 * Creates element with attributes and children.
 *
 *		createElement( document, 'p' ); // <p>
 *		createElement( document, 'p', { class: 'foo' } ); // <p class="foo">
 *		createElement( document, 'p', null, 'foo' ); // <p>foo</p>
 *		createElement( document, 'p', null, [ 'foo', createElement( document, 'img' ) ] ); // <p>foo<img></p>
 *
 * @param {Document} doc Document used to create element.
 * @param {String} name Name of the element.
 * @param {Object} [attributes] Object keys will become attributes keys and object values will became attributes values.
 * @param {Node|String|Iterable.<Node|String>} [children] Child or any iterable of children. Strings will be automatically turned
 * into Text nodes.
 * @returns {Element} Created element.
 */
function createElement(doc, name, attributes = {}, children = []) {
    const namespace = attributes && attributes.xmlns;
    const element = namespace ? doc.createElementNS(namespace, name) : doc.createElement(name);
    for (const key in attributes) {
        element.setAttribute(key, attributes[key]);
    }
    if (lodash_es_isString(children) || !isIterable(children)) {
        children = [children];
    }
    for (let child of children) {
        if (lodash_es_isString(child)) {
            child = doc.createTextNode(child);
        }
        element.appendChild(child);
    }
    return element;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/getdatafromelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* globals HTMLTextAreaElement */
/**
 * @module utils/dom/getdatafromelement
 */
/**
 * Gets data from a given source element.
 *
 * @param {HTMLElement} el The element from which the data will be retrieved.
 * @returns {String} The data string.
 */
function getDataFromElement(el) {
    if (el instanceof HTMLTextAreaElement) {
        return el.value;
    }
    return el.innerHTML;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/isrange.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/isrange
 */
/**
 * Checks if the object is a native DOM Range.
 *
 * @param {*} obj
 * @returns {Boolean}
 */
function isRange(obj) {
    return Object.prototype.toString.apply(obj) == '[object Range]';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/getborderwidths.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/getborderwidths
 */
/**
 * Returns an object containing CSS border widths of a specified HTML element.
 *
 * @param {HTMLElement} element An element which has CSS borders.
 * @returns {module:utils/dom/getborderwidths~BorderWidths} An object containing `top`, `left`, `right` and `bottom` properties
 * with numerical values of the `border-[top,left,right,bottom]-width` CSS styles.
 */
function getBorderWidths(element) {
    // Call getComputedStyle on the window the element document belongs to.
    const style = element.ownerDocument.defaultView.getComputedStyle(element);
    return {
        top: parseInt(style.borderTopWidth, 10),
        right: parseInt(style.borderRightWidth, 10),
        bottom: parseInt(style.borderBottomWidth, 10),
        left: parseInt(style.borderLeftWidth, 10)
    };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/rect.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/rect
 */




const rectProperties = ['top', 'right', 'bottom', 'left', 'width', 'height'];
/**
 * A helper class representing a `ClientRect` object, e.g. value returned by
 * the native `object.getBoundingClientRect()` method. Provides a set of methods
 * to manipulate the rect and compare it against other rect instances.
 */
class rect_Rect {
    /**
     * Creates an instance of rect.
     *
     *		// Rect of an HTMLElement.
     *		const rectA = new Rect( document.body );
     *
     *		// Rect of a DOM Range.
     *		const rectB = new Rect( document.getSelection().getRangeAt( 0 ) );
     *
     *		// Rect of a window (web browser viewport).
     *		const rectC = new Rect( window );
     *
     *		// Rect out of an object.
     *		const rectD = new Rect( { top: 0, right: 10, bottom: 10, left: 0, width: 10, height: 10 } );
     *
     *		// Rect out of another Rect instance.
     *		const rectE = new Rect( rectD );
     *
     *		// Rect out of a ClientRect.
     *		const rectF = new Rect( document.body.getClientRects().item( 0 ) );
     *
     * **Note**: By default a rect of an HTML element includes its CSS borders and scrollbars (if any)
     * ant the rect of a `window` includes scrollbars too. Use {@link #excludeScrollbarsAndBorders}
     * to get the inner part of the rect.
     *
     * @param {module:utils/dom/rect~RectSource} source A source object to create the rect.
     */
    constructor(source) {
        const isSourceRange = isRange(source);
        Object.defineProperty(this, '_source', {
            // If the source is a Rect instance, copy it's #_source.
            value: source._source || source,
            writable: true,
            enumerable: false
        });
        if (isDomElement(source) || isSourceRange) {
            // The `Rect` class depends on `getBoundingClientRect` and `getClientRects` DOM methods. If the source
            // of a rect in an HTML element or a DOM range but it does not belong to any rendered DOM tree, these methods
            // will fail to obtain the geometry and the rect instance makes little sense to the features using it.
            // To get rid of this warning make sure the source passed to the constructor is a descendant of `window.document.body`.
            // @if CK_DEBUG // const sourceNode = isSourceRange ? source.startContainer : source;
            // @if CK_DEBUG // if ( !sourceNode.ownerDocument || !sourceNode.ownerDocument.body.contains( sourceNode ) ) {
            // @if CK_DEBUG // 	console.warn(
            // @if CK_DEBUG // 		'rect-source-not-in-dom: The source of this rect does not belong to any rendered DOM tree.',
            // @if CK_DEBUG // 		{ source } );
            // @if CK_DEBUG // }
            if (isSourceRange) {
                const rangeRects = rect_Rect.getDomRangeRects(source);
                copyRectProperties(this, rect_Rect.getBoundingRect(rangeRects));
            }
            else {
                copyRectProperties(this, source.getBoundingClientRect());
            }
        }
        else if (isWindow(source)) {
            const { innerWidth, innerHeight } = source;
            copyRectProperties(this, {
                top: 0,
                right: innerWidth,
                bottom: innerHeight,
                left: 0,
                width: innerWidth,
                height: innerHeight
            });
        }
        else {
            copyRectProperties(this, source);
        }
    }
    /**
     * Returns a clone of the rect.
     *
     * @returns {module:utils/dom/rect~Rect} A cloned rect.
     */
    clone() {
        return new rect_Rect(this);
    }
    /**
     * Moves the rect so that its upper–left corner lands in desired `[ x, y ]` location.
     *
     * @param {Number} x Desired horizontal location.
     * @param {Number} y Desired vertical location.
     * @returns {module:utils/dom/rect~Rect} A rect which has been moved.
     */
    moveTo(x, y) {
        this.top = y;
        this.right = x + this.width;
        this.bottom = y + this.height;
        this.left = x;
        return this;
    }
    /**
     * Moves the rect in–place by a dedicated offset.
     *
     * @param {Number} x A horizontal offset.
     * @param {Number} y A vertical offset
     * @returns {module:utils/dom/rect~Rect} A rect which has been moved.
     */
    moveBy(x, y) {
        this.top += y;
        this.right += x;
        this.left += x;
        this.bottom += y;
        return this;
    }
    /**
     * Returns a new rect a a result of intersection with another rect.
     *
     * @param {module:utils/dom/rect~Rect} anotherRect
     * @returns {module:utils/dom/rect~Rect|null}
     */
    getIntersection(anotherRect) {
        const rect = {
            top: Math.max(this.top, anotherRect.top),
            right: Math.min(this.right, anotherRect.right),
            bottom: Math.min(this.bottom, anotherRect.bottom),
            left: Math.max(this.left, anotherRect.left),
            width: 0,
            height: 0
        };
        rect.width = rect.right - rect.left;
        rect.height = rect.bottom - rect.top;
        if (rect.width < 0 || rect.height < 0) {
            return null;
        }
        else {
            return new rect_Rect(rect);
        }
    }
    /**
     * Returns the area of intersection with another rect.
     *
     * @param {module:utils/dom/rect~Rect} anotherRect
     * @returns {Number} Area of intersection.
     */
    getIntersectionArea(anotherRect) {
        const rect = this.getIntersection(anotherRect);
        if (rect) {
            return rect.getArea();
        }
        else {
            return 0;
        }
    }
    /**
     * Returns the area of the rect.
     *
     * @returns {Number}
     */
    getArea() {
        return this.width * this.height;
    }
    /**
     * Returns a new rect, a part of the original rect, which is actually visible to the user,
     * e.g. an original rect cropped by parent element rects which have `overflow` set in CSS
     * other than `"visible"`.
     *
     * If there's no such visible rect, which is when the rect is limited by one or many of
     * the ancestors, `null` is returned.
     *
     * @returns {module:utils/dom/rect~Rect|null} A visible rect instance or `null`, if there's none.
     */
    getVisible() {
        const source = this._source;
        let visibleRect = this.clone();
        // There's no ancestor to crop <body> with the overflow.
        if (!isBody(source)) {
            let parent = source.parentNode || source.commonAncestorContainer;
            // Check the ancestors all the way up to the <body>.
            while (parent && !isBody(parent)) {
                const parentRect = new rect_Rect(parent);
                const intersectionRect = visibleRect.getIntersection(parentRect);
                if (intersectionRect) {
                    if (intersectionRect.getArea() < visibleRect.getArea()) {
                        // Reduce the visible rect to the intersection.
                        visibleRect = intersectionRect;
                    }
                }
                else {
                    // There's no intersection, the rect is completely invisible.
                    return null;
                }
                parent = parent.parentNode;
            }
        }
        return visibleRect;
    }
    /**
     * Checks if all property values ({@link #top}, {@link #left}, {@link #right},
     * {@link #bottom}, {@link #width} and {@link #height}) are the equal in both rect
     * instances.
     *
     * @param {module:utils/dom/rect~Rect} anotherRect A rect instance to compare with.
     * @returns {Boolean} `true` when Rects are equal. `false` otherwise.
     */
    isEqual(anotherRect) {
        for (const prop of rectProperties) {
            if (this[prop] !== anotherRect[prop]) {
                return false;
            }
        }
        return true;
    }
    /**
     * Checks whether a rect fully contains another rect instance.
     *
     * @param {module:utils/dom/rect~Rect} anotherRect
     * @returns {Boolean} `true` if contains, `false` otherwise.
     */
    contains(anotherRect) {
        const intersectRect = this.getIntersection(anotherRect);
        return !!(intersectRect && intersectRect.isEqual(anotherRect));
    }
    /**
     * Excludes scrollbars and CSS borders from the rect.
     *
     * * Borders are removed when {@link #_source} is an HTML element.
     * * Scrollbars are excluded from HTML elements and the `window`.
     *
     * @returns {module:utils/dom/rect~Rect} A rect which has been updated.
     */
    excludeScrollbarsAndBorders() {
        const source = this._source;
        let scrollBarWidth, scrollBarHeight, direction;
        if (isWindow(source)) {
            scrollBarWidth = source.innerWidth - source.document.documentElement.clientWidth;
            scrollBarHeight = source.innerHeight - source.document.documentElement.clientHeight;
            direction = source.getComputedStyle(source.document.documentElement).direction;
        }
        else {
            const borderWidths = getBorderWidths(source);
            scrollBarWidth = source.offsetWidth - source.clientWidth - borderWidths.left - borderWidths.right;
            scrollBarHeight = source.offsetHeight - source.clientHeight - borderWidths.top - borderWidths.bottom;
            direction = source.ownerDocument.defaultView.getComputedStyle(source).direction;
            this.left += borderWidths.left;
            this.top += borderWidths.top;
            this.right -= borderWidths.right;
            this.bottom -= borderWidths.bottom;
            this.width = this.right - this.left;
            this.height = this.bottom - this.top;
        }
        this.width -= scrollBarWidth;
        if (direction === 'ltr') {
            this.right -= scrollBarWidth;
        }
        else {
            this.left += scrollBarWidth;
        }
        this.height -= scrollBarHeight;
        this.bottom -= scrollBarHeight;
        return this;
    }
    /**
     * Returns an array of rects of the given native DOM Range.
     *
     * @param {Range} range A native DOM range.
     * @returns {Array.<module:utils/dom/rect~Rect>} DOM Range rects.
     */
    static getDomRangeRects(range) {
        const rects = [];
        // Safari does not iterate over ClientRectList using for...of loop.
        const clientRects = Array.from(range.getClientRects());
        if (clientRects.length) {
            for (const rect of clientRects) {
                rects.push(new rect_Rect(rect));
            }
        }
        // If there's no client rects for the Range, use parent container's bounding rect
        // instead and adjust rect's width to simulate the actual geometry of such range.
        // https://github.com/ckeditor/ckeditor5-utils/issues/153
        // https://github.com/ckeditor/ckeditor5-ui/issues/317
        else {
            let startContainer = range.startContainer;
            if (isText(startContainer)) {
                startContainer = startContainer.parentNode;
            }
            const rect = new rect_Rect(startContainer.getBoundingClientRect());
            rect.right = rect.left;
            rect.width = 0;
            rects.push(rect);
        }
        return rects;
    }
    /**
     * Returns a bounding rectangle that contains all the given `rects`.
     *
     * @param {Iterable.<module:utils/dom/rect~Rect>} rects A list of rectangles that should be contained in the result rectangle.
     * @returns {module:utils/dom/rect~Rect|null} Bounding rectangle or `null` if no `rects` were given.
     */
    static getBoundingRect(rects) {
        const boundingRectData = {
            left: Number.POSITIVE_INFINITY,
            top: Number.POSITIVE_INFINITY,
            right: Number.NEGATIVE_INFINITY,
            bottom: Number.NEGATIVE_INFINITY,
            width: 0,
            height: 0
        };
        let rectangleCount = 0;
        for (const rect of rects) {
            rectangleCount++;
            boundingRectData.left = Math.min(boundingRectData.left, rect.left);
            boundingRectData.top = Math.min(boundingRectData.top, rect.top);
            boundingRectData.right = Math.max(boundingRectData.right, rect.right);
            boundingRectData.bottom = Math.max(boundingRectData.bottom, rect.bottom);
        }
        if (rectangleCount == 0) {
            return null;
        }
        boundingRectData.width = boundingRectData.right - boundingRectData.left;
        boundingRectData.height = boundingRectData.bottom - boundingRectData.top;
        return new rect_Rect(boundingRectData);
    }
}
// Acquires all the rect properties from the passed source.
//
// @private
// @param {module:utils/dom/rect~Rect} rect
// @param {module:utils/dom/rect~RectLike} source
function copyRectProperties(rect, source) {
    for (const p of rectProperties) {
        rect[p] = source[p];
    }
}
// Checks if provided object is a <body> HTML element.
//
// @private
// @param {*} value
// @returns {Boolean}
function isBody(value) {
    if (!isDomElement(value)) {
        return false;
    }
    return value === value.ownerDocument.body;
}
// Checks if provided object "looks like" a DOM Element and has API required by `Rect` class.
//
// @private
// @param {*} value
// @returns {Boolean}
function isDomElement(value) {
    // Note: earlier we used `isElement()` from lodash library, however that function is less performant because
    // it makes complicated checks to make sure that given value is a DOM element.
    return value !== null && typeof value === 'object' && value.nodeType === 1 && typeof value.getBoundingClientRect === 'function';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/resizeobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/resizeobserver
 */

/**
 * A helper class which instances allow performing custom actions when native DOM elements are resized.
 *
 *		const editableElement = editor.editing.view.getDomRoot();
 *
 *		const observer = new ResizeObserver( editableElement, entry => {
 *			console.log( 'The editable element has been resized in DOM.' );
 *			console.log( entry.target ); // -> editableElement
 *			console.log( entry.contentRect.width ); // -> e.g. '423px'
 *		} );
 *
 * It uses the [native DOM resize observer](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserver)
 * under the hood.
 */
class resizeobserver_ResizeObserver {
    /**
     * Creates an instance of the `ResizeObserver` class.
     *
     * @param {Element} element A DOM element that is to be observed for resizing. Note that
     * the element must be visible (i.e. not detached from DOM) for the observer to work.
     * @param {Function} callback A function called when the observed element was resized. It passes
     * the [`ResizeObserverEntry`](https://developer.mozilla.org/en-US/docs/Web/API/ResizeObserverEntry)
     * object with information about the resize event.
     */
    constructor(element, callback) {
        // **Note**: For the maximum performance, this class ensures only a single instance of the native
        // observer is used no matter how many instances of this class were created.
        if (!resizeobserver_ResizeObserver._observerInstance) {
            resizeobserver_ResizeObserver._createObserver();
        }
        this._element = element;
        this._callback = callback;
        resizeobserver_ResizeObserver._addElementCallback(element, callback);
        resizeobserver_ResizeObserver._observerInstance.observe(element);
    }
    /**
     * Destroys the observer which disables the `callback` passed to the {@link #constructor}.
     */
    destroy() {
        resizeobserver_ResizeObserver._deleteElementCallback(this._element, this._callback);
    }
    /**
     * Registers a new resize callback for the DOM element.
     *
     * @private
     * @static
     * @param {Element} element
     * @param {Function} callback
     */
    static _addElementCallback(element, callback) {
        if (!resizeobserver_ResizeObserver._elementCallbacks) {
            resizeobserver_ResizeObserver._elementCallbacks = new Map();
        }
        let callbacks = resizeobserver_ResizeObserver._elementCallbacks.get(element);
        if (!callbacks) {
            callbacks = new Set();
            resizeobserver_ResizeObserver._elementCallbacks.set(element, callbacks);
        }
        callbacks.add(callback);
    }
    /**
     * Removes a resize callback from the DOM element. If no callbacks are left
     * for the element, it removes the element from the native observer.
     *
     * @private
     * @static
     * @param {Element} element
     * @param {Function} callback
     */
    static _deleteElementCallback(element, callback) {
        const callbacks = resizeobserver_ResizeObserver._getElementCallbacks(element);
        // Remove the element callback. Check if exist first in case someone
        // called destroy() twice.
        if (callbacks) {
            callbacks.delete(callback);
            // If no callbacks left for the element, also remove the element.
            if (!callbacks.size) {
                resizeobserver_ResizeObserver._elementCallbacks.delete(element);
                resizeobserver_ResizeObserver._observerInstance.unobserve(element);
            }
        }
        if (resizeobserver_ResizeObserver._elementCallbacks && !resizeobserver_ResizeObserver._elementCallbacks.size) {
            resizeobserver_ResizeObserver._observerInstance = null;
            resizeobserver_ResizeObserver._elementCallbacks = null;
        }
    }
    /**
     * Returns are registered resize callbacks for the DOM element.
     *
     * @private
     * @static
     * @param {Element} element
     * @returns {Set.<Function>|null|undefined}
     */
    static _getElementCallbacks(element) {
        if (!resizeobserver_ResizeObserver._elementCallbacks) {
            return null;
        }
        return resizeobserver_ResizeObserver._elementCallbacks.get(element);
    }
    /**
     * Creates the single native observer shared across all `ResizeObserver` instances.
     *
     * @private
     * @static
     */
    static _createObserver() {
        resizeobserver_ResizeObserver._observerInstance = new dom_global.window.ResizeObserver(entries => {
            for (const entry of entries) {
                const callbacks = resizeobserver_ResizeObserver._getElementCallbacks(entry.target);
                if (callbacks) {
                    for (const callback of callbacks) {
                        callback(entry);
                    }
                }
            }
        });
    }
}
/**
 * The single native observer instance shared across all {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
 *
 * @static
 * @private
 * @readonly
 * @property {Object|null}
 */
resizeobserver_ResizeObserver._observerInstance = null;
/**
 * A mapping of native DOM elements and their callbacks shared across all
 * {@link module:utils/dom/resizeobserver~ResizeObserver} instances.
 *
 * @static
 * @private
 * @property {Map.<Element,Set>|null}
 */
resizeobserver_ResizeObserver._elementCallbacks = null;

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/tounit.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/tounit
 */
/**
 * Returns a helper function, which adds a desired trailing
 * `unit` to the passed value.
 *
 * @param {String} unit An unit like "px" or "em".
 * @returns {module:utils/dom/tounit~helper}
 */
function toUnit(unit) {
    /**
     * A function, which adds a pre–defined trailing `unit`
     * to the passed `value`.
     *
     * @function helper
     * @param {String|Number} value A value to be given the unit.
     * @returns {String} A value with the trailing unit.
     */
    return value => value + unit;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/isvisible.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/isvisible
 */
/**
 * Checks whether the element is visible to the user in DOM:
 *
 * * connected to the root of the document,
 * * has no `display: none`,
 * * has no ancestors with `display: none`.
 *
 * **Note**: This helper does not check whether the element is hidden by cropping, overflow, etc..
 * To check that, use {@link module:utils/dom/rect~Rect} instead.
 *
 * @param {HTMLElement|null|undefined} element
 * @returns {Boolean}
 */
function isVisible(element) {
    return !!(element && element.getClientRects && element.getClientRects().length);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/first.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/first
 */
/**
 * Returns first item of the given `iterator`.
 *
 * @param {Iterator.<*>} iterator
 * @returns {*}
 */
function first_first(iterator) {
    const iteratorItem = iterator.next();
    if (iteratorItem.done) {
        return null;
    }
    return iteratorItem.value;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/focustracker.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* global setTimeout, clearTimeout */
/* eslint-disable new-cap */
/**
 * @module utils/focustracker
 */



/**
 * Allows observing a group of `Element`s whether at least one of them is focused.
 *
 * Used by the {@link module:core/editor/editor~Editor} in order to track whether the focus is still within the application,
 * or were used outside of its UI.
 *
 * **Note** `focus` and `blur` listeners use event capturing, so it is only needed to register wrapper `Element`
 * which contain other `focusable` elements. But note that this wrapper element has to be focusable too
 * (have e.g. `tabindex="-1"`).
 *
 * Check out the {@glink framework/guides/deep-dive/ui/focus-tracking "Deep dive into focus tracking" guide} to learn more.
 *
 * @mixes module:utils/dom/emittermixin~EmitterMixin
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class FocusTracker extends DomEmitterMixin(Observable) {
    constructor() {
        super();
        this.set('isFocused', false);
        this.set('focusedElement', null);
        this._elements = new Set();
        this._nextEventLoopTimeout = null;
    }
    /**
     * Starts tracking the specified element.
     *
     * @param {Element} element
     */
    add(element) {
        if (this._elements.has(element)) {
            /**
             * This element is already tracked by {@link module:utils/focustracker~FocusTracker}.
             *
             * @error focustracker-add-element-already-exist
             */
            throw new CKEditorError('focustracker-add-element-already-exist', this);
        }
        this.listenTo(element, 'focus', () => this._focus(element), { useCapture: true });
        this.listenTo(element, 'blur', () => this._blur(), { useCapture: true });
        this._elements.add(element);
    }
    /**
     * Stops tracking the specified element and stops listening on this element.
     *
     * @param {Element} element
     */
    remove(element) {
        if (element === this.focusedElement) {
            this._blur();
        }
        if (this._elements.has(element)) {
            this.stopListening(element);
            this._elements.delete(element);
        }
    }
    /**
     * Destroys the focus tracker by:
     * - Disabling all event listeners attached to tracked elements.
     * - Removing all tracked elements that were previously added.
     */
    destroy() {
        this.stopListening();
    }
    /**
     * Stores currently focused element and set {#isFocused} as `true`.
     *
     * @private
     * @param {Element} element Element which has been focused.
     */
    _focus(element) {
        clearTimeout(this._nextEventLoopTimeout);
        this.focusedElement = element;
        this.isFocused = true;
    }
    /**
     * Clears currently focused element and set {@link #isFocused} as `false`.
     * This method uses `setTimeout` to change order of fires `blur` and `focus` events.
     *
     * @private
     * @fires blur
     */
    _blur() {
        clearTimeout(this._nextEventLoopTimeout);
        this._nextEventLoopTimeout = setTimeout(() => {
            this.focusedElement = null;
            this.isFocused = false;
        }, 0);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/keystrokehandler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/keystrokehandler
 */


/**
 * Keystroke handler allows registering callbacks for given keystrokes.
 *
 * The most frequent use of this class is through the {@link module:core/editor/editor~Editor#keystrokes `editor.keystrokes`}
 * property. It allows listening to keystrokes executed in the editing view:
 *
 *		editor.keystrokes.set( 'Ctrl+A', ( keyEvtData, cancel ) => {
 *			console.log( 'Ctrl+A has been pressed' );
 *			cancel();
 *		} );
 *
 * However, this utility class can be used in various part of the UI. For instance, a certain {@link module:ui/view~View}
 * can use it like this:
 *
 *		class MyView extends View {
 *			constructor() {
 *				this.keystrokes = new KeystrokeHandler();
 *
 * 				this.keystrokes.set( 'tab', handleTabKey );
 *			}
 *
 *			render() {
 *				super.render();
 *
 *				this.keystrokes.listenTo( this.element );
 *			}
 *		}
 *
 * That keystroke handler will listen to `keydown` events fired in this view's main element.
 *
 */
class KeystrokeHandler {
    /**
     * Creates an instance of the keystroke handler.
     */
    constructor() {
        this._listener = new emittermixin_Emitter();
    }
    /**
     * Starts listening for `keydown` events from a given emitter.
     *
     * @param {module:utils/emittermixin~Emitter|HTMLElement|Window} emitter
     */
    listenTo(emitter) {
        // The #_listener works here as a kind of dispatcher. It groups the events coming from the same
        // keystroke so the listeners can be attached to them with different priorities.
        //
        // E.g. all the keystrokes with the `keyCode` of 42 coming from the `emitter` are propagated
        // as a `_keydown:42` event by the `_listener`. If there's a callback created by the `set`
        // method for this 42 keystroke, it listens to the `_listener#_keydown:42` event only and interacts
        // only with other listeners of this particular event, thus making it possible to prioritize
        // the listeners and safely cancel execution, when needed. Instead of duplicating the Emitter logic,
        // the KeystrokeHandler re–uses it to do its job.
        this._listener.listenTo(emitter, 'keydown', (evt, keyEvtData) => {
            this._listener.fire('_keydown:' + getCode(keyEvtData), keyEvtData);
        });
    }
    /**
     * Registers a handler for the specified keystroke.
     *
     * @param {String|Array.<String|Number>} keystroke Keystroke defined in a format accepted by
     * the {@link module:utils/keyboard~parseKeystroke} function.
     * @param {Function} callback A function called with the
     * {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
     * a helper function to call both `preventDefault()` and `stopPropagation()` on the underlying event.
     * @param {Object} [options={}] Additional options.
     * @param {module:utils/priorities~PriorityString} [options.priority='normal'] The priority of the keystroke
     * callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority
     * are called in the order they were added.
     */
    set(keystroke, callback, options = {}) {
        const keyCode = parseKeystroke(keystroke);
        const priority = options.priority;
        // Execute the passed callback on KeystrokeHandler#_keydown.
        // TODO: https://github.com/ckeditor/ckeditor5-utils/issues/144
        this._listener.listenTo(this._listener, '_keydown:' + keyCode, (evt, keyEvtData) => {
            callback(keyEvtData, () => {
                // Stop the event in the DOM: no listener in the web page
                // will be triggered by this event.
                keyEvtData.preventDefault();
                keyEvtData.stopPropagation();
                // Stop the event in the KeystrokeHandler: no more callbacks
                // will be executed for this keystroke.
                evt.stop();
            });
            // Mark this keystroke as handled by the callback. See: #press.
            evt.return = true;
        }, { priority });
    }
    /**
     * Triggers a keystroke handler for a specified key combination, if such a keystroke was {@link #set defined}.
     *
     * @param {module:engine/view/observer/keyobserver~KeyEventData} keyEvtData Key event data.
     * @returns {Boolean} Whether the keystroke was handled.
     */
    press(keyEvtData) {
        return !!this._listener.fire('_keydown:' + getCode(keyEvtData), keyEvtData);
    }
    /**
     * Destroys the keystroke handler.
     */
    destroy() {
        this._listener.stopListening();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils
 */































;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/arrowkeysobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/arrowkeysobserver
 */



/**
 * Arrow keys observer introduces the {@link module:engine/view/document~Document#event:arrowKey `Document#arrowKey`} event.
 *
 * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class ArrowKeysObserver extends Observer {
    /**
     * @inheritDoc
     */
    constructor(view) {
        super(view);
        this.document.on('keydown', (event, data) => {
            if (this.isEnabled && isArrowKeyCode(data.keyCode)) {
                const eventInfo = new BubblingEventInfo(this.document, 'arrowKey', this.document.selection.getFirstRange());
                this.document.fire(eventInfo, data);
                if (eventInfo.stop.called) {
                    event.stop();
                }
            }
        });
    }
    /**
     * @inheritDoc
     */
    observe() { }
}
/**
 * Event fired when the user presses an arrow keys.
 *
 * Introduced by {@link module:engine/view/observer/arrowkeysobserver~ArrowKeysObserver}.
 *
 * Note that because {@link module:engine/view/observer/arrowkeysobserver~ArrowKeysObserver} is attached by the
 * {@link module:engine/view/view~View} this event is available by default.
 *
 * @event module:engine/view/document~Document#event:arrowKey
 * @param {module:engine/view/observer/domeventdata~DomEventData} data
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/tabobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */



/**
 * Tab observer introduces the {@link module:engine/view/document~Document#event:tab `Document#tab`} event.
 *
 * Note that because {@link module:engine/view/observer/tabobserver~TabObserver} is attached by the
 * {@link module:engine/view/view~View}, this event is available by default.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class TabObserver extends Observer {
    /**
     * @inheritDoc
     */
    constructor(view) {
        super(view);
        const doc = this.document;
        doc.on('keydown', (evt, data) => {
            if (!this.isEnabled ||
                data.keyCode != keyCodes.tab ||
                data.ctrlKey) {
                return;
            }
            const event = new BubblingEventInfo(doc, 'tab', doc.selection.getFirstRange());
            doc.fire(event, data);
            if (event.stop.called) {
                evt.stop();
            }
        });
    }
    /**
     * @inheritDoc
     */
    observe() { }
}
/**
 * Event fired when the user presses a tab key.
 *
 * Introduced by {@link module:engine/view/observer/tabobserver~TabObserver}.
 *
 * Note that because {@link module:engine/view/observer/tabobserver~TabObserver} is attached by the
 * {@link module:engine/view/view~View}, this event is available by default.
 *
 * @event module:engine/view/document~Document#event:tab
 *
 * @param {module:engine/view/observer/domeventdata~DomEventData} data
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/scroll.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/scroll
 */



/**
 * Makes any page `HTMLElement` or `Range` (`target`) visible inside the browser viewport.
 * This helper will scroll all `target` ancestors and the web browser viewport to reveal the target to
 * the user. If the `target` is already visible, nothing will happen.
 *
 * @param {Object} options
 * @param {HTMLElement|Range} options.target A target, which supposed to become visible to the user.
 * @param {Number} [options.viewportOffset] An offset from the edge of the viewport (in pixels)
 * the `target` will be moved by when the viewport is scrolled. It enhances the user experience
 * by keeping the `target` some distance from the edge of the viewport and thus making it easier to
 * read or edit by the user.
 */
function scrollViewportToShowTarget({ target, viewportOffset = 0 }) {
    const targetWindow = getWindow(target);
    let currentWindow = targetWindow;
    let currentFrame = null;
    // Iterate over all windows, starting from target's parent window up to window#top.
    while (currentWindow) {
        let firstAncestorToScroll;
        // Let's scroll target's ancestors first to reveal it. Then, once the ancestor scrolls
        // settled down, the algorithm can eventually scroll the viewport of the current window.
        //
        // Note: If the current window is target's **original** window (e.g. the first one),
        // start scrolling the closest parent of the target. If not, scroll the closest parent
        // of an iframe that resides in the current window.
        if (currentWindow == targetWindow) {
            firstAncestorToScroll = getParentElement(target);
        }
        else {
            firstAncestorToScroll = getParentElement(currentFrame);
        }
        // Scroll the target's ancestors first. Once done, scrolling the viewport is easy.
        scrollAncestorsToShowRect(firstAncestorToScroll, () => {
            // Note: If the target does not belong to the current window **directly**,
            // i.e. it resides in an iframe belonging to the window, obtain the target's rect
            // in the coordinates of the current window. By default, a Rect returns geometry
            // relative to the current window's viewport. To make it work in a parent window,
            // it must be shifted.
            return getRectRelativeToWindow(target, currentWindow);
        });
        // Obtain the rect of the target after it has been scrolled within its ancestors.
        // It's time to scroll the viewport.
        const targetRect = getRectRelativeToWindow(target, currentWindow);
        scrollWindowToShowRect(currentWindow, targetRect, viewportOffset);
        if (currentWindow.parent != currentWindow) {
            // Keep the reference to the <iframe> element the "previous current window" was
            // rendered within. It will be useful to re–calculate the rect of the target
            // in the parent window's relative geometry. The target's rect must be shifted
            // by it's iframe's position.
            currentFrame = currentWindow.frameElement;
            currentWindow = currentWindow.parent;
            // If the current window has some parent but frameElement is inaccessible, then they have
            // different domains/ports and, due to security reasons, accessing and scrolling
            // the parent window won't be possible.
            // See https://github.com/ckeditor/ckeditor5/issues/930.
            if (!currentFrame) {
                return;
            }
        }
        else {
            currentWindow = null;
        }
    }
}
/**
 * Makes any page `HTMLElement` or `Range` (target) visible within its scrollable ancestors,
 * e.g. if they have `overflow: scroll` CSS style.
 *
 * @param {HTMLElement|Range} target A target, which supposed to become visible to the user.
 */
function scrollAncestorsToShowTarget(target) {
    const targetParent = getParentElement(target);
    scrollAncestorsToShowRect(targetParent, () => {
        return new Rect(target);
    });
}
// Makes a given rect visible within its parent window.
//
// Note: Avoid the situation where the caret is still in the viewport, but totally
// at the edge of it. In such situation, if it moved beyond the viewport in the next
// action e.g. after paste, the scrolling would move it to the viewportOffset level
// and it all would look like the caret visually moved up/down:
//
// 1.
//		| foo[]
//		|                                    <--- N px of space below the caret
//		+---------------------------------...
//
// 2. *paste*
// 3.
//		|
//		|
//		+-foo-----------------------------...
//		  bar[]                              <--- caret below viewport, scrolling...
//
// 4. *scrolling*
// 5.
//		|
//		| foo
//		| bar[]                              <--- caret precisely at the edge
//		+---------------------------------...
//
// To prevent this, this method checks the rects moved by the viewportOffset to cover
// the upper/lower edge of the viewport. It makes sure if the action repeats, there's
// no twitching – it's a purely visual improvement:
//
// 5. (after fix)
//		|
//		| foo
//		| bar[]
//		|                                    <--- N px of space below the caret
//		+---------------------------------...
//
// @private
// @param {Window} window A window which is scrolled to reveal the rect.
// @param {module:utils/dom/rect~Rect} rect A rect which is to be revealed.
// @param {Number} viewportOffset See scrollViewportToShowTarget.
function scrollWindowToShowRect(window, rect, viewportOffset) {
    const targetShiftedDownRect = rect.clone().moveBy(0, viewportOffset);
    const targetShiftedUpRect = rect.clone().moveBy(0, -viewportOffset);
    const viewportRect = new rect_Rect(window).excludeScrollbarsAndBorders();
    const rects = [targetShiftedUpRect, targetShiftedDownRect];
    if (!rects.every(rect => viewportRect.contains(rect))) {
        let { scrollX, scrollY } = window;
        if (isAbove(targetShiftedUpRect, viewportRect)) {
            scrollY -= viewportRect.top - rect.top + viewportOffset;
        }
        else if (isBelow(targetShiftedDownRect, viewportRect)) {
            scrollY += rect.bottom - viewportRect.bottom + viewportOffset;
        }
        // TODO: Web browsers scroll natively to place the target in the middle
        // of the viewport. It's not a very popular case, though.
        if (isLeftOf(rect, viewportRect)) {
            scrollX -= viewportRect.left - rect.left + viewportOffset;
        }
        else if (isRightOf(rect, viewportRect)) {
            scrollX += rect.right - viewportRect.right + viewportOffset;
        }
        window.scrollTo(scrollX, scrollY);
    }
}
// Recursively scrolls element ancestors to visually reveal a rect.
//
// @private
// @param {HTMLElement} A parent The first ancestors to start scrolling.
// @param {Function} getRect A function which returns the Rect, which is to be revealed.
function scrollAncestorsToShowRect(parent, getRect) {
    const parentWindow = getWindow(parent);
    let parentRect, targetRect;
    while (parent != parentWindow.document.body) {
        targetRect = getRect();
        parentRect = new rect_Rect(parent).excludeScrollbarsAndBorders();
        if (!parentRect.contains(targetRect)) {
            if (isAbove(targetRect, parentRect)) {
                parent.scrollTop -= parentRect.top - targetRect.top;
            }
            else if (isBelow(targetRect, parentRect)) {
                parent.scrollTop += targetRect.bottom - parentRect.bottom;
            }
            if (isLeftOf(targetRect, parentRect)) {
                parent.scrollLeft -= parentRect.left - targetRect.left;
            }
            else if (isRightOf(targetRect, parentRect)) {
                parent.scrollLeft += targetRect.right - parentRect.right;
            }
        }
        parent = parent.parentNode;
    }
}
// Determines if a given `Rect` extends beyond the bottom edge of the second `Rect`.
//
// @private
// @param {module:utils/dom/rect~Rect} firstRect
// @param {module:utils/dom/rect~Rect} secondRect
// @returns {Boolean}
function isBelow(firstRect, secondRect) {
    return firstRect.bottom > secondRect.bottom;
}
// Determines if a given `Rect` extends beyond the top edge of the second `Rect`.
//
// @private
// @param {module:utils/dom/rect~Rect} firstRect
// @param {module:utils/dom/rect~Rect} secondRect
// @returns {Boolean}
function isAbove(firstRect, secondRect) {
    return firstRect.top < secondRect.top;
}
// Determines if a given `Rect` extends beyond the left edge of the second `Rect`.
//
// @private
// @param {module:utils/dom/rect~Rect} firstRect
// @param {module:utils/dom/rect~Rect} secondRect
// @returns {Boolean}
function isLeftOf(firstRect, secondRect) {
    return firstRect.left < secondRect.left;
}
// Determines if a given `Rect` extends beyond the right edge of the second `Rect`.
//
// @private
// @param {module:utils/dom/rect~Rect} firstRect
// @param {module:utils/dom/rect~Rect} secondRect
// @returns {Boolean}
function isRightOf(firstRect, secondRect) {
    return firstRect.right > secondRect.right;
}
// Returns the closest window of an element or range.
//
// @private
// @param {HTMLElement|Range} elementOrRange
// @returns {Window}
function getWindow(elementOrRange) {
    if (isRange(elementOrRange)) {
        return elementOrRange.startContainer.ownerDocument.defaultView;
    }
    else {
        return elementOrRange.ownerDocument.defaultView;
    }
}
// Returns the closest parent of an element or DOM range.
//
// @private
// @param {HTMLElement|Range} elementOrRange
// @returns {HTMLelement}
function getParentElement(elementOrRange) {
    if (isRange(elementOrRange)) {
        let parent = elementOrRange.commonAncestorContainer;
        // If a Range is attached to the Text, use the closest element ancestor.
        if (isText(parent)) {
            parent = parent.parentNode;
        }
        return parent;
    }
    else {
        return elementOrRange.parentNode;
    }
}
// Returns the rect of an element or range residing in an iframe.
// The result rect is relative to the geometry of the passed window instance.
//
// @private
// @param {HTMLElement|Range} target Element or range which rect should be returned.
// @param {Window} relativeWindow A window the rect should be relative to.
// @returns {module:utils/dom/rect~Rect}
function getRectRelativeToWindow(target, relativeWindow) {
    const targetWindow = getWindow(target);
    const rect = new rect_Rect(target);
    if (targetWindow === relativeWindow) {
        return rect;
    }
    else {
        let currentWindow = targetWindow;
        while (currentWindow != relativeWindow) {
            const frame = currentWindow.frameElement;
            const frameRect = new rect_Rect(frame).excludeScrollbarsAndBorders();
            rect.moveBy(frameRect.left, frameRect.top);
            currentWindow = currentWindow.parent;
        }
    }
    return rect;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/view.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/view
 */





















/**
 * Editor's view controller class. Its main responsibility is DOM - View management for editing purposes, to provide
 * abstraction over the DOM structure and events and hide all browsers quirks.
 *
 * View controller renders view document to DOM whenever view structure changes. To determine when view can be rendered,
 * all changes need to be done using the {@link module:engine/view/view~View#change} method, using
 * {@link module:engine/view/downcastwriter~DowncastWriter}:
 *
 *		view.change( writer => {
 *			writer.insert( position, writer.createText( 'foo' ) );
 *		} );
 *
 * View controller also register {@link module:engine/view/observer/observer~Observer observers} which observes changes
 * on DOM and fire events on the {@link module:engine/view/document~Document Document}.
 * Note that the following observers are added by the class constructor and are always available:
 *
 * * {@link module:engine/view/observer/selectionobserver~SelectionObserver},
 * * {@link module:engine/view/observer/focusobserver~FocusObserver},
 * * {@link module:engine/view/observer/keyobserver~KeyObserver},
 * * {@link module:engine/view/observer/fakeselectionobserver~FakeSelectionObserver}.
 * * {@link module:engine/view/observer/compositionobserver~CompositionObserver}.
 * * {@link module:engine/view/observer/inputobserver~InputObserver}.
 * * {@link module:engine/view/observer/arrowkeysobserver~ArrowKeysObserver}.
 * * {@link module:engine/view/observer/tabobserver~TabObserver}.
 *
 * This class also {@link module:engine/view/view~View#attachDomRoot binds the DOM and the view elements}.
 *
 * If you do not need full a DOM - view management, and only want to transform a tree of view elements to a tree of DOM
 * elements you do not need this controller. You can use the {@link module:engine/view/domconverter~DomConverter DomConverter} instead.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class view_View extends Observable {
    /**
     * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
     */
    constructor(stylesProcessor) {
        super();
        /**
         * Instance of the {@link module:engine/view/document~Document} associated with this view controller.
         *
         * @readonly
         * @type {module:engine/view/document~Document}
         */
        this.document = new Document(stylesProcessor);
        /**
         * Instance of the {@link module:engine/view/domconverter~DomConverter domConverter} used by
         * {@link module:engine/view/view~View#_renderer renderer}
         * and {@link module:engine/view/observer/observer~Observer observers}.
         *
         * @readonly
         * @type {module:engine/view/domconverter~DomConverter}
         */
        this.domConverter = new DomConverter(this.document);
        /**
         * Roots of the DOM tree. Map on the `HTMLElement`s with roots names as keys.
         *
         * @readonly
         * @type {Map.<String, HTMLElement>}
         */
        this.domRoots = new Map();
        /**
         * Used to prevent calling {@link #forceRender} and {@link #change} during rendering view to the DOM.
         *
         * @readonly
         * @member {Boolean} #isRenderingInProgress
         */
        this.set('isRenderingInProgress', false);
        /**
         * Informs whether the DOM selection is inside any of the DOM roots managed by the view.
         *
         * @readonly
         * @member {Boolean} #hasDomSelection
         */
        this.set('hasDomSelection', false);
        /**
         * Instance of the {@link module:engine/view/renderer~Renderer renderer}.
         *
         * @protected
         * @type {module:engine/view/renderer~Renderer}
         */
        this._renderer = new Renderer(this.domConverter, this.document.selection);
        this._renderer.bind('isFocused', 'isSelecting', 'isComposing').to(this.document, 'isFocused', 'isSelecting', 'isComposing');
        /**
         * A DOM root attributes cache. It saves the initial values of DOM root attributes before the DOM element
         * is {@link module:engine/view/view~View#attachDomRoot attached} to the view so later on, when
         * the view is destroyed ({@link module:engine/view/view~View#detachDomRoot}), they can be easily restored.
         * This way, the DOM element can go back to the (clean) state as if the editing view never used it.
         *
         * @private
         * @member {WeakMap.<HTMLElement,Object>}
         */
        this._initialDomRootAttributes = new WeakMap();
        /**
         * Map of registered {@link module:engine/view/observer/observer~Observer observers}.
         *
         * @private
         * @type {Map.<Function, module:engine/view/observer/observer~Observer>}
         */
        this._observers = new Map();
        /**
         * Is set to `true` when {@link #change view changes} are currently in progress.
         *
         * @private
         * @type {Boolean}
         */
        this._ongoingChange = false;
        /**
         * Used to prevent calling {@link #forceRender} and {@link #change} during rendering view to the DOM.
         *
         * @private
         * @type {Boolean}
         */
        this._postFixersInProgress = false;
        /**
         * Internal flag to temporary disable rendering. See the usage in the {@link #_disableRendering}.
         *
         * @private
         * @type {Boolean}
         */
        this._renderingDisabled = false;
        /**
         * Internal flag that disables rendering when there are no changes since the last rendering.
         * It stores information about changed selection and changed elements from attached document roots.
         *
         * @private
         * @type {Boolean}
         */
        this._hasChangedSinceTheLastRendering = false;
        /**
         * DowncastWriter instance used in {@link #change change method} callbacks.
         *
         * @private
         * @type {module:engine/view/downcastwriter~DowncastWriter}
         */
        this._writer = new DowncastWriter(this.document);
        // Add default observers.
        this.addObserver(MutationObserver);
        this.addObserver(SelectionObserver);
        this.addObserver(FocusObserver);
        this.addObserver(KeyObserver);
        this.addObserver(FakeSelectionObserver);
        this.addObserver(CompositionObserver);
        this.addObserver(ArrowKeysObserver);
        this.addObserver(InputObserver);
        this.addObserver(TabObserver);
        // Inject quirks handlers.
        injectQuirksHandling(this);
        injectUiElementHandling(this);
        // Use 'normal' priority so that rendering is performed as first when using that priority.
        this.on('render', () => {
            this._render();
            // Informs that layout has changed after render.
            this.document.fire('layoutChanged');
            // Reset the `_hasChangedSinceTheLastRendering` flag after rendering.
            this._hasChangedSinceTheLastRendering = false;
        });
        // Listen to the document selection changes directly.
        this.listenTo(this.document.selection, 'change', () => {
            this._hasChangedSinceTheLastRendering = true;
        });
        // Trigger re-render if only the focus changed.
        this.listenTo(this.document, 'change:isFocused', () => {
            this._hasChangedSinceTheLastRendering = true;
        });
    }
    /**
     * Attaches a DOM root element to the view element and enable all observers on that element.
     * Also {@link module:engine/view/renderer~Renderer#markToSync mark element} to be synchronized
     * with the view what means that all child nodes will be removed and replaced with content of the view root.
     *
     * This method also will change view element name as the same as tag name of given dom root.
     * Name is always transformed to lower case.
     *
     * **Note:** Use {@link #detachDomRoot `detachDomRoot()`} to revert this action.
     *
     * @param {Element} domRoot DOM root element.
     * @param {String} [name='main'] Name of the root.
     */
    attachDomRoot(domRoot, name = 'main') {
        const viewRoot = this.document.getRoot(name);
        // Set view root name the same as DOM root tag name.
        viewRoot._name = domRoot.tagName.toLowerCase();
        const initialDomRootAttributes = {};
        // 1. Copy and cache the attributes to remember the state of the element before attaching.
        //    The cached attributes will be restored in detachDomRoot() so the element goes to the
        //    clean state as if the editing view never used it.
        // 2. Apply the attributes using the view writer, so they all go under the control of the engine.
        //    The editing view takes over the attribute management completely because various
        //    features (e.g. addPlaceholder()) require dynamic changes of those attributes and they
        //    cannot be managed by the engine and the UI library at the same time.
        for (const { name, value } of Array.from(domRoot.attributes)) {
            initialDomRootAttributes[name] = value;
            // Do not use writer.setAttribute() for the class attribute. The EditableUIView class
            // and its descendants could have already set some using the writer.addClass() on the view
            // document root. They haven't been rendered yet so they are not present in the DOM root.
            // Using writer.setAttribute( 'class', ... ) would override them completely.
            if (name === 'class') {
                this._writer.addClass(value.split(' '), viewRoot);
            }
            else {
                this._writer.setAttribute(name, value, viewRoot);
            }
        }
        this._initialDomRootAttributes.set(domRoot, initialDomRootAttributes);
        const updateContenteditableAttribute = () => {
            this._writer.setAttribute('contenteditable', (!viewRoot.isReadOnly).toString(), viewRoot);
            if (viewRoot.isReadOnly) {
                this._writer.addClass('ck-read-only', viewRoot);
            }
            else {
                this._writer.removeClass('ck-read-only', viewRoot);
            }
        };
        // Set initial value.
        updateContenteditableAttribute();
        this.domRoots.set(name, domRoot);
        this.domConverter.bindElements(domRoot, viewRoot);
        this._renderer.markToSync('children', viewRoot);
        this._renderer.markToSync('attributes', viewRoot);
        this._renderer.domDocuments.add(domRoot.ownerDocument);
        viewRoot.on('change:children', (evt, node) => this._renderer.markToSync('children', node));
        viewRoot.on('change:attributes', (evt, node) => this._renderer.markToSync('attributes', node));
        viewRoot.on('change:text', (evt, node) => this._renderer.markToSync('text', node));
        viewRoot.on('change:isReadOnly', () => this.change(updateContenteditableAttribute));
        viewRoot.on('change', () => {
            this._hasChangedSinceTheLastRendering = true;
        });
        for (const observer of this._observers.values()) {
            observer.observe(domRoot, name);
        }
    }
    /**
     * Detaches a DOM root element from the view element and restores its attributes to the state before
     * {@link #attachDomRoot `attachDomRoot()`}.
     *
     * @param {String} name Name of the root to detach.
     */
    detachDomRoot(name) {
        const domRoot = this.domRoots.get(name);
        // Remove all root attributes so the DOM element is "bare".
        Array.from(domRoot.attributes).forEach(({ name }) => domRoot.removeAttribute(name));
        const initialDomRootAttributes = this._initialDomRootAttributes.get(domRoot);
        // Revert all view root attributes back to the state before attachDomRoot was called.
        for (const attribute in initialDomRootAttributes) {
            domRoot.setAttribute(attribute, initialDomRootAttributes[attribute]);
        }
        this.domRoots.delete(name);
        this.domConverter.unbindDomElement(domRoot);
    }
    /**
     * Gets DOM root element.
     *
     * @param {String} [name='main']  Name of the root.
     * @returns {Element} DOM root element instance.
     */
    getDomRoot(name = 'main') {
        return this.domRoots.get(name);
    }
    /**
     * Creates observer of the given type if not yet created, {@link module:engine/view/observer/observer~Observer#enable enables} it
     * and {@link module:engine/view/observer/observer~Observer#observe attaches} to all existing and future
     * {@link #domRoots DOM roots}.
     *
     * Note: Observers are recognized by their constructor (classes). A single observer will be instantiated and used only
     * when registered for the first time. This means that features and other components can register a single observer
     * multiple times without caring whether it has been already added or not.
     *
     * @param {Function} Observer The constructor of an observer to add.
     * Should create an instance inheriting from {@link module:engine/view/observer/observer~Observer}.
     * @returns {module:engine/view/observer/observer~Observer} Added observer instance.
     */
    addObserver(ObserverConstructor) {
        let observer = this._observers.get(ObserverConstructor);
        if (observer) {
            return observer;
        }
        observer = new ObserverConstructor(this);
        this._observers.set(ObserverConstructor, observer);
        for (const [name, domElement] of this.domRoots) {
            observer.observe(domElement, name);
        }
        observer.enable();
        return observer;
    }
    /**
     * Returns observer of the given type or `undefined` if such observer has not been added yet.
     *
     * @param {Function} Observer The constructor of an observer to get.
     * @returns {module:engine/view/observer/observer~Observer|undefined} Observer instance or undefined.
     */
    getObserver(ObserverConstructor) {
        return this._observers.get(ObserverConstructor);
    }
    /**
     * Disables all added observers.
     */
    disableObservers() {
        for (const observer of this._observers.values()) {
            observer.disable();
        }
    }
    /**
     * Enables all added observers.
     */
    enableObservers() {
        for (const observer of this._observers.values()) {
            observer.enable();
        }
    }
    /**
     * Scrolls the page viewport and {@link #domRoots} with their ancestors to reveal the
     * caret, if not already visible to the user.
     */
    scrollToTheSelection() {
        const range = this.document.selection.getFirstRange();
        if (range) {
            scrollViewportToShowTarget({
                target: this.domConverter.viewRangeToDom(range),
                viewportOffset: 20
            });
        }
    }
    /**
     * It will focus DOM element representing {@link module:engine/view/editableelement~EditableElement EditableElement}
     * that is currently having selection inside.
     */
    focus() {
        if (!this.document.isFocused) {
            const editable = this.document.selection.editableElement;
            if (editable) {
                this.domConverter.focus(editable);
                this.forceRender();
            }
            else {
                // Before focusing view document, selection should be placed inside one of the view's editables.
                // Normally its selection will be converted from model document (which have default selection), but
                // when using view document on its own, we need to manually place selection before focusing it.
                //
                // @if CK_DEBUG // console.warn( 'There is no selection in any editable to focus.' );
            }
        }
    }
    /**
     * The `change()` method is the primary way of changing the view. You should use it to modify any node in the view tree.
     * It makes sure that after all changes are made the view is rendered to the DOM (assuming that the view will be changed
     * inside the callback). It prevents situations when the DOM is updated when the view state is not yet correct. It allows
     * to nest calls one inside another and still performs a single rendering after all those changes are made.
     * It also returns the return value of its callback.
     *
     *		const text = view.change( writer => {
     *			const newText = writer.createText( 'foo' );
     *			writer.insert( position1, newText );
     *
     *			view.change( writer => {
     *				writer.insert( position2, writer.createText( 'bar' ) );
     *			} );
     *
     * 			writer.remove( range );
     *
     * 			return newText;
     *		} );
     *
     * When the outermost change block is done and rendering to the DOM is over the
     * {@link module:engine/view/view~View#event:render `View#render`} event is fired.
     *
     * This method throws a `applying-view-changes-on-rendering` error when
     * the change block is used after rendering to the DOM has started.
     *
     * @param {Function} callback Callback function which may modify the view.
     * @returns {*} Value returned by the callback.
     */
    change(callback) {
        if (this.isRenderingInProgress || this._postFixersInProgress) {
            /**
             * Thrown when there is an attempt to make changes to the view tree when it is in incorrect state. This may
             * cause some unexpected behaviour and inconsistency between the DOM and the view.
             * This may be caused by:
             *
             * * calling {@link #change} or {@link #forceRender} during rendering process,
             * * calling {@link #change} or {@link #forceRender} inside of
             *   {@link module:engine/view/document~Document#registerPostFixer post-fixer function}.
             *
             * @error cannot-change-view-tree
             */
            throw new CKEditorError('cannot-change-view-tree', this);
        }
        try {
            // Recursive call to view.change() method - execute listener immediately.
            if (this._ongoingChange) {
                return callback(this._writer);
            }
            // This lock will assure that all recursive calls to view.change() will end up in same block - one "render"
            // event for all nested calls.
            this._ongoingChange = true;
            const callbackResult = callback(this._writer);
            this._ongoingChange = false;
            // This lock is used by editing controller to render changes from outer most model.change() once. As plugins might call
            // view.change() inside model.change() block - this will ensures that postfixers and rendering are called once after all
            // changes. Also, we don't need to render anything if there're no changes since last rendering.
            if (!this._renderingDisabled && this._hasChangedSinceTheLastRendering) {
                this._postFixersInProgress = true;
                this.document._callPostFixers(this._writer);
                this._postFixersInProgress = false;
                this.fire('render');
            }
            return callbackResult;
        }
        catch (err) {
            // @if CK_DEBUG // throw err;
            /* istanbul ignore next */
            CKEditorError.rethrowUnexpectedError(err, this);
        }
    }
    /**
     * Forces rendering {@link module:engine/view/document~Document view document} to DOM. If any view changes are
     * currently in progress, rendering will start after all {@link #change change blocks} are processed.
     *
     * Note that this method is dedicated for special cases. All view changes should be wrapped in the {@link #change}
     * block and the view will automatically check whether it needs to render DOM or not.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `applying-view-changes-on-rendering` when
     * trying to re-render when rendering to DOM has already started.
     */
    forceRender() {
        this._hasChangedSinceTheLastRendering = true;
        this.change(() => { });
    }
    /**
     * Destroys this instance. Makes sure that all observers are destroyed and listeners removed.
     */
    destroy() {
        for (const observer of this._observers.values()) {
            observer.destroy();
        }
        this.document.destroy();
        this.stopListening();
    }
    /**
     * Creates position at the given location. The location can be specified as:
     *
     * * a {@link module:engine/view/position~Position position},
     * * parent element and offset (offset defaults to `0`),
     * * parent element and `'end'` (sets position at the end of that element),
     * * {@link module:engine/view/item~Item view item} and `'before'` or `'after'` (sets position before or after given view item).
     *
     * This method is a shortcut to other constructors such as:
     *
     * * {@link #createPositionBefore},
     * * {@link #createPositionAfter},
     *
     * @param {module:engine/view/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/view/item~Item view item}.
     */
    createPositionAt(itemOrPosition, offset) {
        return Position._createAt(itemOrPosition, offset);
    }
    /**
     * Creates a new position after given view item.
     *
     * @param {module:engine/view/item~Item} item View item after which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    createPositionAfter(item) {
        return Position._createAfter(item);
    }
    /**
     * Creates a new position before given view item.
     *
     * @param {module:engine/view/item~Item} item View item before which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    createPositionBefore(item) {
        return Position._createBefore(item);
    }
    /**
     * Creates a range spanning from `start` position to `end` position.
     *
     * **Note:** This factory method creates it's own {@link module:engine/view/position~Position} instances basing on passed values.
     *
     * @param {module:engine/view/position~Position} start Start position.
     * @param {module:engine/view/position~Position} [end] End position. If not set, range will be collapsed at `start` position.
     * @returns {module:engine/view/range~Range}
     */
    createRange(...args) {
        return new Range(...args);
    }
    /**
     * Creates a range that starts before given {@link module:engine/view/item~Item view item} and ends after it.
     *
     * @param {module:engine/view/item~Item} item
     * @returns {module:engine/view/range~Range}
     */
    createRangeOn(item) {
        return Range._createOn(item);
    }
    /**
     * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     * @param {module:engine/view/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/view/range~Range}
     */
    createRangeIn(element) {
        return Range._createIn(element);
    }
    /**
     Creates new {@link module:engine/view/selection~Selection} instance.
     *
     * 		// Creates empty selection without ranges.
     *		const selection = view.createSelection();
     *
     *		// Creates selection at the given range.
     *		const range = view.createRange( start, end );
     *		const selection = view.createSelection( range );
     *
     *		// Creates selection at the given ranges
     * 		const ranges = [ view.createRange( start1, end2 ), view.createRange( star2, end2 ) ];
     *		const selection = view.createSelection( ranges );
     *
     *		// Creates selection from the other selection.
     *		const otherSelection = view.createSelection();
     *		const selection = view.createSelection( otherSelection );
     *
     *		// Creates selection from the document selection.
     *		const selection = view.createSelection( editor.editing.view.document.selection );
     *
     * 		// Creates selection at the given position.
     *		const position = view.createPositionFromPath( root, path );
     *		const selection = view.createSelection( position );
     *
     *		// Creates collapsed selection at the position of given item and offset.
     *		const paragraph = view.createContainerElement( 'paragraph' );
     *		const selection = view.createSelection( paragraph, offset );
     *
     *		// Creates a range inside an {@link module:engine/view/element~Element element} which starts before the
     *		// first child of that element and ends after the last child of that element.
     *		const selection = view.createSelection( paragraph, 'in' );
     *
     *		// Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends
     *		// just after the item.
     *		const selection = view.createSelection( paragraph, 'on' );
     *
     * `Selection`'s factory method allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Creates backward selection.
     *		const selection = view.createSelection( range, { backward: true } );
     *
     * Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
     * (and be  properly handled by screen readers).
     *
     *		// Creates fake selection with label.
     *		const selection = view.createSelection( range, { fake: true, label: 'foo' } );
     *
     * @param {module:engine/view/selection~Selectable} [selectable=null]
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Offset or place when selectable is an `Item`.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     * @returns {module:engine/view/selection~Selection}
     */
    createSelection(...args) {
        return new Selection(...args);
    }
    /**
     * Disables or enables rendering. If the flag is set to `true` then the rendering will be disabled.
     * If the flag is set to `false` and if there was some change in the meantime, then the rendering action will be performed.
     *
     * @protected
     * @internal
     * @param {Boolean} flag A flag indicates whether the rendering should be disabled.
     */
    _disableRendering(flag) {
        this._renderingDisabled = flag;
        if (flag == false) {
            // Render when you stop blocking rendering.
            this.change(() => { });
        }
    }
    /**
     * Renders all changes. In order to avoid triggering the observers (e.g. selection) all observers are disabled
     * before rendering and re-enabled after that.
     *
     * @private
     */
    _render() {
        this.isRenderingInProgress = true;
        this.disableObservers();
        this._renderer.render();
        this.enableObservers();
        this.isRenderingInProgress = false;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/typecheckable.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
class typecheckable_TypeCheckable {
    /* istanbul ignore next */
    is() {
        // There are a lot of overloads above.
        // Overriding method in derived classes remove them and only `is( type: string ): boolean` is visible which we don't want.
        // One option would be to copy them all to all classes, but that's ugly.
        // It's best when TypeScript compiler doesn't see those overloads, except the one in the top base class.
        // To overload a method, but not let the compiler see it, do after class definition:
        // `MyClass.prototype.is = function( type: string ) {...}`
        throw new Error('is() method is abstract');
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/node.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable @typescript-eslint/no-unused-vars */
/**
 * @module engine/model/node
 */




// To check if component is loaded more than once.

/**
 * Model node. Most basic structure of model tree.
 *
 * This is an abstract class that is a base for other classes representing different nodes in model.
 *
 * **Note:** If a node is detached from the model tree, you can manipulate it using it's API.
 * However, it is **very important** that nodes already attached to model tree should be only changed through
 * {@link module:engine/model/writer~Writer Writer API}.
 *
 * Changes done by `Node` methods, like {@link module:engine/model/element~Element#_insertChild _insertChild} or
 * {@link module:engine/model/node~Node#_setAttribute _setAttribute}
 * do not generate {@link module:engine/model/operation/operation~Operation operations}
 * which are essential for correct editor work if you modify nodes in {@link module:engine/model/document~Document document} root.
 *
 * The flow of working on `Node` (and classes that inherits from it) is as such:
 * 1. You can create a `Node` instance, modify it using it's API.
 * 2. Add `Node` to the model using `Batch` API.
 * 3. Change `Node` that was already added to the model using `Batch` API.
 *
 * Similarly, you cannot use `Batch` API on a node that has not been added to the model tree, with the exception
 * of {@link module:engine/model/writer~Writer#insert inserting} that node to the model tree.
 *
 * Be aware that using {@link module:engine/model/writer~Writer#remove remove from Batch API} does not allow to use `Node` API because
 * the information about `Node` is still kept in model document.
 *
 * In case of {@link module:engine/model/element~Element element node}, adding and removing children also counts as changing a node and
 * follows same rules.
 */
class model_node_Node extends typecheckable_TypeCheckable {
    /**
     * Creates a model node.
     *
     * This is an abstract class, so this constructor should not be used directly.
     *
     * @abstract
     * @param {Object} [attrs] Node's attributes. See {@link module:utils/tomap~toMap} for a list of accepted values.
     */
    constructor(attrs) {
        super();
        /**
         * Parent of this node. It could be {@link module:engine/model/element~Element}
         * or {@link module:engine/model/documentfragment~DocumentFragment}.
         * Equals to `null` if the node has no parent.
         *
         * @readonly
         * @member {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment|null}
         */
        this.parent = null;
        /**
         * Attributes set on this node.
         *
         * @private
         * @member {Map} module:engine/model/node~Node#_attrs
         */
        this._attrs = toMap(attrs);
    }
    /**
     * {@link module:engine/model/document~Document Document} that owns this root element.
     *
     * @readonly
     * @type {module:engine/model/document~Document|null}
     */
    get document() {
        return null;
    }
    /**
     * Index of this node in it's parent or `null` if the node has no parent.
     *
     * Accessing this property throws an error if this node's parent element does not contain it.
     * This means that model tree got broken.
     *
     * @readonly
     * @type {Number|null}
     */
    get index() {
        let pos;
        if (!this.parent) {
            return null;
        }
        if ((pos = this.parent.getChildIndex(this)) === null) {
            throw new CKEditorError('model-node-not-found-in-parent', this);
        }
        return pos;
    }
    /**
     * Offset at which this node starts in it's parent. It is equal to the sum of {@link #offsetSize offsetSize}
     * of all it's previous siblings. Equals to `null` if node has no parent.
     *
     * Accessing this property throws an error if this node's parent element does not contain it.
     * This means that model tree got broken.
     *
     * @readonly
     * @type {Number|null}
     */
    get startOffset() {
        let pos;
        if (!this.parent) {
            return null;
        }
        if ((pos = this.parent.getChildStartOffset(this)) === null) {
            throw new CKEditorError('model-node-not-found-in-parent', this);
        }
        return pos;
    }
    /**
     * Offset size of this node. Represents how much "offset space" is occupied by the node in it's parent.
     * It is important for {@link module:engine/model/position~Position position}. When node has `offsetSize` greater than `1`, position
     * can be placed between that node start and end. `offsetSize` greater than `1` is for nodes that represents more
     * than one entity, i.e. {@link module:engine/model/text~Text text node}.
     *
     * @readonly
     * @type {Number}
     */
    get offsetSize() {
        return 1;
    }
    /**
     * Offset at which this node ends in it's parent. It is equal to the sum of this node's
     * {@link module:engine/model/node~Node#startOffset start offset} and {@link #offsetSize offset size}.
     * Equals to `null` if the node has no parent.
     *
     * @readonly
     * @type {Number|null}
     */
    get endOffset() {
        if (!this.parent) {
            return null;
        }
        return this.startOffset + this.offsetSize;
    }
    /**
     * Node's next sibling or `null` if the node is a last child of it's parent or if the node has no parent.
     *
     * @readonly
     * @type {module:engine/model/node~Node|null}
     */
    get nextSibling() {
        const index = this.index;
        return (index !== null && this.parent.getChild(index + 1)) || null;
    }
    /**
     * Node's previous sibling or `null` if the node is a first child of it's parent or if the node has no parent.
     *
     * @readonly
     * @type {module:engine/model/node~Node|null}
     */
    get previousSibling() {
        const index = this.index;
        return (index !== null && this.parent.getChild(index - 1)) || null;
    }
    /**
     * The top-most ancestor of the node. If node has no parent it is the root itself. If the node is a part
     * of {@link module:engine/model/documentfragment~DocumentFragment}, it's `root` is equal to that `DocumentFragment`.
     *
     * @readonly
     * @type {module:engine/model/node~Node|module:engine/model/documentfragment~DocumentFragment}
     */
    get root() {
        // eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
        let root = this;
        while (root.parent) {
            root = root.parent;
        }
        return root;
    }
    /**
     * Returns true if the node is in a tree rooted in the document (is a descendant of one of its roots).
     *
     * @returns {Boolean}
     */
    isAttached() {
        return this.root.is('rootElement');
    }
    /**
     * Gets path to the node. The path is an array containing starting offsets of consecutive ancestors of this node,
     * beginning from {@link module:engine/model/node~Node#root root}, down to this node's starting offset. The path can be used to
     * create {@link module:engine/model/position~Position Position} instance.
     *
     *		const abc = new Text( 'abc' );
     *		const foo = new Text( 'foo' );
     *		const h1 = new Element( 'h1', null, new Text( 'header' ) );
     *		const p = new Element( 'p', null, [ abc, foo ] );
     *		const div = new Element( 'div', null, [ h1, p ] );
     *		foo.getPath(); // Returns [ 1, 3 ]. `foo` is in `p` which is in `div`. `p` starts at offset 1, while `foo` at 3.
     *		h1.getPath(); // Returns [ 0 ].
     *		div.getPath(); // Returns [].
     *
     * @returns {Array.<Number>} The path.
     */
    getPath() {
        const path = [];
        // eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
        let node = this;
        while (node.parent) {
            path.unshift(node.startOffset);
            node = node.parent;
        }
        return path;
    }
    /**
     * Returns ancestors array of this node.
     *
     * @param {Object} options Options object.
     * @param {Boolean} [options.includeSelf=false] When set to `true` this node will be also included in parent's array.
     * @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from node's parent to root element,
     * otherwise root element will be the first item in the array.
     * @returns {Array} Array with ancestors.
     */
    getAncestors(options = {}) {
        const ancestors = [];
        let parent = options.includeSelf ? this : this.parent;
        while (parent) {
            ancestors[options.parentFirst ? 'push' : 'unshift'](parent);
            parent = parent.parent;
        }
        return ancestors;
    }
    /**
     * Returns a {@link module:engine/model/element~Element} or {@link module:engine/model/documentfragment~DocumentFragment}
     * which is a common ancestor of both nodes.
     *
     * @param {module:engine/model/node~Node} node The second node.
     * @param {Object} options Options object.
     * @param {Boolean} [options.includeSelf=false] When set to `true` both nodes will be considered "ancestors" too.
     * Which means that if e.g. node A is inside B, then their common ancestor will be B.
     * @returns {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment|null}
     */
    getCommonAncestor(node, options = {}) {
        const ancestorsA = this.getAncestors(options);
        const ancestorsB = node.getAncestors(options);
        let i = 0;
        while (ancestorsA[i] == ancestorsB[i] && ancestorsA[i]) {
            i++;
        }
        return i === 0 ? null : ancestorsA[i - 1];
    }
    /**
     * Returns whether this node is before given node. `false` is returned if nodes are in different trees (for example,
     * in different {@link module:engine/model/documentfragment~DocumentFragment}s).
     *
     * @param {module:engine/model/node~Node} node Node to compare with.
     * @returns {Boolean}
     */
    isBefore(node) {
        // Given node is not before this node if they are same.
        if (this == node) {
            return false;
        }
        // Return `false` if it is impossible to compare nodes.
        if (this.root !== node.root) {
            return false;
        }
        const thisPath = this.getPath();
        const nodePath = node.getPath();
        const result = compareArrays(thisPath, nodePath);
        switch (result) {
            case 'prefix':
                return true;
            case 'extension':
                return false;
            default:
                return thisPath[result] < nodePath[result];
        }
    }
    /**
     * Returns whether this node is after given node. `false` is returned if nodes are in different trees (for example,
     * in different {@link module:engine/model/documentfragment~DocumentFragment}s).
     *
     * @param {module:engine/model/node~Node} node Node to compare with.
     * @returns {Boolean}
     */
    isAfter(node) {
        // Given node is not before this node if they are same.
        if (this == node) {
            return false;
        }
        // Return `false` if it is impossible to compare nodes.
        if (this.root !== node.root) {
            return false;
        }
        // In other cases, just check if the `node` is before, and return the opposite.
        return !this.isBefore(node);
    }
    /**
     * Checks if the node has an attribute with given key.
     *
     * @param {String} key Key of attribute to check.
     * @returns {Boolean} `true` if attribute with given key is set on node, `false` otherwise.
     */
    hasAttribute(key) {
        return this._attrs.has(key);
    }
    /**
     * Gets an attribute value for given key or `undefined` if that attribute is not set on node.
     *
     * @param {String} key Key of attribute to look for.
     * @returns {*} Attribute value or `undefined`.
     */
    getAttribute(key) {
        return this._attrs.get(key);
    }
    /**
     * Returns iterator that iterates over this node's attributes.
     *
     * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
     * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
     *
     * @returns {Iterable.<*>}
     */
    getAttributes() {
        return this._attrs.entries();
    }
    /**
     * Returns iterator that iterates over this node's attribute keys.
     *
     * @returns {Iterable.<String>}
     */
    getAttributeKeys() {
        return this._attrs.keys();
    }
    /**
     * Converts `Node` to plain object and returns it.
     *
     * @returns {Object} `Node` converted to plain object.
     */
    toJSON() {
        const json = {};
        // Serializes attributes to the object.
        // attributes = { a: 'foo', b: 1, c: true }.
        if (this._attrs.size) {
            json.attributes = Array.from(this._attrs).reduce((result, attr) => {
                result[attr[0]] = attr[1];
                return result;
            }, {});
        }
        return json;
    }
    /**
     * Creates a copy of this node, that is a node with exactly same attributes, and returns it.
     *
     * @internal
     * @protected
     * @returns {module:engine/model/node~Node} Node with same attributes as this node.
     */
    _clone(_deep) {
        return new model_node_Node(this._attrs);
    }
    /**
     * Removes this node from it's parent.
     *
     * @internal
     * @see module:engine/model/writer~Writer#remove
     * @protected
     */
    _remove() {
        this.parent._removeChildren(this.index);
    }
    /**
     * Sets attribute on the node. If attribute with the same key already is set, it's value is overwritten.
     *
     * @see module:engine/model/writer~Writer#setAttribute
     * @internal
     * @protected
     * @param {String} key Key of attribute to set.
     * @param {*} value Attribute value.
     */
    _setAttribute(key, value) {
        this._attrs.set(key, value);
    }
    /**
     * Removes all attributes from the node and sets given attributes.
     *
     * @see module:engine/model/writer~Writer#setAttributes
     * @internal
     * @protected
     * @param {Object} [attrs] Attributes to set. See {@link module:utils/tomap~toMap} for a list of accepted values.
     */
    _setAttributesTo(attrs) {
        this._attrs = toMap(attrs);
    }
    /**
     * Removes an attribute with given key from the node.
     *
     * @see module:engine/model/writer~Writer#removeAttribute
     * @internal
     * @protected
     * @param {String} key Key of attribute to remove.
     * @returns {Boolean} `true` if the attribute was set on the element, `false` otherwise.
     */
    _removeAttribute(key) {
        return this._attrs.delete(key);
    }
    /**
     * Removes all attributes from the node.
     *
     * @see module:engine/model/writer~Writer#clearAttributes
     * @internal
     * @protected
     */
    _clearAttributes() {
        this._attrs.clear();
    }
}
/**
 * Checks whether this object is of the given type.
 *
 * This method is useful when processing model objects that are of unknown type. For example, a function
 * may return a {@link module:engine/model/documentfragment~DocumentFragment} or a {@link module:engine/model/node~Node}
 * that can be either a text node or an element. This method can be used to check what kind of object is returned.
 *
 *		someObject.is( 'element' ); // -> true if this is an element
 *		someObject.is( 'node' ); // -> true if this is a node (a text node or an element)
 *		someObject.is( 'documentFragment' ); // -> true if this is a document fragment
 *
 * Since this method is also available on a range of view objects, you can prefix the type of the object with
 * `model:` or `view:` to check, for example, if this is the model's or view's element:
 *
 *		modelElement.is( 'model:element' ); // -> true
 *		modelElement.is( 'view:element' ); // -> false
 *
 * By using this method it is also possible to check a name of an element:
 *
 *		imageElement.is( 'element', 'imageBlock' ); // -> true
 *		imageElement.is( 'element', 'imageBlock' ); // -> same as above
 *		imageElement.is( 'model:element', 'imageBlock' ); // -> same as above, but more precise
 *
 * The list of model objects which implement the `is()` method:
 *
 * * {@link module:engine/model/node~Node#is `Node#is()`}
 * * {@link module:engine/model/text~Text#is `Text#is()`}
 * * {@link module:engine/model/element~Element#is `Element#is()`}
 * * {@link module:engine/model/rootelement~RootElement#is `RootElement#is()`}
 * * {@link module:engine/model/position~Position#is `Position#is()`}
 * * {@link module:engine/model/liveposition~LivePosition#is `LivePosition#is()`}
 * * {@link module:engine/model/range~Range#is `Range#is()`}
 * * {@link module:engine/model/liverange~LiveRange#is `LiveRange#is()`}
 * * {@link module:engine/model/documentfragment~DocumentFragment#is `DocumentFragment#is()`}
 * * {@link module:engine/model/selection~Selection#is `Selection#is()`}
 * * {@link module:engine/model/documentselection~DocumentSelection#is `DocumentSelection#is()`}
 * * {@link module:engine/model/markercollection~Marker#is `Marker#is()`}
 * * {@link module:engine/model/textproxy~TextProxy#is `TextProxy#is()`}
 *
 * @method #is
 * @param {String} type Type to check.
 * @returns {Boolean}
 */
model_node_Node.prototype.is = function (type) {
    return type === 'node' || type === 'model:node';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/splicearray.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/splicearray
 */
const BIG_CHUNK_SIZE = 10000;
/**
 * Splices one array into another. To be used instead of `Array.prototype.splice` as the latter may
 * throw "Maximum call stack size exceeded" when passed huge number of items to insert.
 *
 * Note: in contrary to Array.splice, this function does not modify the original `target`.
 *
 * 		spliceArray( [ 1, 2 ], [ 3, 4 ], 0, 0 );		// [ 3, 4, 1, 2 ]
 * 		spliceArray( [ 1, 2 ], [ 3, 4 ], 1, 1 );		// [ 1, 3, 4 ]
 * 		spliceArray( [ 1, 2 ], [ 3, 4 ], 1, 0 );		// [ 1, 3, 4, 2 ]
 * 		spliceArray( [ 1, 2 ], [ 3, 4 ], 2, 0 );		// [ 1, 2, 3, 4 ]
 * 		spliceArray( [ 1, 2 ], [], 0, 1 );				// [ 2 ]
 *
 * @private
 * @param {Array} target Array to be spliced.
 * @param {Array} source Array of elements to be inserted to target.
 * @param {Number} start Index at which nodes should be inserted/removed.
 * @param {Number} count Number of items.
 *
 * @returns {Array} New spliced array.
 */
function spliceArray(target, source, start, count) {
    // In case of performance problems, see: https://github.com/ckeditor/ckeditor5/pull/12429/files#r965850568
    if (Math.max(source.length, target.length) > BIG_CHUNK_SIZE) {
        return target.slice(0, start).concat(source).concat(target.slice(start + count, target.length));
    }
    else {
        const newTarget = Array.from(target);
        newTarget.splice(start, count, ...source);
        return newTarget;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/nodelist.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/nodelist
 */



/**
 * Provides an interface to operate on a list of {@link module:engine/model/node~Node nodes}. `NodeList` is used internally
 * in classes like {@link module:engine/model/element~Element Element}
 * or {@link module:engine/model/documentfragment~DocumentFragment DocumentFragment}.
 */
class NodeList {
    /**
     * Creates an empty node list.
     *
     * @protected
     * @param {Iterable.<module:engine/model/node~Node>} [nodes] Nodes contained in this node list.
     */
    constructor(nodes) {
        /**
         * Nodes contained in this node list.
         *
         * @private
         * @member {Array.<module:engine/model/node~Node>}
         */
        this._nodes = [];
        if (nodes) {
            this._insertNodes(0, nodes);
        }
    }
    /**
     * Iterable interface.
     *
     * Iterates over all nodes contained inside this node list.
     *
     * @returns {Iterator.<module:engine/model/node~Node>}
     */
    [Symbol.iterator]() {
        return this._nodes[Symbol.iterator]();
    }
    /**
     * Number of nodes contained inside this node list.
     *
     * @readonly
     * @type {Number}
     */
    get length() {
        return this._nodes.length;
    }
    /**
     * Sum of {@link module:engine/model/node~Node#offsetSize offset sizes} of all nodes contained inside this node list.
     *
     * @readonly
     * @type {Number}
     */
    get maxOffset() {
        return this._nodes.reduce((sum, node) => sum + node.offsetSize, 0);
    }
    /**
     * Gets the node at the given index. Returns `null` if incorrect index was passed.
     *
     * @param {Number} index Index of node.
     * @returns {module:engine/model/node~Node|null} Node at given index.
     */
    getNode(index) {
        return this._nodes[index] || null;
    }
    /**
     * Returns an index of the given node. Returns `null` if given node is not inside this node list.
     *
     * @param {module:engine/model/node~Node} node Child node to look for.
     * @returns {Number|null} Child node's index.
     */
    getNodeIndex(node) {
        const index = this._nodes.indexOf(node);
        return index == -1 ? null : index;
    }
    /**
     * Returns the starting offset of given node. Starting offset is equal to the sum of
     * {@link module:engine/model/node~Node#offsetSize offset sizes} of all nodes that are before this node in this node list.
     *
     * @param {module:engine/model/node~Node} node Node to look for.
     * @returns {Number|null} Node's starting offset.
     */
    getNodeStartOffset(node) {
        const index = this.getNodeIndex(node);
        return index === null ? null : this._nodes.slice(0, index).reduce((sum, node) => sum + node.offsetSize, 0);
    }
    /**
     * Converts index to offset in node list.
     *
     * Returns starting offset of a node that is at given index. Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
     * `model-nodelist-index-out-of-bounds` if given index is less than `0` or more than {@link #length}.
     *
     * @param {Number} index Node's index.
     * @returns {Number} Node's starting offset.
     */
    indexToOffset(index) {
        if (index == this._nodes.length) {
            return this.maxOffset;
        }
        const node = this._nodes[index];
        if (!node) {
            /**
             * Given index cannot be found in the node list.
             *
             * @error model-nodelist-index-out-of-bounds
             */
            throw new CKEditorError('model-nodelist-index-out-of-bounds', this);
        }
        return this.getNodeStartOffset(node);
    }
    /**
     * Converts offset in node list to index.
     *
     * Returns index of a node that occupies given offset. Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError}
     * `model-nodelist-offset-out-of-bounds` if given offset is less than `0` or more than {@link #maxOffset}.
     *
     * @param {Number} offset Offset to look for.
     * @returns {Number} Index of a node that occupies given offset.
     */
    offsetToIndex(offset) {
        let totalOffset = 0;
        for (const node of this._nodes) {
            if (offset >= totalOffset && offset < totalOffset + node.offsetSize) {
                return this.getNodeIndex(node);
            }
            totalOffset += node.offsetSize;
        }
        if (totalOffset != offset) {
            /**
             * Given offset cannot be found in the node list.
             *
             * @error model-nodelist-offset-out-of-bounds
             * @param {Number} offset
             * @param {module:engine/model/nodelist~NodeList} nodeList Stringified node list.
             */
            throw new CKEditorError('model-nodelist-offset-out-of-bounds', this, {
                offset,
                nodeList: this
            });
        }
        return this.length;
    }
    /**
     * Inserts given nodes at given index.
     *
     * @internal
     * @protected
     * @param {Number} index Index at which nodes should be inserted.
     * @param {Iterable.<module:engine/model/node~Node>} nodes Nodes to be inserted.
     */
    _insertNodes(index, nodes) {
        // Validation.
        for (const node of nodes) {
            if (!(node instanceof model_node_Node)) {
                /**
                 * Trying to insert an object which is not a Node instance.
                 *
                 * @error model-nodelist-insertnodes-not-node
                 */
                throw new CKEditorError('model-nodelist-insertnodes-not-node', this);
            }
        }
        this._nodes = spliceArray(this._nodes, Array.from(nodes), index, 0);
    }
    /**
     * Removes one or more nodes starting at the given index.
     *
     * @internal
     * @protected
     * @param {Number} indexStart Index of the first node to remove.
     * @param {Number} [howMany=1] Number of nodes to remove.
     * @returns {Array.<module:engine/model/node~Node>} Array containing removed nodes.
     */
    _removeNodes(indexStart, howMany = 1) {
        return this._nodes.splice(indexStart, howMany);
    }
    /**
     * Converts `NodeList` instance to an array containing nodes that were inserted in the node list. Nodes
     * are also converted to their plain object representation.
     *
     * @returns {Array.<module:engine/model/node~Node>} `NodeList` instance converted to `Array`.
     */
    toJSON() {
        return this._nodes.map(node => node.toJSON());
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/text.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/text
 */

// @if CK_DEBUG_ENGINE // const { convertMapToStringifiedObject } = require( '../dev-utils/utils' );
/**
 * Model text node. Type of {@link module:engine/model/node~Node node} that contains {@link module:engine/model/text~Text#data text data}.
 *
 * **Important:** see {@link module:engine/model/node~Node} to read about restrictions using `Text` and `Node` API.
 *
 * **Note:** keep in mind that `Text` instances might indirectly got removed from model tree when model is changed.
 * This happens when {@link module:engine/model/writer~Writer model writer} is used to change model and the text node is merged with
 * another text node. Then, both text nodes are removed and a new text node is inserted into the model. Because of
 * this behavior, keeping references to `Text` is not recommended. Instead, consider creating
 * {@link module:engine/model/liveposition~LivePosition live position} placed before the text node.
 *
 * @extends module:engine/model/node~Node
 */
class model_text_Text extends model_node_Node {
    /**
     * Creates a text node.
     *
     * **Note:** Constructor of this class shouldn't be used directly in the code.
     * Use the {@link module:engine/model/writer~Writer#createText} method instead.
     *
     * @protected
     * @param {String} [data] Node's text.
     * @param {Object} [attrs] Node's attributes. See {@link module:utils/tomap~toMap} for a list of accepted values.
     */
    constructor(data, attrs) {
        super(attrs);
        /**
         * Text data contained in this text node.
         *
         * @protected
         * @type {String}
         */
        this._data = data || '';
    }
    /**
     * @inheritDoc
     */
    get offsetSize() {
        return this.data.length;
    }
    /**
     * Returns a text data contained in the node.
     *
     * @readonly
     * @type {String}
     */
    get data() {
        return this._data;
    }
    /**
     * Converts `Text` instance to plain object and returns it.
     *
     * @returns {Object} `Text` instance converted to plain object.
     */
    toJSON() {
        const json = super.toJSON();
        json.data = this.data;
        return json;
    }
    /**
     * Creates a copy of this text node and returns it. Created text node has same text data and attributes as original text node.
     *
     * @internal
     * @protected
     * @returns {module:engine/model/text~Text} `Text` instance created using given plain object.
     */
    _clone() {
        return new model_text_Text(this.data, this.getAttributes());
    }
    /**
     * Creates a `Text` instance from given plain object (i.e. parsed JSON string).
     *
     * @param {Object} json Plain object to be converted to `Text`.
     * @returns {module:engine/model/text~Text} `Text` instance created using given plain object.
     */
    static fromJSON(json) {
        return new model_text_Text(json.data, json.attributes);
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		text.is( '$text' ); // -> true
 *		text.is( 'node' ); // -> true
 *		text.is( 'model:$text' ); // -> true
 *		text.is( 'model:node' ); // -> true
 *
 *		text.is( 'view:$text' ); // -> false
 *		text.is( 'documentSelection' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * **Note:** Until version 20.0.0 this method wasn't accepting `'$text'` type. The legacy `'text'` type is still
 * accepted for backward compatibility.
 *
 * @param {String} type Type to check.
 * @returns {Boolean}
 */
model_text_Text.prototype.is = function (type) {
    return type === '$text' || type === 'model:$text' ||
        // This are legacy values kept for backward compatibility.
        type === 'text' || type === 'model:text' ||
        // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
        type === 'node' || type === 'model:node';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/textproxy.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/textproxy
 */


// @if CK_DEBUG_ENGINE // const { convertMapToStringifiedObject } = require( '../dev-utils/utils' );
/**
 * `TextProxy` represents a part of {@link module:engine/model/text~Text text node}.
 *
 * Since {@link module:engine/model/position~Position positions} can be placed between characters of a text node,
 * {@link module:engine/model/range~Range ranges} may contain only parts of text nodes. When {@link module:engine/model/range~Range#getItems
 * getting items}
 * contained in such range, we need to represent a part of that text node, since returning the whole text node would be incorrect.
 * `TextProxy` solves this issue.
 *
 * `TextProxy` has an API similar to {@link module:engine/model/text~Text Text} and allows to do most of the common tasks performed
 * on model nodes.
 *
 * **Note:** Some `TextProxy` instances may represent whole text node, not just a part of it.
 * See {@link module:engine/model/textproxy~TextProxy#isPartial}.
 *
 * **Note:** `TextProxy` is not an instance of {@link module:engine/model/node~Node node}. Keep this in mind when using it as a
 * parameter of methods.
 *
 * **Note:** `TextProxy` is a readonly interface. If you want to perform changes on model data represented by a `TextProxy`
 * use {@link module:engine/model/writer~Writer model writer API}.
 *
 * **Note:** `TextProxy` instances are created on the fly, basing on the current state of model. Because of this, it is
 * highly unrecommended to store references to `TextProxy` instances. `TextProxy` instances are not refreshed when
 * model changes, so they might get invalidated. Instead, consider creating {@link module:engine/model/liveposition~LivePosition live
 * position}.
 *
 * `TextProxy` instances are created by {@link module:engine/model/treewalker~TreeWalker model tree walker}. You should not need to create
 * an instance of this class by your own.
 */
class textproxy_TextProxy extends typecheckable_TypeCheckable {
    /**
     * Creates a text proxy.
     *
     * @protected
     * @param {module:engine/model/text~Text} textNode Text node which part is represented by this text proxy.
     * @param {Number} offsetInText Offset in {@link module:engine/model/textproxy~TextProxy#textNode text node} from which the text proxy
     * starts.
     * @param {Number} length Text proxy length, that is how many text node's characters, starting from `offsetInText` it represents.
     * @constructor
     */
    constructor(textNode, offsetInText, length) {
        super();
        /**
         * Text node which part is represented by this text proxy.
         *
         * @readonly
         * @member {module:engine/model/text~Text}
         */
        this.textNode = textNode;
        if (offsetInText < 0 || offsetInText > textNode.offsetSize) {
            /**
             * Given `offsetInText` value is incorrect.
             *
             * @error model-textproxy-wrong-offsetintext
             */
            throw new CKEditorError('model-textproxy-wrong-offsetintext', this);
        }
        if (length < 0 || offsetInText + length > textNode.offsetSize) {
            /**
             * Given `length` value is incorrect.
             *
             * @error model-textproxy-wrong-length
             */
            throw new CKEditorError('model-textproxy-wrong-length', this);
        }
        /**
         * Text data represented by this text proxy.
         *
         * @readonly
         * @member {String}
         */
        this.data = textNode.data.substring(offsetInText, offsetInText + length);
        /**
         * Offset in {@link module:engine/model/textproxy~TextProxy#textNode text node} from which the text proxy starts.
         *
         * @readonly
         * @member {Number}
         */
        this.offsetInText = offsetInText;
    }
    /**
     * Offset at which this text proxy starts in it's parent.
     *
     * @see module:engine/model/node~Node#startOffset
     * @readonly
     * @type {Number|null}
     */
    get startOffset() {
        return this.textNode.startOffset !== null ? this.textNode.startOffset + this.offsetInText : null;
    }
    /**
     * Offset size of this text proxy. Equal to the number of characters represented by the text proxy.
     *
     * @see module:engine/model/node~Node#offsetSize
     * @readonly
     * @type {Number}
     */
    get offsetSize() {
        return this.data.length;
    }
    /**
     * Offset at which this text proxy ends in it's parent.
     *
     * @see module:engine/model/node~Node#endOffset
     * @readonly
     * @type {Number|null}
     */
    get endOffset() {
        return this.startOffset !== null ? this.startOffset + this.offsetSize : null;
    }
    /**
     * Flag indicating whether `TextProxy` instance covers only part of the original {@link module:engine/model/text~Text text node}
     * (`true`) or the whole text node (`false`).
     *
     * This is `false` when text proxy starts at the very beginning of {@link module:engine/model/textproxy~TextProxy#textNode textNode}
     * ({@link module:engine/model/textproxy~TextProxy#offsetInText offsetInText} equals `0`) and text proxy sizes is equal to
     * text node size.
     *
     * @readonly
     * @type {Boolean}
     */
    get isPartial() {
        return this.offsetSize !== this.textNode.offsetSize;
    }
    /**
     * Parent of this text proxy, which is same as parent of text node represented by this text proxy.
     *
     * @readonly
     * @type {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment|null}
     */
    get parent() {
        return this.textNode.parent;
    }
    /**
     * Root of this text proxy, which is same as root of text node represented by this text proxy.
     *
     * @readonly
     * @type {module:engine/model/node~Node|module:engine/model/documentfragment~DocumentFragment}
     */
    get root() {
        return this.textNode.root;
    }
    /**
     * Gets path to this text proxy.
     *
     * @see module:engine/model/node~Node#getPath
     * @returns {Array.<Number>}
     */
    getPath() {
        const path = this.textNode.getPath();
        if (path.length > 0) {
            path[path.length - 1] += this.offsetInText;
        }
        return path;
    }
    /**
     * Returns ancestors array of this text proxy.
     *
     * @param {Object} options Options object.
     * @param {Boolean} [options.includeSelf=false] When set to `true` this text proxy will be also included in parent's array.
     * @param {Boolean} [options.parentFirst=false] When set to `true`, array will be sorted from text proxy parent to root element,
     * otherwise root element will be the first item in the array.
     * @returns {Array} Array with ancestors.
     */
    getAncestors(options = {}) {
        const ancestors = [];
        let parent = options.includeSelf ? this : this.parent;
        while (parent) {
            ancestors[options.parentFirst ? 'push' : 'unshift'](parent);
            parent = parent.parent;
        }
        return ancestors;
    }
    /**
     * Checks if this text proxy has an attribute for given key.
     *
     * @param {String} key Key of attribute to check.
     * @returns {Boolean} `true` if attribute with given key is set on text proxy, `false` otherwise.
     */
    hasAttribute(key) {
        return this.textNode.hasAttribute(key);
    }
    /**
     * Gets an attribute value for given key or `undefined` if that attribute is not set on text proxy.
     *
     * @param {String} key Key of attribute to look for.
     * @returns {*} Attribute value or `undefined`.
     */
    getAttribute(key) {
        return this.textNode.getAttribute(key);
    }
    /**
     * Returns iterator that iterates over this node's attributes. Attributes are returned as arrays containing two
     * items. First one is attribute key and second is attribute value.
     *
     * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
     *
     * @returns {Iterable.<*>}
     */
    getAttributes() {
        return this.textNode.getAttributes();
    }
    /**
     * Returns iterator that iterates over this node's attribute keys.
     *
     * @returns {Iterable.<String>}
     */
    getAttributeKeys() {
        return this.textNode.getAttributeKeys();
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		textProxy.is( '$textProxy' ); // -> true
 *		textProxy.is( 'model:$textProxy' ); // -> true
 *
 *		textProxy.is( 'view:$textProxy' ); // -> false
 *		textProxy.is( 'range' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * **Note:** Until version 20.0.0 this method wasn't accepting `'$textProxy'` type. The legacy `'textProxt'` type is still
 * accepted for backward compatibility.
 *
 * @param {String} type Type to check.
 * @returns {Boolean}
 */
textproxy_TextProxy.prototype.is = function (type) {
    return type === '$textProxy' || type === 'model:$textProxy' ||
        // This are legacy values kept for backward compatibility.
        type === 'textProxy' || type === 'model:textProxy';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/element.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/element
 */





// @if CK_DEBUG_ENGINE // const { stringifyMap, convertMapToStringifiedObject, convertMapToTags } = require( '../dev-utils/utils' );
/**
 * Model element. Type of {@link module:engine/model/node~Node node} that has a {@link module:engine/model/element~Element#name name} and
 * {@link module:engine/model/element~Element#getChildren child nodes}.
 *
 * **Important**: see {@link module:engine/model/node~Node} to read about restrictions using `Element` and `Node` API.
 *
 * @extends module:engine/model/node~Node
 */
class element_Element extends model_node_Node {
    /**
     * Creates a model element.
     *
     * **Note:** Constructor of this class shouldn't be used directly in the code.
     * Use the {@link module:engine/model/writer~Writer#createElement} method instead.
     *
     * @protected
     * @param {String} name Element's name.
     * @param {Object} [attrs] Element's attributes. See {@link module:utils/tomap~toMap} for a list of accepted values.
     * @param {module:engine/model/node~Node|Iterable.<module:engine/model/node~Node>} [children]
     * One or more nodes to be inserted as children of created element.
     */
    constructor(name, attrs, children) {
        super(attrs);
        /**
         * Element name.
         *
         * @readonly
         * @member {String} module:engine/model/element~Element#name
         */
        this.name = name;
        /**
         * List of children nodes.
         *
         * @private
         * @member {module:engine/model/nodelist~NodeList} module:engine/model/element~Element#_children
         */
        this._children = new NodeList();
        if (children) {
            this._insertChild(0, children);
        }
    }
    /**
     * Number of this element's children.
     *
     * @readonly
     * @type {Number}
     */
    get childCount() {
        return this._children.length;
    }
    /**
     * Sum of {@link module:engine/model/node~Node#offsetSize offset sizes} of all of this element's children.
     *
     * @readonly
     * @type {Number}
     */
    get maxOffset() {
        return this._children.maxOffset;
    }
    /**
     * Is `true` if there are no nodes inside this element, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isEmpty() {
        return this.childCount === 0;
    }
    /**
     * Gets the child at the given index.
     *
     * @param {Number} index Index of child.
     * @returns {module:engine/model/node~Node|null} Child node.
     */
    getChild(index) {
        return this._children.getNode(index);
    }
    /**
     * Returns an iterator that iterates over all of this element's children.
     *
     * @returns {Iterable.<module:engine/model/node~Node>}
     */
    getChildren() {
        return this._children[Symbol.iterator]();
    }
    /**
     * Returns an index of the given child node. Returns `null` if given node is not a child of this element.
     *
     * @param {module:engine/model/node~Node} node Child node to look for.
     * @returns {Number|null} Child node's index in this element.
     */
    getChildIndex(node) {
        return this._children.getNodeIndex(node);
    }
    /**
     * Returns the starting offset of given child. Starting offset is equal to the sum of
     * {@link module:engine/model/node~Node#offsetSize offset sizes} of all node's siblings that are before it. Returns `null` if
     * given node is not a child of this element.
     *
     * @param {module:engine/model/node~Node} node Child node to look for.
     * @returns {Number|null} Child node's starting offset.
     */
    getChildStartOffset(node) {
        return this._children.getNodeStartOffset(node);
    }
    /**
     * Returns index of a node that occupies given offset. If given offset is too low, returns `0`. If given offset is
     * too high, returns {@link module:engine/model/element~Element#getChildIndex index after last child}.
     *
     *		const textNode = new Text( 'foo' );
     *		const pElement = new Element( 'p' );
     *		const divElement = new Element( [ textNode, pElement ] );
     *		divElement.offsetToIndex( -1 ); // Returns 0, because offset is too low.
     *		divElement.offsetToIndex( 0 ); // Returns 0, because offset 0 is taken by `textNode` which is at index 0.
     *		divElement.offsetToIndex( 1 ); // Returns 0, because `textNode` has `offsetSize` equal to 3, so it occupies offset 1 too.
     *		divElement.offsetToIndex( 2 ); // Returns 0.
     *		divElement.offsetToIndex( 3 ); // Returns 1.
     *		divElement.offsetToIndex( 4 ); // Returns 2. There are no nodes at offset 4, so last available index is returned.
     *
     * @param {Number} offset Offset to look for.
     * @returns {Number}
     */
    offsetToIndex(offset) {
        return this._children.offsetToIndex(offset);
    }
    /**
     * Returns a descendant node by its path relative to this element.
     *
     *		// <this>a<b>c</b></this>
     *		this.getNodeByPath( [ 0 ] );     // -> "a"
     *		this.getNodeByPath( [ 1 ] );     // -> <b>
     *		this.getNodeByPath( [ 1, 0 ] );  // -> "c"
     *
     * @param {Array.<Number>} relativePath Path of the node to find, relative to this element.
     * @returns {module:engine/model/node~Node}
     */
    getNodeByPath(relativePath) {
        // eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
        let node = this;
        for (const index of relativePath) {
            node = node.getChild(node.offsetToIndex(index));
        }
        return node;
    }
    /**
     * Returns the parent element of the given name. Returns null if the element is not inside the desired parent.
     *
     * @param {String} parentName The name of the parent element to find.
     * @param {Object} [options] Options object.
     * @param {Boolean} [options.includeSelf=false] When set to `true` this node will be also included while searching.
     * @returns {module:engine/model/element~Element|null}
     */
    findAncestor(parentName, options = {}) {
        let parent = options.includeSelf ? this : this.parent;
        while (parent) {
            if (parent.name === parentName) {
                return parent;
            }
            parent = parent.parent;
        }
        return null;
    }
    /**
     * Converts `Element` instance to plain object and returns it. Takes care of converting all of this element's children.
     *
     * @returns {Object} `Element` instance converted to plain object.
     */
    toJSON() {
        const json = super.toJSON();
        json.name = this.name;
        if (this._children.length > 0) {
            json.children = [];
            for (const node of this._children) {
                json.children.push(node.toJSON());
            }
        }
        return json;
    }
    /**
     * Creates a copy of this element and returns it. Created element has the same name and attributes as the original element.
     * If clone is deep, the original element's children are also cloned. If not, then empty element is returned.
     *
     * @internal
     * @protected
     * @param {Boolean} [deep=false] If set to `true` clones element and all its children recursively. When set to `false`,
     * element will be cloned without any child.
     */
    _clone(deep = false) {
        const children = deep ? Array.from(this._children).map(node => node._clone(true)) : undefined;
        return new element_Element(this.name, this.getAttributes(), children);
    }
    /**
     * {@link module:engine/model/element~Element#_insertChild Inserts} one or more nodes at the end of this element.
     *
     * @see module:engine/model/writer~Writer#append
     * @internal
     * @protected
     * @param {String|module:engine/model/item~Item|Iterable.<String|module:engine/model/item~Item>} nodes Nodes to be inserted.
     */
    _appendChild(nodes) {
        this._insertChild(this.childCount, nodes);
    }
    /**
     * Inserts one or more nodes at the given index and sets {@link module:engine/model/node~Node#parent parent} of these nodes
     * to this element.
     *
     * @see module:engine/model/writer~Writer#insert
     * @internal
     * @protected
     * @param {Number} index Index at which nodes should be inserted.
     * @param {String|module:engine/model/item~Item|Iterable.<String|module:engine/model/item~Item>} items Items to be inserted.
     */
    _insertChild(index, items) {
        const nodes = element_normalize(items);
        for (const node of nodes) {
            // If node that is being added to this element is already inside another element, first remove it from the old parent.
            if (node.parent !== null) {
                node._remove();
            }
            node.parent = this;
        }
        this._children._insertNodes(index, nodes);
    }
    /**
     * Removes one or more nodes starting at the given index and sets
     * {@link module:engine/model/node~Node#parent parent} of these nodes to `null`.
     *
     * @see module:engine/model/writer~Writer#remove
     * @protected
     * @param {Number} index Index of the first node to remove.
     * @param {Number} [howMany=1] Number of nodes to remove.
     * @returns {Array.<module:engine/model/node~Node>} Array containing removed nodes.
     */
    _removeChildren(index, howMany = 1) {
        const nodes = this._children._removeNodes(index, howMany);
        for (const node of nodes) {
            node.parent = null;
        }
        return nodes;
    }
    /**
     * Creates an `Element` instance from given plain object (i.e. parsed JSON string).
     * Converts `Element` children to proper nodes.
     *
     * @param {Object} json Plain object to be converted to `Element`.
     * @returns {module:engine/model/element~Element} `Element` instance created using given plain object.
     */
    static fromJSON(json) {
        let children;
        if (json.children) {
            children = [];
            for (const child of json.children) {
                if (child.name) {
                    // If child has name property, it is an Element.
                    children.push(element_Element.fromJSON(child));
                }
                else {
                    // Otherwise, it is a Text node.
                    children.push(model_text_Text.fromJSON(child));
                }
            }
        }
        return new element_Element(json.name, json.attributes, children);
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		element.is( 'element' ); // -> true
 *		element.is( 'node' ); // -> true
 *		element.is( 'model:element' ); // -> true
 *		element.is( 'model:node' ); // -> true
 *
 *		element.is( 'view:element' ); // -> false
 *		element.is( 'documentSelection' ); // -> false
 *
 * Assuming that the object being checked is an element, you can also check its
 * {@link module:engine/model/element~Element#name name}:
 *
 *		element.is( 'element', 'imageBlock' ); // -> true if this is an <imageBlock> element
 *		element.is( 'element', 'imageBlock' ); // -> same as above
 *		text.is( 'element', 'imageBlock' ); -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
element_Element.prototype.is = function (type, name) {
    if (!name) {
        return type === 'element' || type === 'model:element' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'node' || type === 'model:node';
    }
    return name === this.name && (type === 'element' || type === 'model:element');
};
// Converts strings to Text and non-iterables to arrays.
//
// @param {String|module:engine/model/item~Item|Iterable.<String|module:engine/model/item~Item>}
// @returns {Iterable.<module:engine/model/node~Node>}
function element_normalize(nodes) {
    // Separate condition because string is iterable.
    if (typeof nodes == 'string') {
        return [new model_text_Text(nodes)];
    }
    if (!isIterable(nodes)) {
        nodes = [nodes];
    }
    // Array.from to enable .map() on non-arrays.
    return Array.from(nodes)
        .map(node => {
        if (typeof node == 'string') {
            return new model_text_Text(node);
        }
        if (node instanceof textproxy_TextProxy) {
            return new model_text_Text(node.data, node.getAttributes());
        }
        return node;
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/treewalker.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/treewalker
 */





/**
 * Position iterator class. It allows to iterate forward and backward over the document.
 */
class model_treewalker_TreeWalker {
    /**
     * Creates a range iterator. All parameters are optional, but you have to specify either `boundaries` or `startPosition`.
     *
     * @constructor
     * @param {Object} [options={}] Object with configuration.
     * @param {'forward'|'backward'} [options.direction='forward'] Walking direction.
     * @param {module:engine/model/range~Range|null} [options.boundaries=null] Range to define boundaries of the iterator.
     * @param {module:engine/model/position~Position} [options.startPosition] Starting position.
     * @param {Boolean} [options.singleCharacters=false] Flag indicating whether all consecutive characters with the same attributes
     * should be returned one by one as multiple {@link module:engine/model/textproxy~TextProxy} (`true`) objects or as one
     * {@link module:engine/model/textproxy~TextProxy} (`false`).
     * @param {Boolean} [options.shallow=false] Flag indicating whether iterator should enter elements or not. If the
     * iterator is shallow child nodes of any iterated node will not be returned along with `elementEnd` tag.
     * @param {Boolean} [options.ignoreElementEnd=false] Flag indicating whether iterator should ignore `elementEnd`
     * tags. If the option is true walker will not return a parent node of start position. If this option is `true`
     * each {@link module:engine/model/element~Element} will be returned once, while if the option is `false` they might be returned
     * twice: for `'elementStart'` and `'elementEnd'`.
     */
    constructor(options = {}) {
        if (!options.boundaries && !options.startPosition) {
            /**
             * Neither boundaries nor starting position of a `TreeWalker` have been defined.
             *
             * @error model-tree-walker-no-start-position
             */
            throw new CKEditorError('model-tree-walker-no-start-position', null);
        }
        const direction = options.direction || 'forward';
        if (direction != 'forward' && direction != 'backward') {
            /**
             * Only `backward` and `forward` direction allowed.
             *
             * @error model-tree-walker-unknown-direction
             */
            throw new CKEditorError('model-tree-walker-unknown-direction', options, { direction });
        }
        /**
         * Walking direction. Defaults `'forward'`.
         *
         * @readonly
         * @member {'backward'|'forward'} module:engine/model/treewalker~TreeWalker#direction
         */
        this.direction = direction;
        /**
         * Iterator boundaries.
         *
         * When the iterator is walking `'forward'` on the end of boundary or is walking `'backward'`
         * on the start of boundary, then `{ done: true }` is returned.
         *
         * If boundaries are not defined they are set before first and after last child of the root node.
         *
         * @readonly
         * @member {module:engine/model/range~Range|null} module:engine/model/treewalker~TreeWalker#boundaries
         */
        this.boundaries = options.boundaries || null;
        /**
         * Iterator position. This is always static position, even if the initial position was a
         * {@link module:engine/model/liveposition~LivePosition live position}. If start position is not defined then position depends
         * on {@link #direction}. If direction is `'forward'` position starts form the beginning, when direction
         * is `'backward'` position starts from the end.
         *
         * @readonly
         * @member {module:engine/model/position~Position} module:engine/model/treewalker~TreeWalker#position
         */
        if (options.startPosition) {
            this.position = options.startPosition.clone();
        }
        else {
            this.position = position_Position._createAt(this.boundaries[this.direction == 'backward' ? 'end' : 'start']);
        }
        // Reset position stickiness in case it was set to other value, as the stickiness is kept after cloning.
        this.position.stickiness = 'toNone';
        /**
         * Flag indicating whether all consecutive characters with the same attributes should be
         * returned as one {@link module:engine/model/textproxy~TextProxy} (`true`) or one by one (`false`).
         *
         * @readonly
         * @member {Boolean} module:engine/model/treewalker~TreeWalker#singleCharacters
         */
        this.singleCharacters = !!options.singleCharacters;
        /**
         * Flag indicating whether iterator should enter elements or not. If the iterator is shallow child nodes of any
         * iterated node will not be returned along with `elementEnd` tag.
         *
         * @readonly
         * @member {Boolean} module:engine/model/treewalker~TreeWalker#shallow
         */
        this.shallow = !!options.shallow;
        /**
         * Flag indicating whether iterator should ignore `elementEnd` tags. If the option is true walker will not
         * return a parent node of the start position. If this option is `true` each {@link module:engine/model/element~Element} will
         * be returned once, while if the option is `false` they might be returned twice:
         * for `'elementStart'` and `'elementEnd'`.
         *
         * @readonly
         * @member {Boolean} module:engine/model/treewalker~TreeWalker#ignoreElementEnd
         */
        this.ignoreElementEnd = !!options.ignoreElementEnd;
        /**
         * Start boundary cached for optimization purposes.
         *
         * @private
         * @member {module:engine/model/element~Element} module:engine/model/treewalker~TreeWalker#_boundaryStartParent
         */
        this._boundaryStartParent = this.boundaries ? this.boundaries.start.parent : null;
        /**
         * End boundary cached for optimization purposes.
         *
         * @private
         * @member {module:engine/model/element~Element} module:engine/model/treewalker~TreeWalker#_boundaryEndParent
         */
        this._boundaryEndParent = this.boundaries ? this.boundaries.end.parent : null;
        /**
         * Parent of the most recently visited node. Cached for optimization purposes.
         *
         * @private
         * @member {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment}
         * module:engine/model/treewalker~TreeWalker#_visitedParent
         */
        this._visitedParent = this.position.parent;
    }
    /**
     * Iterable interface.
     *
     * @returns {Iterable.<module:engine/model/treewalker~TreeWalkerValue>}
     */
    [Symbol.iterator]() {
        return this;
    }
    /**
     * Moves {@link #position} in the {@link #direction} skipping values as long as the callback function returns `true`.
     *
     * For example:
     *
     * 		walker.skip( value => value.type == 'text' ); // <paragraph>[]foo</paragraph> -> <paragraph>foo[]</paragraph>
     * 		walker.skip( () => true ); // Move the position to the end: <paragraph>[]foo</paragraph> -> <paragraph>foo</paragraph>[]
     * 		walker.skip( () => false ); // Do not move the position.
     *
     * @param {Function} skip Callback function. Gets {@link module:engine/model/treewalker~TreeWalkerValue} and should
     * return `true` if the value should be skipped or `false` if not.
     */
    skip(skip) {
        let done, value, prevPosition, prevVisitedParent;
        do {
            prevPosition = this.position;
            prevVisitedParent = this._visitedParent;
            ({ done, value } = this.next());
        } while (!done && skip(value));
        if (!done) {
            this.position = prevPosition;
            this._visitedParent = prevVisitedParent;
        }
    }
    /**
     * Gets the next tree walker's value.
     *
     * @returns {IteratorResult} Next tree walker's value.
     */
    next() {
        if (this.direction == 'forward') {
            return this._next();
        }
        else {
            return this._previous();
        }
    }
    /**
     * Makes a step forward in model. Moves the {@link #position} to the next position and returns the encountered value.
     *
     * @private
     * @returns {Object}
     * @returns {Boolean} return.done True if iterator is done.
     * @returns {IteratorResult} return.value Information about taken step.
     */
    _next() {
        const previousPosition = this.position;
        const position = this.position.clone();
        const parent = this._visitedParent;
        // We are at the end of the root.
        if (parent.parent === null && position.offset === parent.maxOffset) {
            return { done: true, value: undefined };
        }
        // We reached the walker boundary.
        if (parent === this._boundaryEndParent && position.offset == this.boundaries.end.offset) {
            return { done: true, value: undefined };
        }
        // Get node just after the current position.
        // Use a highly optimized version instead of checking the text node first and then getting the node after. See #6582.
        const textNodeAtPosition = getTextNodeAtPosition(position, parent);
        const node = textNodeAtPosition ? textNodeAtPosition : getNodeAfterPosition(position, parent, textNodeAtPosition);
        if (node instanceof element_Element) {
            if (!this.shallow) {
                // Manual operations on path internals for optimization purposes. Here and in the rest of the method.
                position.path.push(0);
                this._visitedParent = node;
            }
            else {
                position.offset++;
            }
            this.position = position;
            return formatReturnValue('elementStart', node, previousPosition, position, 1);
        }
        else if (node instanceof model_text_Text) {
            let charactersCount;
            if (this.singleCharacters) {
                charactersCount = 1;
            }
            else {
                let offset = node.endOffset;
                if (this._boundaryEndParent == parent && this.boundaries.end.offset < offset) {
                    offset = this.boundaries.end.offset;
                }
                charactersCount = offset - position.offset;
            }
            const offsetInTextNode = position.offset - node.startOffset;
            const item = new textproxy_TextProxy(node, offsetInTextNode, charactersCount);
            position.offset += charactersCount;
            this.position = position;
            return formatReturnValue('text', item, previousPosition, position, charactersCount);
        }
        else {
            // `node` is not set, we reached the end of current `parent`.
            position.path.pop();
            position.offset++;
            this.position = position;
            this._visitedParent = parent.parent;
            if (this.ignoreElementEnd) {
                return this._next();
            }
            else {
                return formatReturnValue('elementEnd', parent, previousPosition, position);
            }
        }
    }
    /**
     * Makes a step backward in model. Moves the {@link #position} to the previous position and returns the encountered value.
     *
     * @private
     * @returns {Object}
     * @returns {Boolean} return.done True if iterator is done.
     * @returns {IteratorResulte} return.value Information about taken step.
     */
    _previous() {
        const previousPosition = this.position;
        const position = this.position.clone();
        const parent = this._visitedParent;
        // We are at the beginning of the root.
        if (parent.parent === null && position.offset === 0) {
            return { done: true, value: undefined };
        }
        // We reached the walker boundary.
        if (parent == this._boundaryStartParent && position.offset == this.boundaries.start.offset) {
            return { done: true, value: undefined };
        }
        // Get node just before the current position.
        // Use a highly optimized version instead of checking the text node first and then getting the node before. See #6582.
        const positionParent = position.parent;
        const textNodeAtPosition = getTextNodeAtPosition(position, positionParent);
        const node = textNodeAtPosition ? textNodeAtPosition : getNodeBeforePosition(position, positionParent, textNodeAtPosition);
        if (node instanceof element_Element) {
            position.offset--;
            if (!this.shallow) {
                position.path.push(node.maxOffset);
                this.position = position;
                this._visitedParent = node;
                if (this.ignoreElementEnd) {
                    return this._previous();
                }
                else {
                    return formatReturnValue('elementEnd', node, previousPosition, position);
                }
            }
            else {
                this.position = position;
                return formatReturnValue('elementStart', node, previousPosition, position, 1);
            }
        }
        else if (node instanceof model_text_Text) {
            let charactersCount;
            if (this.singleCharacters) {
                charactersCount = 1;
            }
            else {
                let offset = node.startOffset;
                if (this._boundaryStartParent == parent && this.boundaries.start.offset > offset) {
                    offset = this.boundaries.start.offset;
                }
                charactersCount = position.offset - offset;
            }
            const offsetInTextNode = position.offset - node.startOffset;
            const item = new textproxy_TextProxy(node, offsetInTextNode - charactersCount, charactersCount);
            position.offset -= charactersCount;
            this.position = position;
            return formatReturnValue('text', item, previousPosition, position, charactersCount);
        }
        else {
            // `node` is not set, we reached the beginning of current `parent`.
            position.path.pop();
            this.position = position;
            this._visitedParent = parent.parent;
            return formatReturnValue('elementStart', parent, previousPosition, position, 1);
        }
    }
}
function formatReturnValue(type, item, previousPosition, nextPosition, length) {
    return {
        done: false,
        value: {
            type,
            item,
            previousPosition,
            nextPosition,
            length
        }
    };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/position.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/position
 */




// To check if component is loaded more than once.

/**
 * Represents a position in the model tree.
 *
 * A position is represented by its {@link module:engine/model/position~Position#root} and
 * a {@link module:engine/model/position~Position#path} in that root.
 *
 * You can create position instances via its constructor or the `createPosition*()` factory methods of
 * {@link module:engine/model/model~Model} and {@link module:engine/model/writer~Writer}.
 *
 * **Note:** Position is based on offsets, not indexes. This means that a position between two text nodes
 * `foo` and `bar` has offset `3`, not `1`. See {@link module:engine/model/position~Position#path} for more information.
 *
 * Since a position in the model is represented by a {@link module:engine/model/position~Position#root position root} and
 * {@link module:engine/model/position~Position#path position path} it is possible to create positions placed in non-existing places.
 * This requirement is important for operational transformation algorithms.
 *
 * Also, {@link module:engine/model/operation/operation~Operation operations}
 * kept in the {@link module:engine/model/document~Document#history document history}
 * are storing positions (and ranges) which were correct when those operations were applied, but may not be correct
 * after the document has changed.
 *
 * When changes are applied to the model, it may also happen that {@link module:engine/model/position~Position#parent position parent}
 * will change even if position path has not changed. Keep in mind, that if a position leads to non-existing element,
 * {@link module:engine/model/position~Position#parent} and some other properties and methods will throw errors.
 *
 * In most cases, position with wrong path is caused by an error in code, but it is sometimes needed, as described above.
 */
class position_Position extends typecheckable_TypeCheckable {
    /**
     * Creates a position.
     *
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} root Root of the position.
     * @param {Array.<Number>} path Position path. See {@link module:engine/model/position~Position#path}.
     * @param {module:engine/model/position~PositionStickiness} [stickiness='toNone'] Position stickiness.
     * See {@link module:engine/model/position~PositionStickiness}.
     */
    constructor(root, path, stickiness = 'toNone') {
        super();
        if (!root.is('element') && !root.is('documentFragment')) {
            /**
             * Position root is invalid.
             *
             * Positions can only be anchored in elements or document fragments.
             *
             * @error model-position-root-invalid
             */
            throw new CKEditorError('model-position-root-invalid', root);
        }
        if (!(path instanceof Array) || path.length === 0) {
            /**
             * Position path must be an array with at least one item.
             *
             * @error model-position-path-incorrect-format
             * @param path
             */
            throw new CKEditorError('model-position-path-incorrect-format', root, { path });
        }
        // Normalize the root and path when element (not root) is passed.
        if (root.is('rootElement')) {
            path = path.slice();
        }
        else {
            path = [...root.getPath(), ...path];
            root = root.root;
        }
        /**
         * Root of the position path.
         *
         * @readonly
         * @member {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment}
         * module:engine/model/position~Position#root
         */
        this.root = root;
        /**
         * Position of the node in the tree. **Path contains offsets, not indexes.**
         *
         * Position can be placed before, after or in a {@link module:engine/model/node~Node node} if that node has
         * {@link module:engine/model/node~Node#offsetSize} greater than `1`. Items in position path are
         * {@link module:engine/model/node~Node#startOffset starting offsets} of position ancestors, starting from direct root children,
         * down to the position offset in it's parent.
         *
         *		 ROOT
         *		  |- P            before: [ 0 ]         after: [ 1 ]
         *		  |- UL           before: [ 1 ]         after: [ 2 ]
         *		     |- LI        before: [ 1, 0 ]      after: [ 1, 1 ]
         *		     |  |- foo    before: [ 1, 0, 0 ]   after: [ 1, 0, 3 ]
         *		     |- LI        before: [ 1, 1 ]      after: [ 1, 2 ]
         *		        |- bar    before: [ 1, 1, 0 ]   after: [ 1, 1, 3 ]
         *
         * `foo` and `bar` are representing {@link module:engine/model/text~Text text nodes}. Since text nodes has offset size
         * greater than `1` you can place position offset between their start and end:
         *
         *		 ROOT
         *		  |- P
         *		  |- UL
         *		     |- LI
         *		     |  |- f^o|o  ^ has path: [ 1, 0, 1 ]   | has path: [ 1, 0, 2 ]
         *		     |- LI
         *		        |- b^a|r  ^ has path: [ 1, 1, 1 ]   | has path: [ 1, 1, 2 ]
         *
         * @readonly
         * @member {Array.<Number>} module:engine/model/position~Position#path
         */
        this.path = path;
        /**
         * Position stickiness. See {@link module:engine/model/position~PositionStickiness}.
         *
         * @member {module:engine/model/position~PositionStickiness} module:engine/model/position~Position#stickiness
         */
        this.stickiness = stickiness;
    }
    /**
     * Offset at which this position is located in its {@link module:engine/model/position~Position#parent parent}. It is equal
     * to the last item in position {@link module:engine/model/position~Position#path path}.
     *
     * @type {Number}
     */
    get offset() {
        return this.path[this.path.length - 1];
    }
    set offset(newOffset) {
        this.path[this.path.length - 1] = newOffset;
    }
    /**
     * Parent element of this position.
     *
     * Keep in mind that `parent` value is calculated when the property is accessed.
     * If {@link module:engine/model/position~Position#path position path}
     * leads to a non-existing element, `parent` property will throw error.
     *
     * Also it is a good idea to cache `parent` property if it is used frequently in an algorithm (i.e. in a long loop).
     *
     * @readonly
     * @type {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment}
     */
    get parent() {
        let parent = this.root;
        for (let i = 0; i < this.path.length - 1; i++) {
            parent = parent.getChild(parent.offsetToIndex(this.path[i]));
            if (!parent) {
                /**
                 * The position's path is incorrect. This means that a position does not point to
                 * a correct place in the tree and hence, some of its methods and getters cannot work correctly.
                 *
                 * **Note**: Unlike DOM and view positions, in the model, the
                 * {@link module:engine/model/position~Position#parent position's parent} is always an element or a document fragment.
                 * The last offset in the {@link module:engine/model/position~Position#path position's path} is the point in this element
                 * where this position points.
                 *
                 * Read more about model positions and offsets in
                 * the {@glink framework/guides/architecture/editing-engine#indexes-and-offsets Editing engine architecture guide}.
                 *
                 * @error model-position-path-incorrect
                 * @param {module:engine/model/position~Position} position The incorrect position.
                 */
                throw new CKEditorError('model-position-path-incorrect', this, { position: this });
            }
        }
        if (parent.is('$text')) {
            throw new CKEditorError('model-position-path-incorrect', this, { position: this });
        }
        return parent;
    }
    /**
     * Position {@link module:engine/model/position~Position#offset offset} converted to an index in position's parent node. It is
     * equal to the {@link module:engine/model/node~Node#index index} of a node after this position. If position is placed
     * in text node, position index is equal to the index of that text node.
     *
     * @readonly
     * @type {Number}
     */
    get index() {
        return this.parent.offsetToIndex(this.offset);
    }
    /**
     * Returns {@link module:engine/model/text~Text text node} instance in which this position is placed or `null` if this
     * position is not in a text node.
     *
     * @readonly
     * @type {module:engine/model/text~Text|null}
     */
    get textNode() {
        return getTextNodeAtPosition(this, this.parent);
    }
    /**
     * Node directly after this position or `null` if this position is in text node.
     *
     * @readonly
     * @type {module:engine/model/node~Node|null}
     */
    get nodeAfter() {
        // Cache the parent and reuse for performance reasons. See #6579 and #6582.
        const parent = this.parent;
        return getNodeAfterPosition(this, parent, getTextNodeAtPosition(this, parent));
    }
    /**
     * Node directly before this position or `null` if this position is in text node.
     *
     * @readonly
     * @type {module:engine/model/node~Node|null}
     */
    get nodeBefore() {
        // Cache the parent and reuse for performance reasons. See #6579 and #6582.
        const parent = this.parent;
        return getNodeBeforePosition(this, parent, getTextNodeAtPosition(this, parent));
    }
    /**
     * Is `true` if position is at the beginning of its {@link module:engine/model/position~Position#parent parent}, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isAtStart() {
        return this.offset === 0;
    }
    /**
     * Is `true` if position is at the end of its {@link module:engine/model/position~Position#parent parent}, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isAtEnd() {
        return this.offset == this.parent.maxOffset;
    }
    /**
     * Checks whether this position is before or after given position.
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @param {module:engine/model/position~Position} otherPosition Position to compare with.
     * @returns {module:engine/model/position~PositionRelation}
     */
    compareWith(otherPosition) {
        if (this.root != otherPosition.root) {
            return 'different';
        }
        const result = compareArrays(this.path, otherPosition.path);
        switch (result) {
            case 'same':
                return 'same';
            case 'prefix':
                return 'before';
            case 'extension':
                return 'after';
            default:
                return this.path[result] < otherPosition.path[result] ? 'before' : 'after';
        }
    }
    /**
     * Gets the farthest position which matches the callback using
     * {@link module:engine/model/treewalker~TreeWalker TreeWalker}.
     *
     * For example:
     *
     * 		getLastMatchingPosition( value => value.type == 'text' );
     * 		// <paragraph>[]foo</paragraph> -> <paragraph>foo[]</paragraph>
     *
     * 		getLastMatchingPosition( value => value.type == 'text', { direction: 'backward' } );
     * 		// <paragraph>foo[]</paragraph> -> <paragraph>[]foo</paragraph>
     *
     * 		getLastMatchingPosition( value => false );
     * 		// Do not move the position.
     *
     * @param {Function} skip Callback function. Gets {@link module:engine/model/treewalker~TreeWalkerValue} and should
     * return `true` if the value should be skipped or `false` if not.
     * @param {Object} options Object with configuration options. See {@link module:engine/model/treewalker~TreeWalker}.
     *
     * @returns {module:engine/model/position~Position} The position after the last item which matches the `skip` callback test.
     */
    getLastMatchingPosition(skip, options = {}) {
        options.startPosition = this;
        const treeWalker = new model_treewalker_TreeWalker(options);
        treeWalker.skip(skip);
        return treeWalker.position;
    }
    /**
     * Returns a path to this position's parent. Parent path is equal to position {@link module:engine/model/position~Position#path path}
     * but without the last item.
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @returns {Array.<Number>} Path to the parent.
     */
    getParentPath() {
        return this.path.slice(0, -1);
    }
    /**
     * Returns ancestors array of this position, that is this position's parent and its ancestors.
     *
     * @returns {Array.<module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment>} Array with ancestors.
     */
    getAncestors() {
        const parent = this.parent;
        if (parent.is('documentFragment')) {
            return [parent];
        }
        else {
            return parent.getAncestors({ includeSelf: true });
        }
    }
    /**
     * Returns the parent element of the given name. Returns null if the position is not inside the desired parent.
     *
     * @param {String} parentName The name of the parent element to find.
     * @returns {module:engine/model/element~Element|null}
     */
    findAncestor(parentName) {
        const parent = this.parent;
        if (parent.is('element')) {
            return parent.findAncestor(parentName, { includeSelf: true });
        }
        return null;
    }
    /**
     * Returns the slice of two position {@link #path paths} which is identical. The {@link #root roots}
     * of these two paths must be identical.
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @param {module:engine/model/position~Position} position The second position.
     * @returns {Array.<Number>} The common path.
     */
    getCommonPath(position) {
        if (this.root != position.root) {
            return [];
        }
        // We find on which tree-level start and end have the lowest common ancestor
        const cmp = compareArrays(this.path, position.path);
        // If comparison returned string it means that arrays are same.
        const diffAt = (typeof cmp == 'string') ? Math.min(this.path.length, position.path.length) : cmp;
        return this.path.slice(0, diffAt);
    }
    /**
     * Returns an {@link module:engine/model/element~Element} or {@link module:engine/model/documentfragment~DocumentFragment}
     * which is a common ancestor of both positions. The {@link #root roots} of these two positions must be identical.
     *
     * @param {module:engine/model/position~Position} position The second position.
     * @returns {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment|null}
     */
    getCommonAncestor(position) {
        const ancestorsA = this.getAncestors();
        const ancestorsB = position.getAncestors();
        let i = 0;
        while (ancestorsA[i] == ancestorsB[i] && ancestorsA[i]) {
            i++;
        }
        return i === 0 ? null : ancestorsA[i - 1];
    }
    /**
     * Returns a new instance of `Position`, that has same {@link #parent parent} but it's offset
     * is shifted by `shift` value (can be a negative value).
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @param {Number} shift Offset shift. Can be a negative value.
     * @returns {module:engine/model/position~Position} Shifted position.
     */
    getShiftedBy(shift) {
        const shifted = this.clone();
        const offset = shifted.offset + shift;
        shifted.offset = offset < 0 ? 0 : offset;
        return shifted;
    }
    /**
     * Checks whether this position is after given position.
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @see module:engine/model/position~Position#isBefore
     * @param {module:engine/model/position~Position} otherPosition Position to compare with.
     * @returns {Boolean} True if this position is after given position.
     */
    isAfter(otherPosition) {
        return this.compareWith(otherPosition) == 'after';
    }
    /**
     * Checks whether this position is before given position.
     *
     * **Note:** watch out when using negation of the value returned by this method, because the negation will also
     * be `true` if positions are in different roots and you might not expect this. You should probably use
     * `a.isAfter( b ) || a.isEqual( b )` or `!a.isBefore( p ) && a.root == b.root` in most scenarios. If your
     * condition uses multiple `isAfter` and `isBefore` checks, build them so they do not use negated values, i.e.:
     *
     *		if ( a.isBefore( b ) && c.isAfter( d ) ) {
     *			// do A.
     *		} else {
     *			// do B.
     *		}
     *
     * or, if you have only one if-branch:
     *
     *		if ( !( a.isBefore( b ) && c.isAfter( d ) ) {
     *			// do B.
     *		}
     *
     * rather than:
     *
     *		if ( !a.isBefore( b ) || && !c.isAfter( d ) ) {
     *			// do B.
     *		} else {
     *			// do A.
     *		}
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @param {module:engine/model/position~Position} otherPosition Position to compare with.
     * @returns {Boolean} True if this position is before given position.
     */
    isBefore(otherPosition) {
        return this.compareWith(otherPosition) == 'before';
    }
    /**
     * Checks whether this position is equal to given position.
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @param {module:engine/model/position~Position} otherPosition Position to compare with.
     * @returns {Boolean} True if positions are same.
     */
    isEqual(otherPosition) {
        return this.compareWith(otherPosition) == 'same';
    }
    /**
     * Checks whether this position is touching given position. Positions touch when there are no text nodes
     * or empty nodes in a range between them. Technically, those positions are not equal but in many cases
     * they are very similar or even indistinguishable.
     *
     * @param {module:engine/model/position~Position} otherPosition Position to compare with.
     * @returns {Boolean} True if positions touch.
     */
    isTouching(otherPosition) {
        if (this.root !== otherPosition.root) {
            return false;
        }
        const commonLevel = Math.min(this.path.length, otherPosition.path.length);
        for (let level = 0; level < commonLevel; level++) {
            const diff = this.path[level] - otherPosition.path[level];
            // Positions are spread by a node, so they are not touching.
            if (diff < -1 || diff > 1) {
                return false;
            }
            else if (diff === 1) {
                // `otherPosition` is on the left.
                // `this` is on the right.
                return checkTouchingBranch(otherPosition, this, level);
            }
            else if (diff === -1) {
                // `this` is on the left.
                // `otherPosition` is on the right.
                return checkTouchingBranch(this, otherPosition, level);
            }
            // `diff === 0`.
            // Positions are inside the same element on this level, compare deeper.
        }
        // If we ended up here, it means that positions paths have the same beginning.
        // If the paths have the same length, then it means that they are identical, so the positions are same.
        if (this.path.length === otherPosition.path.length) {
            return true;
        }
        // If positions have different length of paths, then the common part is the same.
        // In this case, the "shorter" position is on the left, the "longer" position is on the right.
        //
        // If the positions are touching, the "longer" position must have only zeroes. For example:
        // [ 1, 2 ] vs [ 1, 2, 0 ]
        // [ 1, 2 ] vs [ 1, 2, 0, 0, 0 ]
        else if (this.path.length > otherPosition.path.length) {
            return checkOnlyZeroes(this.path, commonLevel);
        }
        else {
            return checkOnlyZeroes(otherPosition.path, commonLevel);
        }
    }
    /**
     * Checks if two positions are in the same parent.
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @param {module:engine/model/position~Position} position Position to compare with.
     * @returns {Boolean} `true` if positions have the same parent, `false` otherwise.
     */
    hasSameParentAs(position) {
        if (this.root !== position.root) {
            return false;
        }
        const thisParentPath = this.getParentPath();
        const posParentPath = position.getParentPath();
        return compareArrays(thisParentPath, posParentPath) == 'same';
    }
    /**
     * Returns a copy of this position that is transformed by given `operation`.
     *
     * The new position's parameters are updated accordingly to the effect of the `operation`.
     *
     * For example, if `n` nodes are inserted before the position, the returned position {@link ~Position#offset} will be
     * increased by `n`. If the position was in a merged element, it will be accordingly moved to the new element, etc.
     *
     * This method is safe to use it on non-existing positions (for example during operational transformation).
     *
     * @param {module:engine/model/operation/operation~Operation} operation Operation to transform by.
     * @returns {module:engine/model/position~Position} Transformed position.
     */
    getTransformedByOperation(operation) {
        let result;
        switch (operation.type) {
            case 'insert':
                result = this._getTransformedByInsertOperation(operation);
                break;
            case 'move':
            case 'remove':
            case 'reinsert':
                result = this._getTransformedByMoveOperation(operation);
                break;
            case 'split':
                result = this._getTransformedBySplitOperation(operation);
                break;
            case 'merge':
                result = this._getTransformedByMergeOperation(operation);
                break;
            default:
                result = position_Position._createAt(this);
                break;
        }
        return result;
    }
    /**
     * Returns a copy of this position transformed by an insert operation.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/insertoperation~InsertOperation} operation
     * @returns {module:engine/model/position~Position}
     */
    _getTransformedByInsertOperation(operation) {
        return this._getTransformedByInsertion(operation.position, operation.howMany);
    }
    /**
     * Returns a copy of this position transformed by a move operation.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/moveoperation~MoveOperation} operation
     * @returns {module:engine/model/position~Position}
     */
    _getTransformedByMoveOperation(operation) {
        return this._getTransformedByMove(operation.sourcePosition, operation.targetPosition, operation.howMany);
    }
    /**
     * Returns a copy of this position transformed by a split operation.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/splitoperation~SplitOperation} operation
     * @returns {module:engine/model/position~Position}
     */
    _getTransformedBySplitOperation(operation) {
        const movedRange = operation.movedRange;
        const isContained = movedRange.containsPosition(this) ||
            (movedRange.start.isEqual(this) && this.stickiness == 'toNext');
        if (isContained) {
            return this._getCombined(operation.splitPosition, operation.moveTargetPosition);
        }
        else {
            if (operation.graveyardPosition) {
                return this._getTransformedByMove(operation.graveyardPosition, operation.insertionPosition, 1);
            }
            else {
                return this._getTransformedByInsertion(operation.insertionPosition, 1);
            }
        }
    }
    /**
     * Returns a copy of this position transformed by merge operation.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/mergeoperation~MergeOperation} operation
     * @returns {module:engine/model/position~Position}
     */
    _getTransformedByMergeOperation(operation) {
        const movedRange = operation.movedRange;
        const isContained = movedRange.containsPosition(this) || movedRange.start.isEqual(this);
        let pos;
        if (isContained) {
            pos = this._getCombined(operation.sourcePosition, operation.targetPosition);
            if (operation.sourcePosition.isBefore(operation.targetPosition)) {
                // Above happens during OT when the merged element is moved before the merged-to element.
                pos = pos._getTransformedByDeletion(operation.deletionPosition, 1);
            }
        }
        else if (this.isEqual(operation.deletionPosition)) {
            pos = position_Position._createAt(operation.deletionPosition);
        }
        else {
            pos = this._getTransformedByMove(operation.deletionPosition, operation.graveyardPosition, 1);
        }
        return pos;
    }
    /**
     * Returns a copy of this position that is updated by removing `howMany` nodes starting from `deletePosition`.
     * It may happen that this position is in a removed node. If that is the case, `null` is returned instead.
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} deletePosition Position before the first removed node.
     * @param {Number} howMany How many nodes are removed.
     * @returns {module:engine/model/position~Position|null} Transformed position or `null`.
     */
    _getTransformedByDeletion(deletePosition, howMany) {
        const transformed = position_Position._createAt(this);
        // This position can't be affected if deletion was in a different root.
        if (this.root != deletePosition.root) {
            return transformed;
        }
        if (compareArrays(deletePosition.getParentPath(), this.getParentPath()) == 'same') {
            // If nodes are removed from the node that is pointed by this position...
            if (deletePosition.offset < this.offset) {
                // And are removed from before an offset of that position...
                if (deletePosition.offset + howMany > this.offset) {
                    // Position is in removed range, it's no longer in the tree.
                    return null;
                }
                else {
                    // Decrement the offset accordingly.
                    transformed.offset -= howMany;
                }
            }
        }
        else if (compareArrays(deletePosition.getParentPath(), this.getParentPath()) == 'prefix') {
            // If nodes are removed from a node that is on a path to this position...
            const i = deletePosition.path.length - 1;
            if (deletePosition.offset <= this.path[i]) {
                // And are removed from before next node of that path...
                if (deletePosition.offset + howMany > this.path[i]) {
                    // If the next node of that path is removed return null
                    // because the node containing this position got removed.
                    return null;
                }
                else {
                    // Otherwise, decrement index on that path.
                    transformed.path[i] -= howMany;
                }
            }
        }
        return transformed;
    }
    /**
     * Returns a copy of this position that is updated by inserting `howMany` nodes at `insertPosition`.
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} insertPosition Position where nodes are inserted.
     * @param {Number} howMany How many nodes are inserted.
     * @returns {module:engine/model/position~Position} Transformed position.
     */
    _getTransformedByInsertion(insertPosition, howMany) {
        const transformed = position_Position._createAt(this);
        // This position can't be affected if insertion was in a different root.
        if (this.root != insertPosition.root) {
            return transformed;
        }
        if (compareArrays(insertPosition.getParentPath(), this.getParentPath()) == 'same') {
            // If nodes are inserted in the node that is pointed by this position...
            if (insertPosition.offset < this.offset || (insertPosition.offset == this.offset && this.stickiness != 'toPrevious')) {
                // And are inserted before an offset of that position...
                // "Push" this positions offset.
                transformed.offset += howMany;
            }
        }
        else if (compareArrays(insertPosition.getParentPath(), this.getParentPath()) == 'prefix') {
            // If nodes are inserted in a node that is on a path to this position...
            const i = insertPosition.path.length - 1;
            if (insertPosition.offset <= this.path[i]) {
                // And are inserted before next node of that path...
                // "Push" the index on that path.
                transformed.path[i] += howMany;
            }
        }
        return transformed;
    }
    /**
     * Returns a copy of this position that is updated by moving `howMany` nodes from `sourcePosition` to `targetPosition`.
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} sourcePosition Position before the first element to move.
     * @param {module:engine/model/position~Position} targetPosition Position where moved elements will be inserted.
     * @param {Number} howMany How many consecutive nodes to move, starting from `sourcePosition`.
     * @returns {module:engine/model/position~Position} Transformed position.
     */
    _getTransformedByMove(sourcePosition, targetPosition, howMany) {
        // Update target position, as it could be affected by nodes removal.
        targetPosition = targetPosition._getTransformedByDeletion(sourcePosition, howMany);
        if (sourcePosition.isEqual(targetPosition)) {
            // If `targetPosition` is equal to `sourcePosition` this isn't really any move. Just return position as it is.
            return position_Position._createAt(this);
        }
        // Moving a range removes nodes from their original position. We acknowledge this by proper transformation.
        const transformed = this._getTransformedByDeletion(sourcePosition, howMany);
        const isMoved = transformed === null ||
            (sourcePosition.isEqual(this) && this.stickiness == 'toNext') ||
            (sourcePosition.getShiftedBy(howMany).isEqual(this) && this.stickiness == 'toPrevious');
        if (isMoved) {
            // This position is inside moved range (or sticks to it).
            // In this case, we calculate a combination of this position, move source position and target position.
            return this._getCombined(sourcePosition, targetPosition);
        }
        else {
            // This position is not inside a removed range.
            //
            // In next step, we simply reflect inserting `howMany` nodes, which might further affect the position.
            return transformed._getTransformedByInsertion(targetPosition, howMany);
        }
    }
    /**
     * Returns a new position that is a combination of this position and given positions.
     *
     * The combined position is a copy of this position transformed by moving a range starting at `source` position
     * to the `target` position. It is expected that this position is inside the moved range.
     *
     * Example:
     *
     *		let original = model.createPositionFromPath( root, [ 2, 3, 1 ] );
     *		let source = model.createPositionFromPath( root, [ 2, 2 ] );
     *		let target = model.createPositionFromPath( otherRoot, [ 1, 1, 3 ] );
     *		original._getCombined( source, target ); // path is [ 1, 1, 4, 1 ], root is `otherRoot`
     *
     * Explanation:
     *
     * We have a position `[ 2, 3, 1 ]` and move some nodes from `[ 2, 2 ]` to `[ 1, 1, 3 ]`. The original position
     * was inside moved nodes and now should point to the new place. The moved nodes will be after
     * positions `[ 1, 1, 3 ]`, `[ 1, 1, 4 ]`, `[ 1, 1, 5 ]`. Since our position was in the second moved node,
     * the transformed position will be in a sub-tree of a node at `[ 1, 1, 4 ]`. Looking at original path, we
     * took care of `[ 2, 3 ]` part of it. Now we have to add the rest of the original path to the transformed path.
     * Finally, the transformed position will point to `[ 1, 1, 4, 1 ]`.
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} source Beginning of the moved range.
     * @param {module:engine/model/position~Position} target Position where the range is moved.
     * @returns {module:engine/model/position~Position} Combined position.
     */
    _getCombined(source, target) {
        const i = source.path.length - 1;
        // The first part of a path to combined position is a path to the place where nodes were moved.
        const combined = position_Position._createAt(target);
        combined.stickiness = this.stickiness;
        // Then we have to update the rest of the path.
        // Fix the offset because this position might be after `from` position and we have to reflect that.
        combined.offset = combined.offset + this.path[i] - source.offset;
        // Then, add the rest of the path.
        // If this position is at the same level as `from` position nothing will get added.
        combined.path = [...combined.path, ...this.path.slice(i + 1)];
        return combined;
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        return {
            root: this.root.toJSON(),
            path: Array.from(this.path),
            stickiness: this.stickiness
        };
    }
    /**
     * Returns a new position that is equal to current position.
     *
     * @returns {module:engine/model/position~Position}
     */
    clone() {
        return new this.constructor(this.root, this.path, this.stickiness);
    }
    /**
     * Creates position at the given location. The location can be specified as:
     *
     * * a {@link module:engine/model/position~Position position},
     * * parent element and offset (offset defaults to `0`),
     * * parent element and `'end'` (sets position at the end of that element),
     * * {@link module:engine/model/item~Item model item} and `'before'` or `'after'` (sets position before or after given model item).
     *
     * This method is a shortcut to other factory methods such as:
     *
     * * {@link module:engine/model/position~Position._createBefore},
     * * {@link module:engine/model/position~Position._createAfter}.
     *
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when the
     * first parameter is a {@link module:engine/model/item~Item model item}.
     * @param {module:engine/model/position~PositionStickiness} [stickiness='toNone'] Position stickiness. Used only when the
     * first parameter is a {@link module:engine/model/item~Item model item}.
     * @protected
     * @internal
     */
    static _createAt(itemOrPosition, offset, stickiness = 'toNone') {
        if (itemOrPosition instanceof position_Position) {
            return new position_Position(itemOrPosition.root, itemOrPosition.path, itemOrPosition.stickiness);
        }
        else {
            const node = itemOrPosition;
            if (offset == 'end') {
                offset = node.maxOffset;
            }
            else if (offset == 'before') {
                return this._createBefore(node, stickiness);
            }
            else if (offset == 'after') {
                return this._createAfter(node, stickiness);
            }
            else if (offset !== 0 && !offset) {
                /**
                 * {@link module:engine/model/model~Model#createPositionAt `Model#createPositionAt()`}
                 * requires the offset to be specified when the first parameter is a model item.
                 *
                 * @error model-createpositionat-offset-required
                 */
                throw new CKEditorError('model-createpositionat-offset-required', [this, itemOrPosition]);
            }
            if (!node.is('element') && !node.is('documentFragment')) {
                /**
                 * Position parent have to be a model element or model document fragment.
                 *
                 * @error model-position-parent-incorrect
                 */
                throw new CKEditorError('model-position-parent-incorrect', [this, itemOrPosition]);
            }
            const path = node.getPath();
            path.push(offset);
            return new this(node.root, path, stickiness);
        }
    }
    /**
     * Creates a new position, after given {@link module:engine/model/item~Item model item}.
     *
     * @param {module:engine/model/item~Item} item Item after which the position should be placed.
     * @param {module:engine/model/position~PositionStickiness} [stickiness='toNone'] Position stickiness.
     * @returns {module:engine/model/position~Position}
     * @protected
     * @internal
     */
    static _createAfter(item, stickiness) {
        if (!item.parent) {
            /**
             * You can not make a position after a root element.
             *
             * @error model-position-after-root
             * @param {module:engine/model/item~Item} root
             */
            throw new CKEditorError('model-position-after-root', [this, item], { root: item });
        }
        return this._createAt(item.parent, item.endOffset, stickiness);
    }
    /**
     * Creates a new position, before the given {@link module:engine/model/item~Item model item}.
     *
     * @param {module:engine/model/item~Item} item Item before which the position should be placed.
     * @param {module:engine/model/position~PositionStickiness} [stickiness='toNone'] Position stickiness.
     * @returns {module:engine/model/position~Position}
     * @protected
     * @internal
     */
    static _createBefore(item, stickiness) {
        if (!item.parent) {
            /**
             * You can not make a position before a root element.
             *
             * @error model-position-before-root
             * @param {module:engine/model/item~Item} root
             */
            throw new CKEditorError('model-position-before-root', item, { root: item });
        }
        return this._createAt(item.parent, item.startOffset, stickiness);
    }
    /**
     * Creates a `Position` instance from given plain object (i.e. parsed JSON string).
     *
     * @param {Object} json Plain object to be converted to `Position`.
     * @param {module:engine/model/document~Document} doc Document object that will be position owner.
     * @returns {module:engine/model/position~Position} `Position` instance created using given plain object.
     */
    static fromJSON(json, doc) {
        if (json.root === '$graveyard') {
            const pos = new position_Position(doc.graveyard, json.path);
            pos.stickiness = json.stickiness;
            return pos;
        }
        if (!doc.getRoot(json.root)) {
            /**
             * Cannot create position for document. Root with specified name does not exist.
             *
             * @error model-position-fromjson-no-root
             * @param {String} rootName
             */
            throw new CKEditorError('model-position-fromjson-no-root', doc, { rootName: json.root });
        }
        return new position_Position(doc.getRoot(json.root), json.path, json.stickiness);
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		position.is( 'position' ); // -> true
 *		position.is( 'model:position' ); // -> true
 *
 *		position.is( 'view:position' ); // -> false
 *		position.is( 'documentSelection' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
position_Position.prototype.is = function (type) {
    return type === 'position' || type === 'model:position';
};
/**
 * Returns a text node at the given position.
 *
 * This is a helper function optimized to reuse the position parent instance for performance reasons.
 *
 * Normally, you should use {@link module:engine/model/position~Position#textNode `Position#textNode`}.
 * If you start hitting performance issues with {@link module:engine/model/position~Position#parent `Position#parent`}
 * check if your algorithm does not access it multiple times (which can happen directly or indirectly via other position properties).
 *
 * See https://github.com/ckeditor/ckeditor5/issues/6579.
 *
 * See also:
 *
 * * {@link module:engine/model/position~getNodeAfterPosition}
 * * {@link module:engine/model/position~getNodeBeforePosition}
 *
 * @param {module:engine/model/position~Position} position
 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} positionParent The parent of the
 * given position.
 * @returns {module:engine/model/text~Text|null}
 */
function getTextNodeAtPosition(position, positionParent) {
    const node = positionParent.getChild(positionParent.offsetToIndex(position.offset));
    if (node && node.is('$text') && node.startOffset < position.offset) {
        return node;
    }
    return null;
}
/**
 * Returns the node after the given position.
 *
 * This is a helper function optimized to reuse the position parent instance and the calculation of the text node at the
 * specific position for performance reasons.
 *
 * Normally, you should use {@link module:engine/model/position~Position#nodeAfter `Position#nodeAfter`}.
 * If you start hitting performance issues with {@link module:engine/model/position~Position#parent `Position#parent`} and/or
 * {@link module:engine/model/position~Position#textNode `Position#textNode`}
 * check if your algorithm does not access those properties multiple times
 * (which can happen directly or indirectly via other position properties).
 *
 * See https://github.com/ckeditor/ckeditor5/issues/6579 and https://github.com/ckeditor/ckeditor5/issues/6582.
 *
 * See also:
 *
 * * {@link module:engine/model/position~getTextNodeAtPosition}
 * * {@link module:engine/model/position~getNodeBeforePosition}
 *
 * @param {module:engine/model/position~Position} position
 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} positionParent The parent of the
 * given position.
 * @param {module:engine/model/text~Text|null} textNode Text node at the given position.
 * @returns {module:engine/model/node~Node|null}
 */
function getNodeAfterPosition(position, positionParent, textNode) {
    if (textNode !== null) {
        return null;
    }
    return positionParent.getChild(positionParent.offsetToIndex(position.offset));
}
/**
 * Returns the node before the given position.
 *
 * Refer to {@link module:engine/model/position~getNodeBeforePosition} for documentation on when to use this util method.
 *
 * See also:
 *
 * * {@link module:engine/model/position~getTextNodeAtPosition}
 * * {@link module:engine/model/position~getNodeAfterPosition}
 *
 * @param {module:engine/model/position~Position} position
 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} positionParent The parent of the
 * given position.
 * @param {module:engine/model/text~Text|null} textNode Text node at the given position.
 * @returns {module:engine/model/node~Node|null}
 */
function getNodeBeforePosition(position, positionParent, textNode) {
    if (textNode !== null) {
        return null;
    }
    return positionParent.getChild(positionParent.offsetToIndex(position.offset) - 1);
}
// This is a helper function for `Position#isTouching()`.
//
// It checks whether to given positions are touching, considering that they have the same root and paths
// until given level, and at given level they differ by 1 (so they are branching at `level` point).
//
// The exact requirements for touching positions are described in `Position#isTouching()` and also
// in the body of this function.
//
// @param {module:engine/model/position~Position} left Position "on the left" (it is before `right`).
// @param {module:engine/model/position~Position} right Position "on the right" (it is after `left`).
// @param {Number} level Level on which the positions are different.
// @returns {Boolean}
function checkTouchingBranch(left, right, level) {
    if (level + 1 === left.path.length) {
        // Left position does not have any more entries after the point where the positions differ.
        // [ 2 ] vs [ 3 ]
        // [ 2 ] vs [ 3, 0, 0 ]
        // The positions are spread by node at [ 2 ].
        return false;
    }
    if (!checkOnlyZeroes(right.path, level + 1)) {
        // Right position does not have only zeroes, so we have situation like:
        // [ 2, maxOffset ] vs [ 3, 1 ]
        // [ 2, maxOffset ] vs [ 3, 1, 0, 0 ]
        // The positions are spread by node at [ 3, 0 ].
        return false;
    }
    if (!checkOnlyMaxOffset(left, level + 1)) {
        // Left position does not have only max offsets, so we have situation like:
        // [ 2, 4 ] vs [ 3 ]
        // [ 2, 4 ] vs [ 3, 0, 0 ]
        // The positions are spread by node at [ 2, 5 ].
        return false;
    }
    // Left position has only max offsets and right position has only zeroes or nothing.
    // [ 2, maxOffset ] vs [ 3 ]
    // [ 2, maxOffset, maxOffset ] vs [ 3, 0 ]
    // There are not elements between positions. The positions are touching.
    return true;
}
// Checks whether for given array, starting from given index until the end of the array, all items are `0`s.
//
// This is a helper function for `Position#isTouching()`.
//
// @private
// @param {Array.<Number>} arr Array to check.
// @param {Number} idx Index to start checking from.
// @returns {Boolean}
function checkOnlyZeroes(arr, idx) {
    while (idx < arr.length) {
        if (arr[idx] !== 0) {
            return false;
        }
        idx++;
    }
    return true;
}
// Checks whether for given position, starting from given path level, whether the position is at the end of
// its parent and whether each element on the path to the position is also at at the end of its parent.
//
// This is a helper function for `Position#isTouching()`.
//
// @private
// @param {module:engine/model/position~Position} pos Position to check.
// @param {Number} level Level to start checking from.
// @returns {Boolean}
function checkOnlyMaxOffset(pos, level) {
    let parent = pos.parent;
    let idx = pos.path.length - 1;
    let add = 0;
    while (idx >= level) {
        if (pos.path[idx] + add !== parent.maxOffset) {
            return false;
        }
        // After the first check, we "go up", and check whether the position's parent-parent is the last element.
        // However, we need to add 1 to the value in the path to "simulate" moving the path after the parent.
        // It happens just once.
        add = 1;
        idx--;
        parent = parent.parent;
    }
    return true;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/range.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/range
 */





/**
 * Represents a range in the model tree.
 *
 * A range is defined by its {@link module:engine/model/range~Range#start} and {@link module:engine/model/range~Range#end}
 * positions.
 *
 * You can create range instances via its constructor or the `createRange*()` factory methods of
 * {@link module:engine/model/model~Model} and {@link module:engine/model/writer~Writer}.
 */
class range_Range extends typecheckable_TypeCheckable {
    /**
     * Creates a range spanning from `start` position to `end` position.
     *
     * @param {module:engine/model/position~Position} start The start position.
     * @param {module:engine/model/position~Position|null} [end] The end position. If not set,
     * the range will be collapsed at the `start` position.
     */
    constructor(start, end) {
        super();
        /**
         * Start position.
         *
         * @readonly
         * @member {module:engine/model/position~Position}
         */
        this.start = position_Position._createAt(start);
        /**
         * End position.
         *
         * @readonly
         * @member {module:engine/model/position~Position}
         */
        this.end = end ? position_Position._createAt(end) : position_Position._createAt(start);
        // If the range is collapsed, treat in a similar way as a position and set its boundaries stickiness to 'toNone'.
        // In other case, make the boundaries stick to the "inside" of the range.
        this.start.stickiness = this.isCollapsed ? 'toNone' : 'toNext';
        this.end.stickiness = this.isCollapsed ? 'toNone' : 'toPrevious';
    }
    /**
     * Iterable interface.
     *
     * Iterates over all {@link module:engine/model/item~Item items} that are in this range and returns
     * them together with additional information like length or {@link module:engine/model/position~Position positions},
     * grouped as {@link module:engine/model/treewalker~TreeWalkerValue}.
     * It iterates over all {@link module:engine/model/textproxy~TextProxy text contents} that are inside the range
     * and all the {@link module:engine/model/element~Element}s that are entered into when iterating over this range.
     *
     * This iterator uses {@link module:engine/model/treewalker~TreeWalker} with `boundaries` set to this range
     * and `ignoreElementEnd` option set to `true`.
     *
     * @returns {Iterator.<module:engine/model/treewalker~TreeWalkerValue>}
     */
    *[Symbol.iterator]() {
        yield* new model_treewalker_TreeWalker({ boundaries: this, ignoreElementEnd: true });
    }
    /**
     * Returns whether the range is collapsed, that is if {@link #start} and
     * {@link #end} positions are equal.
     *
     * @type {Boolean}
     */
    get isCollapsed() {
        return this.start.isEqual(this.end);
    }
    /**
     * Returns whether this range is flat, that is if {@link #start} position and
     * {@link #end} position are in the same {@link module:engine/model/position~Position#parent}.
     *
     * @type {Boolean}
     */
    get isFlat() {
        const startParentPath = this.start.getParentPath();
        const endParentPath = this.end.getParentPath();
        return compareArrays(startParentPath, endParentPath) == 'same';
    }
    /**
     * Range root element.
     *
     * @type {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment}
     */
    get root() {
        return this.start.root;
    }
    /**
     * Checks whether this range contains given {@link module:engine/model/position~Position position}.
     *
     * @param {module:engine/model/position~Position} position Position to check.
     * @returns {Boolean} `true` if given {@link module:engine/model/position~Position position} is contained
     * in this range,`false` otherwise.
     */
    containsPosition(position) {
        return position.isAfter(this.start) && position.isBefore(this.end);
    }
    /**
     * Checks whether this range contains given {@link ~Range range}.
     *
     * @param {module:engine/model/range~Range} otherRange Range to check.
     * @param {Boolean} [loose=false] Whether the check is loose or strict. If the check is strict (`false`), compared range cannot
     * start or end at the same position as this range boundaries. If the check is loose (`true`), compared range can start, end or
     * even be equal to this range. Note that collapsed ranges are always compared in strict mode.
     * @returns {Boolean} `true` if given {@link ~Range range} boundaries are contained by this range, `false` otherwise.
     */
    containsRange(otherRange, loose = false) {
        if (otherRange.isCollapsed) {
            loose = false;
        }
        const containsStart = this.containsPosition(otherRange.start) || (loose && this.start.isEqual(otherRange.start));
        const containsEnd = this.containsPosition(otherRange.end) || (loose && this.end.isEqual(otherRange.end));
        return containsStart && containsEnd;
    }
    /**
     * Checks whether given {@link module:engine/model/item~Item} is inside this range.
     *
     * @param {module:engine/model/item~Item} item Model item to check.
     */
    containsItem(item) {
        const pos = position_Position._createBefore(item);
        return this.containsPosition(pos) || this.start.isEqual(pos);
    }
    /**
     * Two ranges are equal if their {@link #start} and {@link #end} positions are equal.
     *
     * @param {module:engine/model/range~Range} otherRange Range to compare with.
     * @returns {Boolean} `true` if ranges are equal, `false` otherwise.
     */
    isEqual(otherRange) {
        return this.start.isEqual(otherRange.start) && this.end.isEqual(otherRange.end);
    }
    /**
     * Checks and returns whether this range intersects with given range.
     *
     * @param {module:engine/model/range~Range} otherRange Range to compare with.
     * @returns {Boolean} `true` if ranges intersect, `false` otherwise.
     */
    isIntersecting(otherRange) {
        return this.start.isBefore(otherRange.end) && this.end.isAfter(otherRange.start);
    }
    /**
     * Computes which part(s) of this {@link ~Range range} is not a part of given {@link ~Range range}.
     * Returned array contains zero, one or two {@link ~Range ranges}.
     *
     * Examples:
     *
     *		let range = model.createRange(
     *			model.createPositionFromPath( root, [ 2, 7 ] ),
     *			model.createPositionFromPath( root, [ 4, 0, 1 ] )
     *		);
     *		let otherRange = model.createRange( model.createPositionFromPath( root, [ 1 ] ), model.createPositionFromPath( root, [ 5 ] ) );
     *		let transformed = range.getDifference( otherRange );
     *		// transformed array has no ranges because `otherRange` contains `range`
     *
     *		otherRange = model.createRange( model.createPositionFromPath( root, [ 1 ] ), model.createPositionFromPath( root, [ 3 ] ) );
     *		transformed = range.getDifference( otherRange );
     *		// transformed array has one range: from [ 3 ] to [ 4, 0, 1 ]
     *
     *		otherRange = model.createRange( model.createPositionFromPath( root, [ 3 ] ), model.createPositionFromPath( root, [ 4 ] ) );
     *		transformed = range.getDifference( otherRange );
     *		// transformed array has two ranges: from [ 2, 7 ] to [ 3 ] and from [ 4 ] to [ 4, 0, 1 ]
     *
     * @param {module:engine/model/range~Range} otherRange Range to differentiate against.
     * @returns {Array.<module:engine/model/range~Range>} The difference between ranges.
     */
    getDifference(otherRange) {
        const ranges = [];
        if (this.isIntersecting(otherRange)) {
            // Ranges intersect.
            if (this.containsPosition(otherRange.start)) {
                // Given range start is inside this range. This means that we have to
                // add shrunken range - from the start to the middle of this range.
                ranges.push(new range_Range(this.start, otherRange.start));
            }
            if (this.containsPosition(otherRange.end)) {
                // Given range end is inside this range. This means that we have to
                // add shrunken range - from the middle of this range to the end.
                ranges.push(new range_Range(otherRange.end, this.end));
            }
        }
        else {
            // Ranges do not intersect, return the original range.
            ranges.push(new range_Range(this.start, this.end));
        }
        return ranges;
    }
    /**
     * Returns an intersection of this {@link ~Range range} and given {@link ~Range range}.
     * Intersection is a common part of both of those ranges. If ranges has no common part, returns `null`.
     *
     * Examples:
     *
     *		let range = model.createRange(
     *			model.createPositionFromPath( root, [ 2, 7 ] ),
     *			model.createPositionFromPath( root, [ 4, 0, 1 ] )
     *		);
     *		let otherRange = model.createRange( model.createPositionFromPath( root, [ 1 ] ), model.createPositionFromPath( root, [ 2 ] ) );
     *		let transformed = range.getIntersection( otherRange ); // null - ranges have no common part
     *
     *		otherRange = model.createRange( model.createPositionFromPath( root, [ 3 ] ), model.createPositionFromPath( root, [ 5 ] ) );
     *		transformed = range.getIntersection( otherRange ); // range from [ 3 ] to [ 4, 0, 1 ]
     *
     * @param {module:engine/model/range~Range} otherRange Range to check for intersection.
     * @returns {module:engine/model/range~Range|null} A common part of given ranges or `null` if ranges have no common part.
     */
    getIntersection(otherRange) {
        if (this.isIntersecting(otherRange)) {
            // Ranges intersect, so a common range will be returned.
            // At most, it will be same as this range.
            let commonRangeStart = this.start;
            let commonRangeEnd = this.end;
            if (this.containsPosition(otherRange.start)) {
                // Given range start is inside this range. This means thaNt we have to
                // shrink common range to the given range start.
                commonRangeStart = otherRange.start;
            }
            if (this.containsPosition(otherRange.end)) {
                // Given range end is inside this range. This means that we have to
                // shrink common range to the given range end.
                commonRangeEnd = otherRange.end;
            }
            return new range_Range(commonRangeStart, commonRangeEnd);
        }
        // Ranges do not intersect, so they do not have common part.
        return null;
    }
    /**
     * Returns a range created by joining this {@link ~Range range} with the given {@link ~Range range}.
     * If ranges have no common part, returns `null`.
     *
     * Examples:
     *
     *		let range = model.createRange(
     *			model.createPositionFromPath( root, [ 2, 7 ] ),
     *			model.createPositionFromPath( root, [ 4, 0, 1 ] )
     *		);
     *		let otherRange = model.createRange(
     *			model.createPositionFromPath( root, [ 1 ] ),
     *			model.createPositionFromPath( root, [ 2 ] )
     *		);
     *		let transformed = range.getJoined( otherRange ); // null - ranges have no common part
     *
     *		otherRange = model.createRange(
     *			model.createPositionFromPath( root, [ 3 ] ),
     *			model.createPositionFromPath( root, [ 5 ] )
     *		);
     *		transformed = range.getJoined( otherRange ); // range from [ 2, 7 ] to [ 5 ]
     *
     * @param {module:engine/model/range~Range} otherRange Range to be joined.
     * @param {Boolean} [loose=false] Whether the intersection check is loose or strict. If the check is strict (`false`),
     * ranges are tested for intersection or whether start/end positions are equal. If the check is loose (`true`),
     * compared range is also checked if it's {@link module:engine/model/position~Position#isTouching touching} current range.
     * @returns {module:engine/model/range~Range|null} A sum of given ranges or `null` if ranges have no common part.
     */
    getJoined(otherRange, loose = false) {
        let shouldJoin = this.isIntersecting(otherRange);
        if (!shouldJoin) {
            if (this.start.isBefore(otherRange.start)) {
                shouldJoin = loose ? this.end.isTouching(otherRange.start) : this.end.isEqual(otherRange.start);
            }
            else {
                shouldJoin = loose ? otherRange.end.isTouching(this.start) : otherRange.end.isEqual(this.start);
            }
        }
        if (!shouldJoin) {
            return null;
        }
        let startPosition = this.start;
        let endPosition = this.end;
        if (otherRange.start.isBefore(startPosition)) {
            startPosition = otherRange.start;
        }
        if (otherRange.end.isAfter(endPosition)) {
            endPosition = otherRange.end;
        }
        return new range_Range(startPosition, endPosition);
    }
    /**
     * Computes and returns the smallest set of {@link #isFlat flat} ranges, that covers this range in whole.
     *
     * See an example of a model structure (`[` and `]` are range boundaries):
     *
     *		root                                                            root
     *		 |- element DIV                         DIV             P2              P3             DIV
     *		 |   |- element H                   H        P1        f o o           b a r       H         P4
     *		 |   |   |- "fir[st"             fir[st     lorem                               se]cond     ipsum
     *		 |   |- element P1
     *		 |   |   |- "lorem"                                              ||
     *		 |- element P2                                                   ||
     *		 |   |- "foo"                                                    VV
     *		 |- element P3
     *		 |   |- "bar"                                                   root
     *		 |- element DIV                         DIV             [P2             P3]             DIV
     *		 |   |- element H                   H       [P1]       f o o           b a r        H         P4
     *		 |   |   |- "se]cond"            fir[st]    lorem                               [se]cond     ipsum
     *		 |   |- element P4
     *		 |   |   |- "ipsum"
     *
     * As it can be seen, letters contained in the range are: `stloremfoobarse`, spread across different parents.
     * We are looking for minimal set of flat ranges that contains the same nodes.
     *
     * Minimal flat ranges for above range `( [ 0, 0, 3 ], [ 3, 0, 2 ] )` will be:
     *
     *		( [ 0, 0, 3 ], [ 0, 0, 5 ] ) = "st"
     *		( [ 0, 1 ], [ 0, 2 ] ) = element P1 ("lorem")
     *		( [ 1 ], [ 3 ] ) = element P2, element P3 ("foobar")
     *		( [ 3, 0, 0 ], [ 3, 0, 2 ] ) = "se"
     *
     * **Note:** if an {@link module:engine/model/element~Element element} is not wholly contained in this range, it won't be returned
     * in any of the returned flat ranges. See in the example how `H` elements at the beginning and at the end of the range
     * were omitted. Only their parts that were wholly in the range were returned.
     *
     * **Note:** this method is not returning flat ranges that contain no nodes.
     *
     * @returns {Array.<module:engine/model/range~Range>} Array of flat ranges covering this range.
     */
    getMinimalFlatRanges() {
        const ranges = [];
        const diffAt = this.start.getCommonPath(this.end).length;
        const pos = position_Position._createAt(this.start);
        let posParent = pos.parent;
        // Go up.
        while (pos.path.length > diffAt + 1) {
            const howMany = posParent.maxOffset - pos.offset;
            if (howMany !== 0) {
                ranges.push(new range_Range(pos, pos.getShiftedBy(howMany)));
            }
            pos.path = pos.path.slice(0, -1);
            pos.offset++;
            posParent = posParent.parent;
        }
        // Go down.
        while (pos.path.length <= this.end.path.length) {
            const offset = this.end.path[pos.path.length - 1];
            const howMany = offset - pos.offset;
            if (howMany !== 0) {
                ranges.push(new range_Range(pos, pos.getShiftedBy(howMany)));
            }
            pos.offset = offset;
            pos.path.push(0);
        }
        return ranges;
    }
    /**
     * Creates a {@link module:engine/model/treewalker~TreeWalker TreeWalker} instance with this range as a boundary.
     *
     * For example, to iterate over all items in the entire document root:
     *
     *		// Create a range spanning over the entire root content:
     *		const range = editor.model.createRangeIn( editor.model.document.getRoot() );
     *
     *		// Iterate over all items in this range:
     *		for ( const value of range.getWalker() ) {
     *			console.log( value.item );
     *		}
     *
     * @param {Object} options Object with configuration options. See {@link module:engine/model/treewalker~TreeWalker}.
     * @param {module:engine/model/position~Position} [options.startPosition]
     * @param {Boolean} [options.singleCharacters=false]
     * @param {Boolean} [options.shallow=false]
     * @param {Boolean} [options.ignoreElementEnd=false]
     * @returns {module:engine/model/treewalker~TreeWalker}
     */
    getWalker(options = {}) {
        options.boundaries = this;
        return new model_treewalker_TreeWalker(options);
    }
    /**
     * Returns an iterator that iterates over all {@link module:engine/model/item~Item items} that are in this range and returns
     * them.
     *
     * This method uses {@link module:engine/model/treewalker~TreeWalker} with `boundaries` set to this range and `ignoreElementEnd` option
     * set to `true`. However it returns only {@link module:engine/model/item~Item model items},
     * not {@link module:engine/model/treewalker~TreeWalkerValue}.
     *
     * You may specify additional options for the tree walker. See {@link module:engine/model/treewalker~TreeWalker} for
     * a full list of available options.
     *
     * @param {Object} [options] Object with configuration options. See {@link module:engine/model/treewalker~TreeWalker}.
     * @returns {Iterable.<module:engine/model/item~Item>}
     */
    *getItems(options = {}) {
        options.boundaries = this;
        options.ignoreElementEnd = true;
        const treeWalker = new model_treewalker_TreeWalker(options);
        for (const value of treeWalker) {
            yield value.item;
        }
    }
    /**
     * Returns an iterator that iterates over all {@link module:engine/model/position~Position positions} that are boundaries or
     * contained in this range.
     *
     * This method uses {@link module:engine/model/treewalker~TreeWalker} with `boundaries` set to this range. However it returns only
     * {@link module:engine/model/position~Position positions}, not {@link module:engine/model/treewalker~TreeWalkerValue}.
     *
     * You may specify additional options for the tree walker. See {@link module:engine/model/treewalker~TreeWalker} for
     * a full list of available options.
     *
     * @param {Object} options Object with configuration options. See {@link module:engine/model/treewalker~TreeWalker}.
     * @returns {Iterable.<module:engine/model/position~Position>}
     */
    *getPositions(options = {}) {
        options.boundaries = this;
        const treeWalker = new model_treewalker_TreeWalker(options);
        yield treeWalker.position;
        for (const value of treeWalker) {
            yield value.nextPosition;
        }
    }
    /**
     * Returns a range that is a result of transforming this range by given `operation`.
     *
     * **Note:** transformation may break one range into multiple ranges (for example, when a part of the range is
     * moved to a different part of document tree). For this reason, an array is returned by this method and it
     * may contain one or more `Range` instances.
     *
     * @param {module:engine/model/operation/operation~Operation} operation Operation to transform range by.
     * @returns {Array.<module:engine/model/range~Range>} Range which is the result of transformation.
     */
    getTransformedByOperation(operation) {
        switch (operation.type) {
            case 'insert':
                return this._getTransformedByInsertOperation(operation);
            case 'move':
            case 'remove':
            case 'reinsert':
                return this._getTransformedByMoveOperation(operation);
            case 'split':
                return [this._getTransformedBySplitOperation(operation)];
            case 'merge':
                return [this._getTransformedByMergeOperation(operation)];
        }
        return [new range_Range(this.start, this.end)];
    }
    /**
     * Returns a range that is a result of transforming this range by multiple `operations`.
     *
     * @see ~Range#getTransformedByOperation
     * @param {Iterable.<module:engine/model/operation/operation~Operation>} operations Operations to transform the range by.
     * @returns {Array.<module:engine/model/range~Range>} Range which is the result of transformation.
     */
    getTransformedByOperations(operations) {
        const ranges = [new range_Range(this.start, this.end)];
        for (const operation of operations) {
            for (let i = 0; i < ranges.length; i++) {
                const result = ranges[i].getTransformedByOperation(operation);
                ranges.splice(i, 1, ...result);
                i += result.length - 1;
            }
        }
        // It may happen that a range is split into two, and then the part of second "piece" is moved into first
        // "piece". In this case we will have incorrect third range, which should not be included in the result --
        // because it is already included in the first "piece". In this loop we are looking for all such ranges that
        // are inside other ranges and we simply remove them.
        for (let i = 0; i < ranges.length; i++) {
            const range = ranges[i];
            for (let j = i + 1; j < ranges.length; j++) {
                const next = ranges[j];
                if (range.containsRange(next) || next.containsRange(range) || range.isEqual(next)) {
                    ranges.splice(j, 1);
                }
            }
        }
        return ranges;
    }
    /**
     * Returns an {@link module:engine/model/element~Element} or {@link module:engine/model/documentfragment~DocumentFragment}
     * which is a common ancestor of the range's both ends (in which the entire range is contained).
     *
     * @returns {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment|null}
     */
    getCommonAncestor() {
        return this.start.getCommonAncestor(this.end);
    }
    /**
     * Returns an {@link module:engine/model/element~Element Element} contained by the range.
     * The element will be returned when it is the **only** node within the range and **fully–contained**
     * at the same time.
     *
     * @returns {module:engine/model/element~Element|null}
     */
    getContainedElement() {
        if (this.isCollapsed) {
            return null;
        }
        const nodeAfterStart = this.start.nodeAfter;
        const nodeBeforeEnd = this.end.nodeBefore;
        if (nodeAfterStart && nodeAfterStart.is('element') && nodeAfterStart === nodeBeforeEnd) {
            return nodeAfterStart;
        }
        return null;
    }
    /**
     * Converts `Range` to plain object and returns it.
     *
     * @returns {Object} `Node` converted to plain object.
     */
    toJSON() {
        return {
            start: this.start.toJSON(),
            end: this.end.toJSON()
        };
    }
    /**
     * Returns a new range that is equal to current range.
     *
     * @returns {module:engine/model/range~Range}
     */
    clone() {
        return new this.constructor(this.start, this.end);
    }
    /**
     * Returns a result of transforming a copy of this range by insert operation.
     *
     * One or more ranges may be returned as a result of this transformation.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/insertoperation~InsertOperation} operation
     * @returns {Array.<module:engine/model/range~Range>}
     */
    _getTransformedByInsertOperation(operation, spread = false) {
        return this._getTransformedByInsertion(operation.position, operation.howMany, spread);
    }
    /**
     * Returns a result of transforming a copy of this range by move operation.
     *
     * One or more ranges may be returned as a result of this transformation.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/moveoperation~MoveOperation} operation
     * @returns {Array.<module:engine/model/range~Range>}
     */
    _getTransformedByMoveOperation(operation, spread = false) {
        const sourcePosition = operation.sourcePosition;
        const howMany = operation.howMany;
        const targetPosition = operation.targetPosition;
        return this._getTransformedByMove(sourcePosition, targetPosition, howMany, spread);
    }
    /**
     * Returns a result of transforming a copy of this range by split operation.
     *
     * Always one range is returned. The transformation is done in a way to not break the range.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/splitoperation~SplitOperation} operation
     * @returns {module:engine/model/range~Range}
     */
    _getTransformedBySplitOperation(operation) {
        const start = this.start._getTransformedBySplitOperation(operation);
        let end = this.end._getTransformedBySplitOperation(operation);
        if (this.end.isEqual(operation.insertionPosition)) {
            end = this.end.getShiftedBy(1);
        }
        // Below may happen when range contains graveyard element used by split operation.
        if (start.root != end.root) {
            // End position was next to the moved graveyard element and was moved with it.
            // Fix it by using old `end` which has proper `root`.
            end = this.end.getShiftedBy(-1);
        }
        return new range_Range(start, end);
    }
    /**
     * Returns a result of transforming a copy of this range by merge operation.
     *
     * Always one range is returned. The transformation is done in a way to not break the range.
     *
     * @internal
     * @protected
     * @param {module:engine/model/operation/mergeoperation~MergeOperation} operation
     * @returns {module:engine/model/range~Range}
     */
    _getTransformedByMergeOperation(operation) {
        // Special case when the marker is set on "the closing tag" of an element. Marker can be set like that during
        // transformations, especially when a content of a few block elements were removed. For example:
        //
        // {} is the transformed range, [] is the removed range.
        // <p>F[o{o</p><p>B}ar</p><p>Xy]z</p>
        //
        // <p>Fo{o</p><p>B}ar</p><p>z</p>
        // <p>F{</p><p>B}ar</p><p>z</p>
        // <p>F{</p>}<p>z</p>
        // <p>F{}z</p>
        //
        if (this.start.isEqual(operation.targetPosition) && this.end.isEqual(operation.deletionPosition)) {
            return new range_Range(this.start);
        }
        let start = this.start._getTransformedByMergeOperation(operation);
        let end = this.end._getTransformedByMergeOperation(operation);
        if (start.root != end.root) {
            // This happens when the end position was next to the merged (deleted) element.
            // Then, the end position was moved to the graveyard root. In this case we need to fix
            // the range cause its boundaries would be in different roots.
            end = this.end.getShiftedBy(-1);
        }
        if (start.isAfter(end)) {
            // This happens in three following cases:
            //
            // Case 1: Merge operation source position is before the target position (due to some transformations, OT, etc.)
            //         This means that start can be moved before the end of the range.
            //
            // Before: <p>a{a</p><p>b}b</p><p>cc</p>
            // Merge:  <p>b}b</p><p>cca{a</p>
            // Fix:    <p>{b}b</p><p>ccaa</p>
            //
            // Case 2: Range start is before merged node but not directly.
            //         Result should include all nodes that were in the original range.
            //
            // Before: <p>aa</p>{<p>cc</p><p>b}b</p>
            // Merge:  <p>aab}b</p>{<p>cc</p>
            // Fix:    <p>aa{bb</p><p>cc</p>}
            //
            //         The range is expanded by an additional `b` letter but it is better than dropping the whole `cc` paragraph.
            //
            // Case 3: Range start is directly before merged node.
            //         Resulting range should include only nodes from the merged element:
            //
            // Before: <p>aa</p>{<p>b}b</p><p>cc</p>
            // Merge:  <p>aab}b</p>{<p>cc</p>
            // Fix:    <p>aa{b}b</p><p>cc</p>
            //
            if (operation.sourcePosition.isBefore(operation.targetPosition)) {
                // Case 1.
                start = position_Position._createAt(end);
                start.offset = 0;
            }
            else {
                if (!operation.deletionPosition.isEqual(start)) {
                    // Case 2.
                    end = operation.deletionPosition;
                }
                // In both case 2 and 3 start is at the end of the merge-to element.
                start = operation.targetPosition;
            }
            return new range_Range(start, end);
        }
        return new range_Range(start, end);
    }
    /**
     * Returns an array containing one or two {@link ~Range ranges} that are a result of transforming this
     * {@link ~Range range} by inserting `howMany` nodes at `insertPosition`. Two {@link ~Range ranges} are
     * returned if the insertion was inside this {@link ~Range range} and `spread` is set to `true`.
     *
     * Examples:
     *
     *		let range = model.createRange(
     *			model.createPositionFromPath( root, [ 2, 7 ] ),
     *			model.createPositionFromPath( root, [ 4, 0, 1 ] )
     *		);
     *		let transformed = range._getTransformedByInsertion( model.createPositionFromPath( root, [ 1 ] ), 2 );
     *		// transformed array has one range from [ 4, 7 ] to [ 6, 0, 1 ]
     *
     *		transformed = range._getTransformedByInsertion( model.createPositionFromPath( root, [ 4, 0, 0 ] ), 4 );
     *		// transformed array has one range from [ 2, 7 ] to [ 4, 0, 5 ]
     *
     *		transformed = range._getTransformedByInsertion( model.createPositionFromPath( root, [ 3, 2 ] ), 4 );
     *		// transformed array has one range, which is equal to original range
     *
     *		transformed = range._getTransformedByInsertion( model.createPositionFromPath( root, [ 3, 2 ] ), 4, true );
     *		// transformed array has two ranges: from [ 2, 7 ] to [ 3, 2 ] and from [ 3, 6 ] to [ 4, 0, 1 ]
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} insertPosition Position where nodes are inserted.
     * @param {Number} howMany How many nodes are inserted.
     * @param {Boolean} [spread] Flag indicating whether this {~Range range} should be spread if insertion
     * was inside the range. Defaults to `false`.
     * @returns {Array.<module:engine/model/range~Range>} Result of the transformation.
     */
    _getTransformedByInsertion(insertPosition, howMany, spread = false) {
        if (spread && this.containsPosition(insertPosition)) {
            // Range has to be spread. The first part is from original start to the spread point.
            // The other part is from spread point to the original end, but transformed by
            // insertion to reflect insertion changes.
            return [
                new range_Range(this.start, insertPosition),
                new range_Range(insertPosition.getShiftedBy(howMany), this.end._getTransformedByInsertion(insertPosition, howMany))
            ];
        }
        else {
            const range = new range_Range(this.start, this.end);
            range.start = range.start._getTransformedByInsertion(insertPosition, howMany);
            range.end = range.end._getTransformedByInsertion(insertPosition, howMany);
            return [range];
        }
    }
    /**
     * Returns an array containing {@link ~Range ranges} that are a result of transforming this
     * {@link ~Range range} by moving `howMany` nodes from `sourcePosition` to `targetPosition`.
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} sourcePosition Position from which nodes are moved.
     * @param {module:engine/model/position~Position} targetPosition Position to where nodes are moved.
     * @param {Number} howMany How many nodes are moved.
     * @param {Boolean} [spread=false] Whether the range should be spread if the move points inside the range.
     * @returns {Array.<module:engine/model/range~Range>} Result of the transformation.
     */
    _getTransformedByMove(sourcePosition, targetPosition, howMany, spread = false) {
        // Special case for transforming a collapsed range. Just transform it like a position.
        if (this.isCollapsed) {
            const newPos = this.start._getTransformedByMove(sourcePosition, targetPosition, howMany);
            return [new range_Range(newPos)];
        }
        // Special case for transformation when a part of the range is moved towards the range.
        //
        // Examples:
        //
        // <div><p>ab</p><p>c[d</p></div><p>e]f</p> --> <div><p>ab</p></div><p>c[d</p><p>e]f</p>
        // <p>e[f</p><div><p>a]b</p><p>cd</p></div> --> <p>e[f</p><p>a]b</p><div><p>cd</p></div>
        //
        // Without this special condition, the default algorithm leaves an "artifact" range from one of `differenceSet` parts:
        //
        // <div><p>ab</p><p>c[d</p></div><p>e]f</p> --> <div><p>ab</p>{</div>}<p>c[d</p><p>e]f</p>
        //
        // This special case is applied only if the range is to be kept together (not spread).
        const moveRange = range_Range._createFromPositionAndShift(sourcePosition, howMany);
        const insertPosition = targetPosition._getTransformedByDeletion(sourcePosition, howMany);
        if (this.containsPosition(targetPosition) && !spread) {
            if (moveRange.containsPosition(this.start) || moveRange.containsPosition(this.end)) {
                const start = this.start._getTransformedByMove(sourcePosition, targetPosition, howMany);
                const end = this.end._getTransformedByMove(sourcePosition, targetPosition, howMany);
                return [new range_Range(start, end)];
            }
        }
        // Default algorithm.
        let result;
        const differenceSet = this.getDifference(moveRange);
        let difference = null;
        const common = this.getIntersection(moveRange);
        if (differenceSet.length == 1) {
            // `moveRange` and this range may intersect but may be separate.
            difference = new range_Range(differenceSet[0].start._getTransformedByDeletion(sourcePosition, howMany), differenceSet[0].end._getTransformedByDeletion(sourcePosition, howMany));
        }
        else if (differenceSet.length == 2) {
            // `moveRange` is inside this range.
            difference = new range_Range(this.start, this.end._getTransformedByDeletion(sourcePosition, howMany));
        } // else, `moveRange` contains this range.
        if (difference) {
            result = difference._getTransformedByInsertion(insertPosition, howMany, common !== null || spread);
        }
        else {
            result = [];
        }
        if (common) {
            const transformedCommon = new range_Range(common.start._getCombined(moveRange.start, insertPosition), common.end._getCombined(moveRange.start, insertPosition));
            if (result.length == 2) {
                result.splice(1, 0, transformedCommon);
            }
            else {
                result.push(transformedCommon);
            }
        }
        return result;
    }
    /**
     * Returns a copy of this range that is transformed by deletion of `howMany` nodes from `deletePosition`.
     *
     * If the deleted range is intersecting with the transformed range, the transformed range will be shrank.
     *
     * If the deleted range contains transformed range, `null` will be returned.
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} deletionPosition Position from which nodes are removed.
     * @param {Number} howMany How many nodes are removed.
     * @returns {module:engine/model/range~Range|null} Result of the transformation.
     */
    _getTransformedByDeletion(deletePosition, howMany) {
        let newStart = this.start._getTransformedByDeletion(deletePosition, howMany);
        let newEnd = this.end._getTransformedByDeletion(deletePosition, howMany);
        if (newStart == null && newEnd == null) {
            return null;
        }
        if (newStart == null) {
            newStart = deletePosition;
        }
        if (newEnd == null) {
            newEnd = deletePosition;
        }
        return new range_Range(newStart, newEnd);
    }
    /**
     * Creates a new range, spreading from specified {@link module:engine/model/position~Position position} to a position moved by
     * given `shift`. If `shift` is a negative value, shifted position is treated as the beginning of the range.
     *
     * @internal
     * @protected
     * @param {module:engine/model/position~Position} position Beginning of the range.
     * @param {Number} shift How long the range should be.
     * @returns {module:engine/model/range~Range}
     */
    static _createFromPositionAndShift(position, shift) {
        const start = position;
        const end = position.getShiftedBy(shift);
        return shift > 0 ? new this(start, end) : new this(end, start);
    }
    /**
     * Creates a range inside an {@link module:engine/model/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     * @internal
     * @protected
     * @param {module:engine/model/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/model/range~Range}
     */
    static _createIn(element) {
        return new this(position_Position._createAt(element, 0), position_Position._createAt(element, element.maxOffset));
    }
    /**
     * Creates a range that starts before given {@link module:engine/model/item~Item model item} and ends after it.
     *
     * @internal
     * @protected
     * @param {module:engine/model/item~Item} item
     * @returns {module:engine/model/range~Range}
     */
    static _createOn(item) {
        return this._createFromPositionAndShift(position_Position._createBefore(item), item.offsetSize);
    }
    /**
     * Combines all ranges from the passed array into a one range. At least one range has to be passed.
     * Passed ranges must not have common parts.
     *
     * The first range from the array is a reference range. If other ranges start or end on the exactly same position where
     * the reference range, they get combined into one range.
     *
     *		[  ][]  [    ][ ][             ][ ][]  [  ]  // Passed ranges, shown sorted
     *		[    ]                                       // The result of the function if the first range was a reference range.
     *	            [                           ]        // The result of the function if the third-to-seventh range was a reference range.
     *	                                           [  ]  // The result of the function if the last range was a reference range.
     *
     * @internal
     * @protected
     * @param {Array.<module:engine/model/range~Range>} ranges Ranges to combine.
     * @returns {module:engine/model/range~Range} Combined range.
     */
    static _createFromRanges(ranges) {
        if (ranges.length === 0) {
            /**
             * At least one range has to be passed to
             * {@link module:engine/model/range~Range._createFromRanges `Range._createFromRanges()`}.
             *
             * @error range-create-from-ranges-empty-array
             */
            throw new CKEditorError('range-create-from-ranges-empty-array', null);
        }
        else if (ranges.length == 1) {
            return ranges[0].clone();
        }
        // 1. Set the first range in `ranges` array as a reference range.
        // If we are going to return just a one range, one of the ranges need to be the reference one.
        // Other ranges will be stuck to that range, if possible.
        const ref = ranges[0];
        // 2. Sort all the ranges so it's easier to process them.
        ranges.sort((a, b) => {
            return a.start.isAfter(b.start) ? 1 : -1;
        });
        // 3. Check at which index the reference range is now.
        const refIndex = ranges.indexOf(ref);
        // 4. At this moment we don't need the original range.
        // We are going to modify the result and we need to return a new instance of Range.
        // We have to create a copy of the reference range.
        const result = new this(ref.start, ref.end);
        // 5. Ranges should be checked and glued starting from the range that is closest to the reference range.
        // Since ranges are sorted, start with the range with index that is closest to reference range index.
        if (refIndex > 0) {
            // eslint-disable-next-line no-constant-condition
            for (let i = refIndex - 1; true; i++) {
                if (ranges[i].end.isEqual(result.start)) {
                    result.start = position_Position._createAt(ranges[i].start);
                }
                else {
                    // If ranges are not starting/ending at the same position there is no point in looking further.
                    break;
                }
            }
        }
        // 6. Ranges should be checked and glued starting from the range that is closest to the reference range.
        // Since ranges are sorted, start with the range with index that is closest to reference range index.
        for (let i = refIndex + 1; i < ranges.length; i++) {
            if (ranges[i].start.isEqual(result.end)) {
                result.end = position_Position._createAt(ranges[i].end);
            }
            else {
                // If ranges are not starting/ending at the same position there is no point in looking further.
                break;
            }
        }
        return result;
    }
    /**
     * Creates a `Range` instance from given plain object (i.e. parsed JSON string).
     *
     * @param {Object} json Plain object to be converted to `Range`.
     * @param {module:engine/model/document~Document} doc Document object that will be range owner.
     * @returns {module:engine/model/range~Range} `Range` instance created using given plain object.
     */
    static fromJSON(json, doc) {
        return new this(position_Position.fromJSON(json.start, doc), position_Position.fromJSON(json.end, doc));
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		range.is( 'range' ); // -> true
 *		range.is( 'model:range' ); // -> true
 *
 *		range.is( 'view:range' ); // -> false
 *		range.is( 'documentSelection' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
range_Range.prototype.is = function (type) {
    return type === 'range' || type === 'model:range';
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/mapper.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/conversion/mapper
 */







/**
 * Maps elements, positions and markers between the {@link module:engine/view/document~Document view} and
 * the {@link module:engine/model/model model}.
 *
 * The instance of the Mapper used for the editing pipeline is available in
 * {@link module:engine/controller/editingcontroller~EditingController#mapper `editor.editing.mapper`}.
 *
 * Mapper uses bound elements to find corresponding elements and positions, so, to get proper results,
 * all model elements should be {@link module:engine/conversion/mapper~Mapper#bindElements bound}.
 *
 * To map the complex model to/from view relations, you may provide custom callbacks for the
 * {@link module:engine/conversion/mapper~Mapper#event:modelToViewPosition modelToViewPosition event} and
 * {@link module:engine/conversion/mapper~Mapper#event:viewToModelPosition viewToModelPosition event} that are fired whenever
 * a position mapping request occurs.
 * Those events are fired by the {@link module:engine/conversion/mapper~Mapper#toViewPosition toViewPosition}
 * and {@link module:engine/conversion/mapper~Mapper#toModelPosition toModelPosition} methods. `Mapper` adds its own default callbacks
 * with `'lowest'` priority. To override default `Mapper` mapping, add custom callback with higher priority and
 * stop the event.
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class Mapper extends Emitter {
    /**
     * Creates an instance of the mapper.
     */
    constructor() {
        super();
        /**
         * Model element to view element mapping.
         *
         * @private
         * @member {WeakMap}
         */
        this._modelToViewMapping = new WeakMap();
        /**
         * View element to model element mapping.
         *
         * @private
         * @member {WeakMap}
         */
        this._viewToModelMapping = new WeakMap();
        /**
         * A map containing callbacks between view element names and functions evaluating length of view elements
         * in model.
         *
         * @private
         * @member {Map}
         */
        this._viewToModelLengthCallbacks = new Map();
        /**
         * Model marker name to view elements mapping.
         *
         * Keys are `String`s while values are `Set`s with {@link module:engine/view/element~Element view elements}.
         * One marker (name) can be mapped to multiple elements.
         *
         * @private
         * @member {Map}
         */
        this._markerNameToElements = new Map();
        /**
         * View element to model marker names mapping.
         *
         * This is reverse to {@link ~Mapper#_markerNameToElements} map.
         *
         * @private
         * @member {Map}
         */
        this._elementToMarkerNames = new Map();
        /**
         * The map of removed view elements with their current root (used for deferred unbinding).
         *
         * @private
         * @member {Map.<module:engine/view/element~Element,module:engine/view/documentfragment~DocumentFragment>}
         */
        this._deferredBindingRemovals = new Map();
        /**
         * Stores marker names of markers which have changed due to unbinding a view element (so it is assumed that the view element
         * has been removed, moved or renamed).
         *
         * @private
         * @member {Set.<module:engine/model/markercollection~Marker>}
         */
        this._unboundMarkerNames = new Set();
        // Default mapper algorithm for mapping model position to view position.
        this.on('modelToViewPosition', (evt, data) => {
            if (data.viewPosition) {
                return;
            }
            const viewContainer = this._modelToViewMapping.get(data.modelPosition.parent);
            if (!viewContainer) {
                /**
                 * A model position could not be mapped to the view because the parent of the model position
                 * does not have a mapped view element (might have not been converted yet or it has no converter).
                 *
                 * Make sure that the model element is correctly converted to the view.
                 *
                 * @error mapping-model-position-view-parent-not-found
                 */
                throw new CKEditorError('mapping-model-position-view-parent-not-found', this, { modelPosition: data.modelPosition });
            }
            data.viewPosition = this.findPositionIn(viewContainer, data.modelPosition.offset);
        }, { priority: 'low' });
        // Default mapper algorithm for mapping view position to model position.
        this.on('viewToModelPosition', (evt, data) => {
            if (data.modelPosition) {
                return;
            }
            const viewBlock = this.findMappedViewAncestor(data.viewPosition);
            const modelParent = this._viewToModelMapping.get(viewBlock);
            const modelOffset = this._toModelOffset(data.viewPosition.parent, data.viewPosition.offset, viewBlock);
            data.modelPosition = position_Position._createAt(modelParent, modelOffset);
        }, { priority: 'low' });
    }
    /**
     * Marks model and view elements as corresponding. Corresponding elements can be retrieved by using
     * the {@link module:engine/conversion/mapper~Mapper#toModelElement toModelElement} and
     * {@link module:engine/conversion/mapper~Mapper#toViewElement toViewElement} methods.
     * The information that elements are bound is also used to translate positions.
     *
     * @param {module:engine/model/element~Element} modelElement Model element.
     * @param {module:engine/view/element~Element} viewElement View element.
     */
    bindElements(modelElement, viewElement) {
        this._modelToViewMapping.set(modelElement, viewElement);
        this._viewToModelMapping.set(viewElement, modelElement);
    }
    /**
     * Unbinds the given {@link module:engine/view/element~Element view element} from the map.
     *
     * **Note:** view-to-model binding will be removed, if it existed. However, corresponding model-to-view binding
     * will be removed only if model element is still bound to the passed `viewElement`.
     *
     * This behavior allows for re-binding model element to another view element without fear of losing the new binding
     * when the previously bound view element is unbound.
     *
     * @param {module:engine/view/element~Element} viewElement View element to unbind.
     * @param {Object} [options={}] The options object.
     * @param {Boolean} [options.defer=false] Controls whether the binding should be removed immediately or deferred until a
     * {@link #flushDeferredBindings `flushDeferredBindings()`} call.
     */
    unbindViewElement(viewElement, options = {}) {
        const modelElement = this.toModelElement(viewElement);
        if (this._elementToMarkerNames.has(viewElement)) {
            for (const markerName of this._elementToMarkerNames.get(viewElement)) {
                this._unboundMarkerNames.add(markerName);
            }
        }
        if (options.defer) {
            this._deferredBindingRemovals.set(viewElement, viewElement.root);
        }
        else {
            this._viewToModelMapping.delete(viewElement);
            if (this._modelToViewMapping.get(modelElement) == viewElement) {
                this._modelToViewMapping.delete(modelElement);
            }
        }
    }
    /**
     * Unbinds the given {@link module:engine/model/element~Element model element} from the map.
     *
     * **Note:** the model-to-view binding will be removed, if it existed. However, the corresponding view-to-model binding
     * will be removed only if the view element is still bound to the passed `modelElement`.
     *
     * This behavior lets for re-binding view element to another model element without fear of losing the new binding
     * when the previously bound model element is unbound.
     *
     * @param {module:engine/model/element~Element} modelElement Model element to unbind.
     */
    unbindModelElement(modelElement) {
        const viewElement = this.toViewElement(modelElement);
        this._modelToViewMapping.delete(modelElement);
        if (this._viewToModelMapping.get(viewElement) == modelElement) {
            this._viewToModelMapping.delete(viewElement);
        }
    }
    /**
     * Binds the given marker name with the given {@link module:engine/view/element~Element view element}. The element
     * will be added to the current set of elements bound with the given marker name.
     *
     * @param {module:engine/view/element~Element} element Element to bind.
     * @param {String} name Marker name.
     */
    bindElementToMarker(element, name) {
        const elements = this._markerNameToElements.get(name) || new Set();
        elements.add(element);
        const names = this._elementToMarkerNames.get(element) || new Set();
        names.add(name);
        this._markerNameToElements.set(name, elements);
        this._elementToMarkerNames.set(element, names);
    }
    /**
     * Unbinds an element from given marker name.
     *
     * @param {module:engine/view/element~Element} element Element to unbind.
     * @param {String} name Marker name.
     */
    unbindElementFromMarkerName(element, name) {
        const nameToElements = this._markerNameToElements.get(name);
        if (nameToElements) {
            nameToElements.delete(element);
            if (nameToElements.size == 0) {
                this._markerNameToElements.delete(name);
            }
        }
        const elementToNames = this._elementToMarkerNames.get(element);
        if (elementToNames) {
            elementToNames.delete(name);
            if (elementToNames.size == 0) {
                this._elementToMarkerNames.delete(element);
            }
        }
    }
    /**
     * Returns all marker names of markers which have changed due to unbinding a view element (so it is assumed that the view element
     * has been removed, moved or renamed) since the last flush. After returning, the marker names list is cleared.
     *
     * @returns {Array.<String>}
     */
    flushUnboundMarkerNames() {
        const markerNames = Array.from(this._unboundMarkerNames);
        this._unboundMarkerNames.clear();
        return markerNames;
    }
    /**
     * Unbinds all deferred binding removals of view elements that in the meantime were not re-attached to some root or document fragment.
     *
     * See: {@link #unbindViewElement `unbindViewElement()`}.
     */
    flushDeferredBindings() {
        for (const [viewElement, root] of this._deferredBindingRemovals) {
            // Unbind it only if it wasn't re-attached to some root or document fragment.
            if (viewElement.root == root) {
                this.unbindViewElement(viewElement);
            }
        }
        this._deferredBindingRemovals = new Map();
    }
    /**
     * Removes all model to view and view to model bindings.
     */
    clearBindings() {
        this._modelToViewMapping = new WeakMap();
        this._viewToModelMapping = new WeakMap();
        this._markerNameToElements = new Map();
        this._elementToMarkerNames = new Map();
        this._unboundMarkerNames = new Set();
        this._deferredBindingRemovals = new Map();
    }
    toModelElement(viewElement) {
        return this._viewToModelMapping.get(viewElement);
    }
    toViewElement(modelElement) {
        return this._modelToViewMapping.get(modelElement);
    }
    /**
     * Gets the corresponding model range.
     *
     * @param {module:engine/view/range~Range} viewRange View range.
     * @returns {module:engine/model/range~Range} Corresponding model range.
     */
    toModelRange(viewRange) {
        return new range_Range(this.toModelPosition(viewRange.start), this.toModelPosition(viewRange.end));
    }
    /**
     * Gets the corresponding view range.
     *
     * @param {module:engine/model/range~Range} modelRange Model range.
     * @returns {module:engine/view/range~Range} Corresponding view range.
     */
    toViewRange(modelRange) {
        return new Range(this.toViewPosition(modelRange.start), this.toViewPosition(modelRange.end));
    }
    /**
     * Gets the corresponding model position.
     *
     * @fires viewToModelPosition
     * @param {module:engine/view/position~Position} viewPosition View position.
     * @returns {module:engine/model/position~Position} Corresponding model position.
     */
    toModelPosition(viewPosition) {
        const data = {
            viewPosition,
            mapper: this
        };
        this.fire('viewToModelPosition', data);
        return data.modelPosition;
    }
    /**
     * Gets the corresponding view position.
     *
     * @fires modelToViewPosition
     * @param {module:engine/model/position~Position} modelPosition Model position.
     * @param {Object} [options] Additional options for position mapping process.
     * @param {Boolean} [options.isPhantom=false] Should be set to `true` if the model position to map is pointing to a place
     * in model tree which no longer exists. For example, it could be an end of a removed model range.
     * @returns {module:engine/view/position~Position} Corresponding view position.
     */
    toViewPosition(modelPosition, options = {}) {
        const data = {
            modelPosition,
            mapper: this,
            isPhantom: options.isPhantom
        };
        this.fire('modelToViewPosition', data);
        return data.viewPosition;
    }
    /**
     * Gets all view elements bound to the given marker name.
     *
     * @param {String} name Marker name.
     * @returns {Set.<module:engine/view/element~Element>|null} View elements bound with the given marker name or `null`
     * if no elements are bound to the given marker name.
     */
    markerNameToElements(name) {
        const boundElements = this._markerNameToElements.get(name);
        if (!boundElements) {
            return null;
        }
        const elements = new Set();
        for (const element of boundElements) {
            if (element.is('attributeElement')) {
                for (const clone of element.getElementsWithSameId()) {
                    elements.add(clone);
                }
            }
            else {
                elements.add(element);
            }
        }
        return elements;
    }
    /**
     * Registers a callback that evaluates the length in the model of a view element with the given name.
     *
     * The callback is fired with one argument, which is a view element instance. The callback is expected to return
     * a number representing the length of the view element in the model.
     *
     *		// List item in view may contain nested list, which have other list items. In model though,
     *		// the lists are represented by flat structure. Because of those differences, length of list view element
     *		// may be greater than one. In the callback it's checked how many nested list items are in evaluated list item.
     *
     *		function getViewListItemLength( element ) {
     *			let length = 1;
     *
     *			for ( let child of element.getChildren() ) {
     *				if ( child.name == 'ul' || child.name == 'ol' ) {
     *					for ( let item of child.getChildren() ) {
     *						length += getViewListItemLength( item );
     *					}
     *				}
     *			}
     *
     *			return length;
     *		}
     *
     *		mapper.registerViewToModelLength( 'li', getViewListItemLength );
     *
     * @param {String} viewElementName Name of view element for which callback is registered.
     * @param {Function} lengthCallback Function return a length of view element instance in model.
     */
    registerViewToModelLength(viewElementName, lengthCallback) {
        this._viewToModelLengthCallbacks.set(viewElementName, lengthCallback);
    }
    /**
     * For the given `viewPosition`, finds and returns the closest ancestor of this position that has a mapping to
     * the model.
     *
     * @param {module:engine/view/position~Position} viewPosition Position for which a mapped ancestor should be found.
     * @returns {module:engine/view/element~Element}
     */
    findMappedViewAncestor(viewPosition) {
        let parent = viewPosition.parent;
        while (!this._viewToModelMapping.has(parent)) {
            parent = parent.parent;
        }
        return parent;
    }
    /**
     * Calculates model offset based on the view position and the block element.
     *
     * Example:
     *
     *		<p>foo<b>ba|r</b></p> // _toModelOffset( b, 2, p ) -> 5
     *
     * Is a sum of:
     *
     *		<p>foo|<b>bar</b></p> // _toModelOffset( p, 3, p ) -> 3
     *		<p>foo<b>ba|r</b></p> // _toModelOffset( b, 2, b ) -> 2
     *
     * @private
     * @param {module:engine/view/element~Element} viewParent Position parent.
     * @param {Number} viewOffset Position offset.
     * @param {module:engine/view/element~Element} viewBlock Block used as a base to calculate offset.
     * @returns {Number} Offset in the model.
     */
    _toModelOffset(viewParent, viewOffset, viewBlock) {
        if (viewBlock != viewParent) {
            // See example.
            const offsetToParentStart = this._toModelOffset(viewParent.parent, viewParent.index, viewBlock);
            const offsetInParent = this._toModelOffset(viewParent, viewOffset, viewParent);
            return offsetToParentStart + offsetInParent;
        }
        // viewBlock == viewParent, so we need to calculate the offset in the parent element.
        // If the position is a text it is simple ("ba|r" -> 2).
        if (viewParent.is('$text')) {
            return viewOffset;
        }
        // If the position is in an element we need to sum lengths of siblings ( <b> bar </b> foo | -> 3 + 3 = 6 ).
        let modelOffset = 0;
        for (let i = 0; i < viewOffset; i++) {
            modelOffset += this.getModelLength(viewParent.getChild(i));
        }
        return modelOffset;
    }
    /**
     * Gets the length of the view element in the model.
     *
     * The length is calculated as follows:
     * * if a {@link #registerViewToModelLength length mapping callback} is provided for the given `viewNode`, it is used to
     * evaluate the model length (`viewNode` is used as first and only parameter passed to the callback),
     * * length of a {@link module:engine/view/text~Text text node} is equal to the length of its
     * {@link module:engine/view/text~Text#data data},
     * * length of a {@link module:engine/view/uielement~UIElement ui element} is equal to 0,
     * * length of a mapped {@link module:engine/view/element~Element element} is equal to 1,
     * * length of a non-mapped {@link module:engine/view/element~Element element} is equal to the length of its children.
     *
     * Examples:
     *
     *		foo                          -> 3 // Text length is equal to its data length.
     *		<p>foo</p>                   -> 1 // Length of an element which is mapped is by default equal to 1.
     *		<b>foo</b>                   -> 3 // Length of an element which is not mapped is a length of its children.
     *		<div><p>x</p><p>y</p></div>  -> 2 // Assuming that <div> is not mapped and <p> are mapped.
     *
     * @param {module:engine/view/element~Element} viewNode View node.
     * @returns {Number} Length of the node in the tree model.
     */
    getModelLength(viewNode) {
        if (this._viewToModelLengthCallbacks.get(viewNode.name)) {
            const callback = this._viewToModelLengthCallbacks.get(viewNode.name);
            return callback(viewNode);
        }
        else if (this._viewToModelMapping.has(viewNode)) {
            return 1;
        }
        else if (viewNode.is('$text')) {
            return viewNode.data.length;
        }
        else if (viewNode.is('uiElement')) {
            return 0;
        }
        else {
            let len = 0;
            for (const child of viewNode.getChildren()) {
                len += this.getModelLength(child);
            }
            return len;
        }
    }
    /**
     * Finds the position in the view node (or in its children) with the expected model offset.
     *
     * Example:
     *
     *		<p>fo<b>bar</b>bom</p> -> expected offset: 4
     *
     *		findPositionIn( p, 4 ):
     *		<p>|fo<b>bar</b>bom</p> -> expected offset: 4, actual offset: 0
     *		<p>fo|<b>bar</b>bom</p> -> expected offset: 4, actual offset: 2
     *		<p>fo<b>bar</b>|bom</p> -> expected offset: 4, actual offset: 5 -> we are too far
     *
     *		findPositionIn( b, 4 - ( 5 - 3 ) ):
     *		<p>fo<b>|bar</b>bom</p> -> expected offset: 2, actual offset: 0
     *		<p>fo<b>bar|</b>bom</p> -> expected offset: 2, actual offset: 3 -> we are too far
     *
     *		findPositionIn( bar, 2 - ( 3 - 3 ) ):
     *		We are in the text node so we can simple find the offset.
     *		<p>fo<b>ba|r</b>bom</p> -> expected offset: 2, actual offset: 2 -> position found
     *
     * @param {module:engine/view/element~Element} viewParent Tree view element in which we are looking for the position.
     * @param {Number} expectedOffset Expected offset.
     * @returns {module:engine/view/position~Position} Found position.
     */
    findPositionIn(viewParent, expectedOffset) {
        // Last scanned view node.
        let viewNode;
        // Length of the last scanned view node.
        let lastLength = 0;
        let modelOffset = 0;
        let viewOffset = 0;
        // In the text node it is simple: the offset in the model equals the offset in the text.
        if (viewParent.is('$text')) {
            return new Position(viewParent, expectedOffset);
        }
        // In other cases we add lengths of child nodes to find the proper offset.
        // If it is smaller we add the length.
        while (modelOffset < expectedOffset) {
            viewNode = viewParent.getChild(viewOffset);
            lastLength = this.getModelLength(viewNode);
            modelOffset += lastLength;
            viewOffset++;
        }
        // If it equals we found the position.
        if (modelOffset == expectedOffset) {
            return this._moveViewPositionToTextNode(new Position(viewParent, viewOffset));
        }
        // If it is higher we need to enter last child.
        else {
            // ( modelOffset - lastLength ) is the offset to the child we enter,
            // so we subtract it from the expected offset to fine the offset in the child.
            return this.findPositionIn(viewNode, expectedOffset - (modelOffset - lastLength));
        }
    }
    /**
     * Because we prefer positions in the text nodes over positions next to text nodes, if the view position was next to a text node,
     * it moves it into the text node instead.
     *
     *		<p>[]<b>foo</b></p> -> <p>[]<b>foo</b></p> // do not touch if position is not directly next to text
     *		<p>foo[]<b>foo</b></p> -> <p>foo{}<b>foo</b></p> // move to text node
     *		<p><b>[]foo</b></p> -> <p><b>{}foo</b></p> // move to text node
     *
     * @private
     * @param {module:engine/view/position~Position} viewPosition Position potentially next to the text node.
     * @returns {module:engine/view/position~Position} Position in the text node if possible.
     */
    _moveViewPositionToTextNode(viewPosition) {
        // If the position is just after a text node, put it at the end of that text node.
        // If the position is just before a text node, put it at the beginning of that text node.
        const nodeBefore = viewPosition.nodeBefore;
        const nodeAfter = viewPosition.nodeAfter;
        if (nodeBefore instanceof text_Text) {
            return new Position(nodeBefore, nodeBefore.data.length);
        }
        else if (nodeAfter instanceof text_Text) {
            return new Position(nodeAfter, 0);
        }
        // Otherwise, just return the given position.
        return viewPosition;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/modelconsumable.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/conversion/modelconsumable
 */


/**
 * Manages a list of consumable values for the {@link module:engine/model/item~Item model items}.
 *
 * Consumables are various aspects of the model. A model item can be broken down into separate, single properties that might be
 * taken into consideration when converting that item.
 *
 * `ModelConsumable` is used by {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} while analyzing the changed
 * parts of {@link module:engine/model/document~Document the document}. The added / changed / removed model items are broken down
 * into singular properties (the item itself and its attributes). All those parts are saved in `ModelConsumable`. Then,
 * during conversion, when the given part of a model item is converted (i.e. the view element has been inserted into the view,
 * but without attributes), the consumable value is removed from `ModelConsumable`.
 *
 * For model items, `ModelConsumable` stores consumable values of one of following types: `insert`, `addattribute:<attributeKey>`,
 * `changeattributes:<attributeKey>`, `removeattributes:<attributeKey>`.
 *
 * In most cases, it is enough to let th {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}
 * gather consumable values, so there is no need to use
 * the {@link module:engine/conversion/modelconsumable~ModelConsumable#add add method} directly.
 * However, it is important to understand how consumable values can be
 * {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed}.
 * See {@link module:engine/conversion/downcasthelpers default downcast converters} for more information.
 *
 * Keep in mind that one conversion event may have multiple callbacks (converters) attached to it. Each of those is
 * able to convert one or more parts of the model. However, when one of those callbacks actually converts
 * something, the others should not, because they would duplicate the results. Using `ModelConsumable` helps to avoid
 * this situation, because callbacks should only convert these values that were not yet consumed from `ModelConsumable`.
 *
 * Consuming multiple values in a single callback:
 *
 *		// Converter for custom `imageBlock` element that might have a `caption` element inside which changes
 *		// how the image is displayed in the view:
 *		//
 *		// Model:
 *		//
 *		// [imageBlock]
 *		//   └─ [caption]
 *		//       └─ foo
 *		//
 *		// View:
 *		//
 *		// <figure>
 *		//   ├─ <img />
 *		//   └─ <caption>
 *		//       └─ foo
 *		modelConversionDispatcher.on( 'insert:imageBlock', ( evt, data, conversionApi ) => {
 *			// First, consume the `imageBlock` element.
 *			conversionApi.consumable.consume( data.item, 'insert' );
 *
 *			// Just create normal image element for the view.
 *			// Maybe it will be "decorated" later.
 *			const viewImage = new ViewElement( 'img' );
 *			const insertPosition = conversionApi.mapper.toViewPosition( data.range.start );
 *			const viewWriter = conversionApi.writer;
 *
 *			// Check if the `imageBlock` element has children.
 *			if ( data.item.childCount > 0 ) {
 *				const modelCaption = data.item.getChild( 0 );
 *
 *				// `modelCaption` insertion change is consumed from consumable values.
 *				// It will not be converted by other converters, but it's children (probably some text) will be.
 *				// Through mapping, converters for text will know where to insert contents of `modelCaption`.
 *				if ( conversionApi.consumable.consume( modelCaption, 'insert' ) ) {
 *					const viewCaption = new ViewElement( 'figcaption' );
 *
 *					const viewImageHolder = new ViewElement( 'figure', null, [ viewImage, viewCaption ] );
 *
 *					conversionApi.mapper.bindElements( modelCaption, viewCaption );
 *					conversionApi.mapper.bindElements( data.item, viewImageHolder );
 *					viewWriter.insert( insertPosition, viewImageHolder );
 *				}
 *			} else {
 *				conversionApi.mapper.bindElements( data.item, viewImage );
 *				viewWriter.insert( insertPosition, viewImage );
 *			}
 *
 *			evt.stop();
 *		} );
 */
class ModelConsumable {
    /**
     * Creates an empty consumables list.
     */
    constructor() {
        /**
         * Contains list of consumable values.
         *
         * @private
         * @member {Map} module:engine/conversion/modelconsumable~ModelConsumable#_consumable
         */
        this._consumable = new Map();
        /**
         * For each {@link module:engine/model/textproxy~TextProxy} added to `ModelConsumable`, this registry holds a parent
         * of that `TextProxy` and the start and end indices of that `TextProxy`. This allows identification of the `TextProxy`
         * instances that point to the same part of the model but are different instances. Each distinct `TextProxy`
         * is given a unique `Symbol` which is then registered as consumable. This process is transparent for the `ModelConsumable`
         * API user because whenever `TextProxy` is added, tested, consumed or reverted, the internal mechanisms of
         * `ModelConsumable` translate `TextProxy` to that unique `Symbol`.
         *
         * @private
         * @member {Map} module:engine/conversion/modelconsumable~ModelConsumable#_textProxyRegistry
         */
        this._textProxyRegistry = new Map();
    }
    /**
     * Adds a consumable value to the consumables list and links it with a given model item.
     *
     *		modelConsumable.add( modelElement, 'insert' ); // Add `modelElement` insertion change to consumable values.
     *		modelConsumable.add( modelElement, 'addAttribute:bold' ); // Add `bold` attribute insertion on `modelElement` change.
     *		modelConsumable.add( modelElement, 'removeAttribute:bold' ); // Add `bold` attribute removal on `modelElement` change.
     *		modelConsumable.add( modelSelection, 'selection' ); // Add `modelSelection` to consumable values.
     *		modelConsumable.add( modelRange, 'range' ); // Add `modelRange` to consumable values.
     *
     * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item
     * Model item, range or selection that has the consumable.
     * @param {String} type Consumable type. Will be normalized to a proper form, that is either `<word>` or `<part>:<part>`.
     * Second colon and everything after will be cut. Passing event name is a safe and good practice.
     */
    add(item, type) {
        type = _normalizeConsumableType(type);
        if (item instanceof textproxy_TextProxy) {
            item = this._getSymbolForTextProxy(item);
        }
        if (!this._consumable.has(item)) {
            this._consumable.set(item, new Map());
        }
        this._consumable.get(item).set(type, true);
    }
    /**
     * Removes a given consumable value from a given model item.
     *
     *		modelConsumable.consume( modelElement, 'insert' ); // Remove `modelElement` insertion change from consumable values.
     *		modelConsumable.consume( modelElement, 'addAttribute:bold' ); // Remove `bold` attribute insertion on `modelElement` change.
     *		modelConsumable.consume( modelElement, 'removeAttribute:bold' ); // Remove `bold` attribute removal on `modelElement` change.
     *		modelConsumable.consume( modelSelection, 'selection' ); // Remove `modelSelection` from consumable values.
     *		modelConsumable.consume( modelRange, 'range' ); // Remove 'modelRange' from consumable values.
     *
     * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item
     * Model item, range or selection from which consumable will be consumed.
     * @param {String} type Consumable type. Will be normalized to a proper form, that is either `<word>` or `<part>:<part>`.
     * Second colon and everything after will be cut. Passing event name is a safe and good practice.
     * @returns {Boolean} `true` if consumable value was available and was consumed, `false` otherwise.
     */
    consume(item, type) {
        type = _normalizeConsumableType(type);
        if (item instanceof textproxy_TextProxy) {
            item = this._getSymbolForTextProxy(item);
        }
        if (this.test(item, type)) {
            this._consumable.get(item).set(type, false);
            return true;
        }
        else {
            return false;
        }
    }
    /**
     * Tests whether there is a consumable value of a given type connected with a given model item.
     *
     *		modelConsumable.test( modelElement, 'insert' ); // Check for `modelElement` insertion change.
     *		modelConsumable.test( modelElement, 'addAttribute:bold' ); // Check for `bold` attribute insertion on `modelElement` change.
     *		modelConsumable.test( modelElement, 'removeAttribute:bold' ); // Check for `bold` attribute removal on `modelElement` change.
     *		modelConsumable.test( modelSelection, 'selection' ); // Check if `modelSelection` is consumable.
     *		modelConsumable.test( modelRange, 'range' ); // Check if `modelRange` is consumable.
     *
     * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item
     * Model item, range or selection to be tested.
     * @param {String} type Consumable type. Will be normalized to a proper form, that is either `<word>` or `<part>:<part>`.
     * Second colon and everything after will be cut. Passing event name is a safe and good practice.
     * @returns {null|Boolean} `null` if such consumable was never added, `false` if the consumable values was
     * already consumed or `true` if it was added and not consumed yet.
     */
    test(item, type) {
        type = _normalizeConsumableType(type);
        if (item instanceof textproxy_TextProxy) {
            item = this._getSymbolForTextProxy(item);
        }
        const itemConsumables = this._consumable.get(item);
        if (itemConsumables === undefined) {
            return null;
        }
        const value = itemConsumables.get(type);
        if (value === undefined) {
            return null;
        }
        return value;
    }
    /**
     * Reverts consuming of a consumable value.
     *
     *		modelConsumable.revert( modelElement, 'insert' ); // Revert consuming `modelElement` insertion change.
     *		modelConsumable.revert( modelElement, 'addAttribute:bold' ); // Revert consuming `bold` attribute insert from `modelElement`.
     *		modelConsumable.revert( modelElement, 'removeAttribute:bold' ); // Revert consuming `bold` attribute remove from `modelElement`.
     *		modelConsumable.revert( modelSelection, 'selection' ); // Revert consuming `modelSelection`.
     *		modelConsumable.revert( modelRange, 'range' ); // Revert consuming `modelRange`.
     *
     * @param {module:engine/model/item~Item|module:engine/model/selection~Selection|module:engine/model/range~Range} item
     * Model item, range or selection to be reverted.
     * @param {String} type Consumable type.
     * @returns {null|Boolean} `true` if consumable has been reversed, `false` otherwise. `null` if the consumable has
     * never been added.
     */
    revert(item, type) {
        type = _normalizeConsumableType(type);
        if (item instanceof textproxy_TextProxy) {
            item = this._getSymbolForTextProxy(item);
        }
        const test = this.test(item, type);
        if (test === false) {
            this._consumable.get(item).set(type, true);
            return true;
        }
        else if (test === true) {
            return false;
        }
        return null;
    }
    /**
     * Verifies if all events from the specified group were consumed.
     *
     * @param {String} eventGroup The events group to verify.
     */
    verifyAllConsumed(eventGroup) {
        const items = [];
        for (const [item, consumables] of this._consumable) {
            for (const [event, canConsume] of consumables) {
                const eventPrefix = event.split(':')[0];
                if (canConsume && eventGroup == eventPrefix) {
                    items.push({
                        event,
                        item: item.name || item.description
                    });
                }
            }
        }
        if (items.length) {
            /**
             * Some of the {@link module:engine/model/item~Item model items} were not consumed while downcasting the model to view.
             *
             * This might be the effect of:
             *
             * * A missing converter for some model elements. Make sure that you registered downcast converters for all model elements.
             * * A custom converter that does not consume converted items. Make sure that you
             * {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed} all model elements that you converted
             * from the model to the view.
             * * A custom converter that called `event.stop()`. When providing a custom converter, keep in mind that you should not stop
             * the event. If you stop it then the default converter at the `lowest` priority will not trigger the conversion of this node's
             * attributes and child nodes.
             *
             * @error conversion-model-consumable-not-consumed
             * @param {Array.<module:engine/model/item~Item>} items Items that were not consumed.
             */
            throw new CKEditorError('conversion-model-consumable-not-consumed', null, { items });
        }
    }
    /**
     * Gets a unique symbol for the passed {@link module:engine/model/textproxy~TextProxy} instance. All `TextProxy` instances that
     * have same parent, same start index and same end index will get the same symbol.
     *
     * Used internally to correctly consume `TextProxy` instances.
     *
     * @internal
     * @protected
     * @param {module:engine/model/textproxy~TextProxy} textProxy `TextProxy` instance to get a symbol for.
     * @returns {Symbol} Symbol representing all equal instances of `TextProxy`.
     */
    _getSymbolForTextProxy(textProxy) {
        let symbol = null;
        const startMap = this._textProxyRegistry.get(textProxy.startOffset);
        if (startMap) {
            const endMap = startMap.get(textProxy.endOffset);
            if (endMap) {
                symbol = endMap.get(textProxy.parent);
            }
        }
        if (!symbol) {
            symbol = this._addSymbolForTextProxy(textProxy);
        }
        return symbol;
    }
    /**
     * Adds a symbol for the given {@link module:engine/model/textproxy~TextProxy} instance.
     *
     * Used internally to correctly consume `TextProxy` instances.
     *
     * @private
     * @param {module:engine/model/textproxy~TextProxy} textProxy Text proxy instance.
     * @returns {Symbol} Symbol generated for given `TextProxy`.
     */
    _addSymbolForTextProxy(textProxy) {
        const start = textProxy.startOffset;
        const end = textProxy.endOffset;
        const parent = textProxy.parent;
        const symbol = Symbol('$textProxy:' + textProxy.data);
        let startMap;
        let endMap;
        startMap = this._textProxyRegistry.get(start);
        if (!startMap) {
            startMap = new Map();
            this._textProxyRegistry.set(start, startMap);
        }
        endMap = startMap.get(end);
        if (!endMap) {
            endMap = new Map();
            startMap.set(end, endMap);
        }
        endMap.set(parent, symbol);
        return symbol;
    }
}
// Returns a normalized consumable type name from the given string. A normalized consumable type name is a string that has
// at most one colon, for example: `insert` or `addMarker:highlight`. If a string to normalize has more "parts" (more colons),
// the further parts are dropped, for example: `addattribute:bold:$text` -> `addattributes:bold`.
//
// @param {String} type Consumable type.
// @returns {String} Normalized consumable type.
function _normalizeConsumableType(type) {
    const parts = type.split(':');
    // For inserts allow passing event name, it's stored in the context of a specified element so the element name is not needed.
    if (parts[0] == 'insert') {
        return parts[0];
    }
    // Markers are identified by the whole name (otherwise we would consume the whole markers group).
    if (parts[0] == 'addMarker' || parts[0] == 'removeMarker') {
        return type;
    }
    return parts.length > 1 ? parts[0] + ':' + parts[1] : parts[0];
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/downcastdispatcher.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/conversion/downcastdispatcher
 */



/**
 * The downcast dispatcher is a central point of downcasting (conversion from the model to the view), which is a process of reacting
 * to changes in the model and firing a set of events. The callbacks listening to these events are called converters. The
 * converters' role is to convert the model changes to changes in view (for example, adding view nodes or
 * changing attributes on view elements).
 *
 * During the conversion process, downcast dispatcher fires events basing on the state of the model and prepares
 * data for these events. It is important to understand that the events are connected with the changes done on the model,
 * for example: "a node has been inserted" or "an attribute has changed". This is in contrary to upcasting (a view-to-model conversion)
 * where you convert the view state (view nodes) to a model tree.
 *
 * The events are prepared basing on a diff created by the {@link module:engine/model/differ~Differ Differ}, which buffers them
 * and then passes to the downcast dispatcher as a diff between the old model state and the new model state.
 *
 * Note that because the changes are converted, there is a need to have a mapping between the model structure and the view structure.
 * To map positions and elements during the downcast (a model-to-view conversion), use {@link module:engine/conversion/mapper~Mapper}.
 *
 * Downcast dispatcher fires the following events for model tree changes:
 *
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert `insert`} &ndash;
 * If a range of nodes was inserted to the model tree.
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:remove `remove`} &ndash;
 * If a range of nodes was removed from the model tree.
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute`} &ndash;
 * If an attribute was added, changed or removed from a model node.
 *
 * For {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert `insert`}
 * and {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute`},
 * the downcast dispatcher generates {@link module:engine/conversion/modelconsumable~ModelConsumable consumables}.
 * These are used to have control over which changes have already been consumed. It is useful when some converters
 * overwrite others or convert multiple changes (for example, it converts an insertion of an element and also converts that
 * element's attributes during the insertion).
 *
 * Additionally, downcast dispatcher fires events for {@link module:engine/model/markercollection~Marker marker} changes:
 *
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker `addMarker`} &ndash; If a marker was added.
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:removeMarker `removeMarker`} &ndash; If a marker was
 * removed.
 *
 * Note that changing a marker is done through removing the marker from the old range and adding it to the new range,
 * so both of these events are fired.
 *
 * Finally, a downcast dispatcher also handles firing events for the {@link module:engine/model/selection model selection}
 * conversion:
 *
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:selection `selection`}
 * &ndash; Converts the selection from the model to the view.
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute`}
 * &ndash; Fired for every selection attribute.
 * * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker `addMarker`}
 * &ndash; Fired for every marker that contains a selection.
 *
 * Unlike the model tree and the markers, the events for selection are not fired for changes but for a selection state.
 *
 * When providing custom listeners for a downcast dispatcher, remember to check whether a given change has not been
 * {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed} yet.
 *
 * When providing custom listeners for a downcast dispatcher, keep in mind that you **should not** stop the event. If you stop it,
 * then the default converter at the `lowest` priority will not trigger the conversion of this node's attributes and child nodes.
 *
 * When providing custom listeners for a downcast dispatcher, remember to use the provided
 * {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer} to apply changes to the view document.
 *
 * You can read more about conversion in the following guide:
 *
 * * {@glink framework/guides/deep-dive/conversion/downcast Downcast conversion}
 *
 * An example of a custom converter for the downcast dispatcher:
 *
 *		// You will convert inserting a "paragraph" model element into the model.
 *		downcastDispatcher.on( 'insert:paragraph', ( evt, data, conversionApi ) => {
 *			// Remember to check whether the change has not been consumed yet and consume it.
 *			if ( !conversionApi.consumable.consume( data.item, 'insert' ) ) {
 *				return;
 *			}
 *
 *			// Translate the position in the model to a position in the view.
 *			const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
 *
 *			// Create a <p> element that will be inserted into the view at the `viewPosition`.
 *			const viewElement = conversionApi.writer.createContainerElement( 'p' );
 *
 *			// Bind the newly created view element to the model element so positions will map accordingly in the future.
 *			conversionApi.mapper.bindElements( data.item, viewElement );
 *
 *			// Add the newly created view element to the view.
 *			conversionApi.writer.insert( viewPosition, viewElement );
 *		} );
 */
class DowncastDispatcher extends Emitter {
    /**
     * Creates a downcast dispatcher instance.
     *
     * @see module:engine/conversion/downcastdispatcher~DowncastConversionApi
     * @param {Object} conversionApi Additional properties for an interface that will be passed to events fired
     * by the downcast dispatcher.
     */
    constructor(conversionApi) {
        super();
        /**
         * A template for an interface passed by the dispatcher to the event callbacks.
         *
         * @protected
         * @member {module:engine/conversion/downcastdispatcher~DowncastConversionApi}
         */
        this._conversionApi = { dispatcher: this, ...conversionApi };
        /**
         * A map of already fired events for a given `ModelConsumable`.
         *
         * @private
         * @member {WeakMap.<module:engine/conversion/downcastdispatcher~DowncastConversionApi,Map>}
         */
        this._firedEventsMap = new WeakMap();
    }
    /**
     * Converts changes buffered in the given {@link module:engine/model/differ~Differ model differ}
     * and fires conversion events based on it.
     *
     * @fires insert
     * @fires remove
     * @fires attribute
     * @fires addMarker
     * @fires removeMarker
     * @fires reduceChanges
     * @param {module:engine/model/differ~Differ} differ The differ object with buffered changes.
     * @param {module:engine/model/markercollection~MarkerCollection} markers Markers related to the model fragment to convert.
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer that should be used to modify the view document.
     */
    convertChanges(differ, markers, writer) {
        const conversionApi = this._createConversionApi(writer, differ.getRefreshedItems());
        // Before the view is updated, remove markers which have changed.
        for (const change of differ.getMarkersToRemove()) {
            this._convertMarkerRemove(change.name, change.range, conversionApi);
        }
        // Let features modify the change list (for example to allow reconversion).
        const changes = this._reduceChanges(differ.getChanges());
        // Convert changes that happened on model tree.
        for (const entry of changes) {
            if (entry.type === 'insert') {
                this._convertInsert(range_Range._createFromPositionAndShift(entry.position, entry.length), conversionApi);
            }
            else if (entry.type === 'reinsert') {
                this._convertReinsert(range_Range._createFromPositionAndShift(entry.position, entry.length), conversionApi);
            }
            else if (entry.type === 'remove') {
                this._convertRemove(entry.position, entry.length, entry.name, conversionApi);
            }
            else {
                // Defaults to 'attribute' change.
                this._convertAttribute(entry.range, entry.attributeKey, entry.attributeOldValue, entry.attributeNewValue, conversionApi);
            }
        }
        for (const markerName of conversionApi.mapper.flushUnboundMarkerNames()) {
            const markerRange = markers.get(markerName).getRange();
            this._convertMarkerRemove(markerName, markerRange, conversionApi);
            this._convertMarkerAdd(markerName, markerRange, conversionApi);
        }
        // After the view is updated, convert markers which have changed.
        for (const change of differ.getMarkersToAdd()) {
            this._convertMarkerAdd(change.name, change.range, conversionApi);
        }
        // Remove mappings for all removed view elements.
        conversionApi.mapper.flushDeferredBindings();
        // Verify if all insert consumables were consumed.
        conversionApi.consumable.verifyAllConsumed('insert');
    }
    /**
     * Starts a conversion of a model range and the provided markers.
     *
     * @fires insert
     * @fires attribute
     * @fires addMarker
     * @param {module:engine/model/range~Range} range The inserted range.
     * @param {Map<String,module:engine/model/range~Range>} markers The map of markers that should be down-casted.
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer that should be used to modify the view document.
     * @param {Object} [options] Optional options object passed to `convertionApi.options`.
     */
    convert(range, markers, writer, options = {}) {
        const conversionApi = this._createConversionApi(writer, undefined, options);
        this._convertInsert(range, conversionApi);
        for (const [name, range] of markers) {
            this._convertMarkerAdd(name, range, conversionApi);
        }
        // Verify if all insert consumables were consumed.
        conversionApi.consumable.verifyAllConsumed('insert');
    }
    /**
     * Starts the model selection conversion.
     *
     * Fires events for a given {@link module:engine/model/selection~Selection selection} to start the selection conversion.
     *
     * @fires selection
     * @fires addMarker
     * @fires attribute
     * @param {module:engine/model/selection~Selection} selection The selection to convert.
     * @param {module:engine/model/markercollection~MarkerCollection} markers Markers connected with the converted model.
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify the view document.
     */
    convertSelection(selection, markers, writer) {
        const markersAtSelection = Array.from(markers.getMarkersAtPosition(selection.getFirstPosition()));
        const conversionApi = this._createConversionApi(writer);
        this._addConsumablesForSelection(conversionApi.consumable, selection, markersAtSelection);
        this.fire('selection', { selection }, conversionApi);
        if (!selection.isCollapsed) {
            return;
        }
        for (const marker of markersAtSelection) {
            const markerRange = marker.getRange();
            if (!shouldMarkerChangeBeConverted(selection.getFirstPosition(), marker, conversionApi.mapper)) {
                continue;
            }
            const data = {
                item: selection,
                markerName: marker.name,
                markerRange
            };
            if (conversionApi.consumable.test(selection, 'addMarker:' + marker.name)) {
                this.fire(`addMarker:${marker.name}`, data, conversionApi);
            }
        }
        for (const key of selection.getAttributeKeys()) {
            const data = {
                item: selection,
                range: selection.getFirstRange(),
                attributeKey: key,
                attributeOldValue: null,
                attributeNewValue: selection.getAttribute(key)
            };
            // Do not fire event if the attribute has been consumed.
            if (conversionApi.consumable.test(selection, 'attribute:' + data.attributeKey)) {
                this.fire(`attribute:${data.attributeKey}:$text`, data, conversionApi);
            }
        }
    }
    /**
     * Fires insertion conversion of a range of nodes.
     *
     * For each node in the range, {@link #event:insert `insert` event is fired}. For each attribute on each node,
     * {@link #event:attribute `attribute` event is fired}.
     *
     * @protected
     * @fires insert
     * @fires attribute
     * @param {module:engine/model/range~Range} range The inserted range.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     * @param {Object} [options]
     * @param {Boolean} [options.doNotAddConsumables=false] Whether the ModelConsumable should not get populated
     * for items in the provided range.
     */
    _convertInsert(range, conversionApi, options = {}) {
        if (!options.doNotAddConsumables) {
            // Collect a list of things that can be consumed, consisting of nodes and their attributes.
            this._addConsumablesForInsert(conversionApi.consumable, Array.from(range));
        }
        // Fire a separate insert event for each node and text fragment contained in the range.
        for (const data of Array.from(range.getWalker({ shallow: true })).map(walkerValueToEventData)) {
            this._testAndFire('insert', data, conversionApi);
        }
    }
    /**
     * Fires conversion of a single node removal. Fires {@link #event:remove remove event} with provided data.
     *
     * @protected
     * @param {module:engine/model/position~Position} position Position from which node was removed.
     * @param {Number} length Offset size of removed node.
     * @param {String} name Name of removed node.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     */
    _convertRemove(position, length, name, conversionApi) {
        this.fire(`remove:${name}`, { position, length }, conversionApi);
    }
    /**
     * Starts a conversion of an attribute change on a given `range`.
     *
     * For each node in the given `range`, {@link #event:attribute attribute event} is fired with the passed data.
     *
     * @protected
     * @fires attribute
     * @param {module:engine/model/range~Range} range Changed range.
     * @param {String} key Key of the attribute that has changed.
     * @param {*} oldValue Attribute value before the change or `null` if the attribute has not been set before.
     * @param {*} newValue New attribute value or `null` if the attribute has been removed.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     */
    _convertAttribute(range, key, oldValue, newValue, conversionApi) {
        // Create a list with attributes to consume.
        this._addConsumablesForRange(conversionApi.consumable, range, `attribute:${key}`);
        // Create a separate attribute event for each node in the range.
        for (const value of range) {
            const data = {
                item: value.item,
                range: range_Range._createFromPositionAndShift(value.previousPosition, value.length),
                attributeKey: key,
                attributeOldValue: oldValue,
                attributeNewValue: newValue
            };
            this._testAndFire(`attribute:${key}`, data, conversionApi);
        }
    }
    /**
     * Fires re-insertion conversion (with a `reconversion` flag passed to `insert` events)
     * of a range of elements (only elements on the range depth, without children).
     *
     * For each node in the range on its depth (without children), {@link #event:insert `insert` event} is fired.
     * For each attribute on each node, {@link #event:attribute `attribute` event} is fired.
     *
     * @protected
     * @fires insert
     * @fires attribute
     * @param {module:engine/model/range~Range} range The range to reinsert.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     */
    _convertReinsert(range, conversionApi) {
        // Convert the elements - without converting children.
        const walkerValues = Array.from(range.getWalker({ shallow: true }));
        // Collect a list of things that can be consumed, consisting of nodes and their attributes.
        this._addConsumablesForInsert(conversionApi.consumable, walkerValues);
        // Fire a separate insert event for each node and text fragment contained shallowly in the range.
        for (const data of walkerValues.map(walkerValueToEventData)) {
            this._testAndFire('insert', { ...data, reconversion: true }, conversionApi);
        }
    }
    /**
     * Converts the added marker. Fires the {@link #event:addMarker `addMarker`} event for each item
     * in the marker's range. If the range is collapsed, a single event is dispatched. See the event description for more details.
     *
     * @protected
     * @fires addMarker
     * @param {String} markerName Marker name.
     * @param {module:engine/model/range~Range} markerRange The marker range.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     */
    _convertMarkerAdd(markerName, markerRange, conversionApi) {
        // Do not convert if range is in graveyard.
        if (markerRange.root.rootName == '$graveyard') {
            return;
        }
        // In markers' case, event name == consumable name.
        const eventName = `addMarker:${markerName}`;
        //
        // First, fire an event for the whole marker.
        //
        conversionApi.consumable.add(markerRange, eventName);
        this.fire(eventName, { markerName, markerRange }, conversionApi);
        //
        // Do not fire events for each item inside the range if the range got consumed.
        // Also consume the whole marker consumable if it wasn't consumed.
        //
        if (!conversionApi.consumable.consume(markerRange, eventName)) {
            return;
        }
        //
        // Then, fire an event for each item inside the marker range.
        //
        this._addConsumablesForRange(conversionApi.consumable, markerRange, eventName);
        for (const item of markerRange.getItems()) {
            // Do not fire event for already consumed items.
            if (!conversionApi.consumable.test(item, eventName)) {
                continue;
            }
            const data = { item, range: range_Range._createOn(item), markerName, markerRange };
            this.fire(eventName, data, conversionApi);
        }
    }
    /**
     * Fires the conversion of the marker removal. Fires the {@link #event:removeMarker `removeMarker`} event with the provided data.
     *
     * @protected
     * @fires removeMarker
     * @param {String} markerName Marker name.
     * @param {module:engine/model/range~Range} markerRange The marker range.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     */
    _convertMarkerRemove(markerName, markerRange, conversionApi) {
        // Do not convert if range is in graveyard.
        if (markerRange.root.rootName == '$graveyard') {
            return;
        }
        this.fire(`removeMarker:${markerName}`, { markerName, markerRange }, conversionApi);
    }
    /**
     * Fires the reduction of changes buffered in the {@link module:engine/model/differ~Differ `Differ`}.
     *
     * Features can replace selected {@link module:engine/model/differ~DiffItem `DiffItem`}s with `reinsert` entries to trigger
     * reconversion. The {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure
     * `DowncastHelpers.elementToStructure()`} is using this event to trigger reconversion.
     *
     * @private
     * @fires reduceChanges
     * @param {Iterable.<module:engine/model/differ~DiffItem>} changes
     * @returns {Iterable.<module:engine/model/differ~DiffItem>}
     */
    _reduceChanges(changes) {
        const data = { changes };
        this.fire('reduceChanges', data);
        return data.changes;
    }
    /**
     * Populates provided {@link module:engine/conversion/modelconsumable~ModelConsumable} with values to consume from a given range,
     * assuming that the range has just been inserted to the model.
     *
     * @private
     * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable The consumable.
     * @param {Iterable.<module:engine/model/treewalker~TreeWalkerValue>} walkerValues The walker values for the inserted range.
     * @returns {module:engine/conversion/modelconsumable~ModelConsumable} The values to consume.
     */
    _addConsumablesForInsert(consumable, walkerValues) {
        for (const value of walkerValues) {
            const item = value.item;
            // Add consumable if it wasn't there yet.
            if (consumable.test(item, 'insert') === null) {
                consumable.add(item, 'insert');
                for (const key of item.getAttributeKeys()) {
                    consumable.add(item, 'attribute:' + key);
                }
            }
        }
        return consumable;
    }
    /**
     * Populates provided {@link module:engine/conversion/modelconsumable~ModelConsumable} with values to consume for a given range.
     *
     * @private
     * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable The consumable.
     * @param {module:engine/model/range~Range} range The affected range.
     * @param {String} type Consumable type.
     * @returns {module:engine/conversion/modelconsumable~ModelConsumable} The values to consume.
     */
    _addConsumablesForRange(consumable, range, type) {
        for (const item of range.getItems()) {
            consumable.add(item, type);
        }
        return consumable;
    }
    /**
     * Populates provided {@link module:engine/conversion/modelconsumable~ModelConsumable} with selection consumable values.
     *
     * @private
     * @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable The consumable.
     * @param {module:engine/model/selection~Selection} selection The selection to create the consumable from.
     * @param {Iterable.<module:engine/model/markercollection~Marker>} markers Markers that contain the selection.
     * @returns {module:engine/conversion/modelconsumable~ModelConsumable} The values to consume.
     */
    _addConsumablesForSelection(consumable, selection, markers) {
        consumable.add(selection, 'selection');
        for (const marker of markers) {
            consumable.add(selection, 'addMarker:' + marker.name);
        }
        for (const key of selection.getAttributeKeys()) {
            consumable.add(selection, 'attribute:' + key);
        }
        return consumable;
    }
    /**
     * Tests whether given event wasn't already fired and if so, fires it.
     *
     * @private
     * @fires insert
     * @fires attribute
     * @param {String} type Event type.
     * @param {Object} data Event data.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     */
    _testAndFire(type, data, conversionApi) {
        const eventName = getEventName(type, data);
        const itemKey = data.item.is('$textProxy') ? conversionApi.consumable._getSymbolForTextProxy(data.item) : data.item;
        const eventsFiredForConversion = this._firedEventsMap.get(conversionApi);
        const eventsFiredForItem = eventsFiredForConversion.get(itemKey);
        if (!eventsFiredForItem) {
            eventsFiredForConversion.set(itemKey, new Set([eventName]));
        }
        else if (!eventsFiredForItem.has(eventName)) {
            eventsFiredForItem.add(eventName);
        }
        else {
            return;
        }
        this.fire(eventName, data, conversionApi);
    }
    /**
     * Fires not already fired events for setting attributes on just inserted item.
     *
     * @private
     * @param {module:engine/model/item~Item} item The model item to convert attributes for.
     * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi The conversion API object.
     */
    _testAndFireAddAttributes(item, conversionApi) {
        const data = {
            item,
            range: range_Range._createOn(item)
        };
        for (const key of data.item.getAttributeKeys()) {
            data.attributeKey = key;
            data.attributeOldValue = null;
            data.attributeNewValue = data.item.getAttribute(key);
            this._testAndFire(`attribute:${key}`, data, conversionApi);
        }
    }
    /**
     * Builds an instance of the {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi} from a template and a given
     * {@link module:engine/view/downcastwriter~DowncastWriter `DowncastWriter`} and options object.
     *
     * @private
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer View writer that should be used to modify the view document.
     * @param {Set.<module:engine/model/element~Element>} [refreshedItems] A set of model elements that should not reuse their
     * previous view representations.
     * @param {Object} [options] Optional options passed to `convertionApi.options`.
     * @return {module:engine/conversion/downcastdispatcher~DowncastConversionApi} The conversion API object.
     */
    _createConversionApi(writer, refreshedItems = new Set(), options = {}) {
        const conversionApi = {
            ...this._conversionApi,
            consumable: new ModelConsumable(),
            writer,
            options,
            convertItem: item => this._convertInsert(range_Range._createOn(item), conversionApi),
            convertChildren: element => this._convertInsert(range_Range._createIn(element), conversionApi, { doNotAddConsumables: true }),
            convertAttributes: item => this._testAndFireAddAttributes(item, conversionApi),
            canReuseView: viewElement => !refreshedItems.has(conversionApi.mapper.toModelElement(viewElement))
        };
        this._firedEventsMap.set(conversionApi, new Map());
        return conversionApi;
    }
}
// Helper function, checks whether change of `marker` at `modelPosition` should be converted. Marker changes are not
// converted if they happen inside an element with custom conversion method.
//
// @param {module:engine/model/position~Position} modelPosition
// @param {module:engine/model/markercollection~Marker} marker
// @param {module:engine/conversion/mapper~Mapper} mapper
// @returns {Boolean}
function shouldMarkerChangeBeConverted(modelPosition, marker, mapper) {
    const range = marker.getRange();
    const ancestors = Array.from(modelPosition.getAncestors());
    ancestors.shift(); // Remove root element. It cannot be passed to `model.Range#containsItem`.
    ancestors.reverse();
    const hasCustomHandling = ancestors.some(element => {
        if (range.containsItem(element)) {
            const viewElement = mapper.toViewElement(element);
            return !!viewElement.getCustomProperty('addHighlight');
        }
    });
    return !hasCustomHandling;
}
function getEventName(type, data) {
    const name = data.item.is('element') ? data.item.name : '$text';
    return `${type}:${name}`;
}
function walkerValueToEventData(value) {
    const item = value.item;
    const itemRange = range_Range._createFromPositionAndShift(value.previousPosition, value.length);
    return {
        item,
        range: itemRange
    };
}
/**
 * The {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} instance.
 *
 * @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher} #dispatcher
 */
/**
 * Stores the information about what parts of a processed model item are still waiting to be handled. After a piece of a model item was
 * converted, an appropriate consumable value should be {@link module:engine/conversion/modelconsumable~ModelConsumable#consume consumed}.
 *
 * @member {module:engine/conversion/modelconsumable~ModelConsumable} #consumable
 */
/**
 * The {@link module:engine/conversion/mapper~Mapper} instance.
 *
 * @member {module:engine/conversion/mapper~Mapper} #mapper
 */
/**
 * The {@link module:engine/model/schema~Schema} instance set for the model that is downcast.
 *
 * @member {module:engine/model/schema~Schema} #schema
 */
/**
 * The {@link module:engine/view/downcastwriter~DowncastWriter} instance used to manipulate the data during conversion.
 *
 * @member {module:engine/view/downcastwriter~DowncastWriter} #writer
 */
/**
 * Triggers conversion of a specified item.
 * This conversion is triggered within (as a separate process of) the parent conversion.
 *
 * @method #convertItem
 * @param {module:engine/model/item~Item} item The model item to trigger nested insert conversion on.
 */
/**
 * Triggers conversion of children of a specified element.
 *
 * @method #convertChildren
 * @param {module:engine/model/element~Element} element The model element to trigger children insert conversion on.
 */
/**
 * Triggers conversion of attributes of a specified item.
 *
 * @method #convertAttributes
 * @param {module:engine/model/item~Item} item The model item to trigger attribute conversion on.
 */
/**
 * An object with an additional configuration which can be used during the conversion process. Available only for data downcast conversion.
 *
 * @member {Object} #options
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/selection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/model/selection
 */







/**
 * Selection is a set of {@link module:engine/model/range~Range ranges}. It has a direction specified by its
 * {@link module:engine/model/selection~Selection#anchor anchor} and {@link module:engine/model/selection~Selection#focus focus}
 * (it can be {@link module:engine/model/selection~Selection#isBackward forward or backward}).
 * Additionally, selection may have its own attributes (think – whether text typed in in this selection
 * should have those attributes – e.g. whether you type a bolded text).
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class selection_Selection extends EmitterMixin(typecheckable_TypeCheckable) {
    /**
     * Creates a new selection instance based on the given {@link module:engine/model/selection~Selectable selectable}
     * or creates an empty selection if no arguments were passed.
     *
     *		// Creates empty selection without ranges.
     *		const selection = writer.createSelection();
     *
     *		// Creates selection at the given range.
     *		const range = writer.createRange( start, end );
     *		const selection = writer.createSelection( range );
     *
     *		// Creates selection at the given ranges
     *		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		const selection = writer.createSelection( ranges );
     *
     *		// Creates selection from the other selection.
     *		// Note: It doesn't copies selection attributes.
     *		const otherSelection = writer.createSelection();
     *		const selection = writer.createSelection( otherSelection );
     *
     *		// Creates selection from the given document selection.
     *		// Note: It doesn't copies selection attributes.
     *		const documentSelection = model.document.selection;
     *		const selection = writer.createSelection( documentSelection );
     *
     *		// Creates selection at the given position.
     *		const position = writer.createPositionFromPath( root, path );
     *		const selection = writer.createSelection( position );
     *
     *		// Creates selection at the given offset in the given element.
     *		const paragraph = writer.createElement( 'paragraph' );
     *		const selection = writer.createSelection( paragraph, offset );
     *
     *		// Creates a range inside an {@link module:engine/model/element~Element element} which starts before the
     *		// first child of that element and ends after the last child of that element.
     *		const selection = writer.createSelection( paragraph, 'in' );
     *
     *		// Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends
     *		// just after the item.
     *		const selection = writer.createSelection( paragraph, 'on' );
     *
     * Selection's constructor allow passing additional options (`'backward'`) as the last argument.
     *
     *		// Creates backward selection.
     *		const selection = writer.createSelection( range, { backward: true } );
     *
     * @param {module:engine/model/selection~Selectable} [selectable]
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     */
    constructor(...args) {
        super();
        /**
         * Specifies whether the last added range was added as a backward or forward range.
         *
         * @private
         * @type {Boolean}
         */
        this._lastRangeBackward = false;
        /**
         * Stores selection ranges.
         *
         * @protected
         * @type {Array.<module:engine/model/range~Range>}
         */
        this._ranges = [];
        /**
         * List of attributes set on current selection.
         *
         * @protected
         * @type {Map.<String,*>}
         */
        this._attrs = new Map();
        if (args.length) {
            this.setTo(...args);
        }
    }
    /**
     * Selection anchor. Anchor is the position from which the selection was started. If a user is making a selection
     * by dragging the mouse, the anchor is where the user pressed the mouse button (the beginning of the selection).
     *
     * Anchor and {@link #focus} define the direction of the selection, which is important
     * when expanding/shrinking selection. The focus moves, while the anchor should remain in the same place.
     *
     * Anchor is always set to the {@link module:engine/model/range~Range#start start} or
     * {@link module:engine/model/range~Range#end end} position of the last of selection's ranges. Whether it is
     * the `start` or `end` depends on the specified `options.backward`. See the {@link #setTo `setTo()`} method.
     *
     * May be set to `null` if there are no ranges in the selection.
     *
     * @see #focus
     * @readonly
     * @type {module:engine/model/position~Position|null}
     */
    get anchor() {
        if (this._ranges.length > 0) {
            const range = this._ranges[this._ranges.length - 1];
            return this._lastRangeBackward ? range.end : range.start;
        }
        return null;
    }
    /**
     * Selection focus. Focus is the position where the selection ends. If a user is making a selection
     * by dragging the mouse, the focus is where the mouse cursor is.
     *
     * May be set to `null` if there are no ranges in the selection.
     *
     * @see #anchor
     * @readonly
     * @type {module:engine/model/position~Position|null}
     */
    get focus() {
        if (this._ranges.length > 0) {
            const range = this._ranges[this._ranges.length - 1];
            return this._lastRangeBackward ? range.start : range.end;
        }
        return null;
    }
    /**
     * Whether the selection is collapsed. Selection is collapsed when there is exactly one range in it
     * and it is collapsed.
     *
     * @readonly
     * @type {Boolean}
     */
    get isCollapsed() {
        const length = this._ranges.length;
        if (length === 1) {
            return this._ranges[0].isCollapsed;
        }
        else {
            return false;
        }
    }
    /**
     * Returns the number of ranges in the selection.
     *
     * @readonly
     * @type {Number}
     */
    get rangeCount() {
        return this._ranges.length;
    }
    /**
     * Specifies whether the selection's {@link #focus} precedes the selection's {@link #anchor}.
     *
     * @readonly
     * @type {Boolean}
     */
    get isBackward() {
        return !this.isCollapsed && this._lastRangeBackward;
    }
    /**
     * Checks whether this selection is equal to the given selection. Selections are equal if they have the same directions,
     * the same number of ranges and all ranges from one selection equal to ranges from the another selection.
     *
     * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} otherSelection
     * Selection to compare with.
     * @returns {Boolean} `true` if selections are equal, `false` otherwise.
     */
    isEqual(otherSelection) {
        if (this.rangeCount != otherSelection.rangeCount) {
            return false;
        }
        else if (this.rangeCount === 0) {
            return true;
        }
        if (!this.anchor.isEqual(otherSelection.anchor) || !this.focus.isEqual(otherSelection.focus)) {
            return false;
        }
        for (const thisRange of this._ranges) {
            let found = false;
            for (const otherRange of otherSelection._ranges) {
                if (thisRange.isEqual(otherRange)) {
                    found = true;
                    break;
                }
            }
            if (!found) {
                return false;
            }
        }
        return true;
    }
    /**
     * Returns an iterable object that iterates over copies of selection ranges.
     *
     * @returns {Iterable.<module:engine/model/range~Range>}
     */
    *getRanges() {
        for (const range of this._ranges) {
            yield new range_Range(range.start, range.end);
        }
    }
    /**
     * Returns a copy of the first range in the selection.
     * First range is the one which {@link module:engine/model/range~Range#start start} position
     * {@link module:engine/model/position~Position#isBefore is before} start position of all other ranges
     * (not to confuse with the first range added to the selection).
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/range~Range|null}
     */
    getFirstRange() {
        let first = null;
        for (const range of this._ranges) {
            if (!first || range.start.isBefore(first.start)) {
                first = range;
            }
        }
        return first ? new range_Range(first.start, first.end) : null;
    }
    /**
     * Returns a copy of the last range in the selection.
     * Last range is the one which {@link module:engine/model/range~Range#end end} position
     * {@link module:engine/model/position~Position#isAfter is after} end position of all other ranges (not to confuse with the range most
     * recently added to the selection).
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/range~Range|null}
     */
    getLastRange() {
        let last = null;
        for (const range of this._ranges) {
            if (!last || range.end.isAfter(last.end)) {
                last = range;
            }
        }
        return last ? new range_Range(last.start, last.end) : null;
    }
    /**
     * Returns the first position in the selection.
     * First position is the position that {@link module:engine/model/position~Position#isBefore is before}
     * any other position in the selection.
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/position~Position|null}
     */
    getFirstPosition() {
        const first = this.getFirstRange();
        return first ? first.start.clone() : null;
    }
    /**
     * Returns the last position in the selection.
     * Last position is the position that {@link module:engine/model/position~Position#isAfter is after}
     * any other position in the selection.
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/position~Position|null}
     */
    getLastPosition() {
        const lastRange = this.getLastRange();
        return lastRange ? lastRange.end.clone() : null;
    }
    /**
     * Sets this selection's ranges and direction to the specified location based on the given
     * {@link module:engine/model/selection~Selectable selectable}.
     *
     *		// Removes all selection's ranges.
     *		selection.setTo( null );
     *
     *		// Sets selection to the given range.
     *		const range = writer.createRange( start, end );
     *		selection.setTo( range );
     *
     *		// Sets selection to given ranges.
     *		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		selection.setTo( ranges );
     *
     *		// Sets selection to other selection.
     *		// Note: It doesn't copies selection attributes.
     *		const otherSelection = writer.createSelection();
     *		selection.setTo( otherSelection );
     *
     *		// Sets selection to the given document selection.
     *		// Note: It doesn't copies selection attributes.
     *		const documentSelection = new DocumentSelection( doc );
     *		selection.setTo( documentSelection );
     *
     *		// Sets collapsed selection at the given position.
     *		const position = writer.createPositionFromPath( root, path );
     *		selection.setTo( position );
     *
     *		// Sets collapsed selection at the position of the given node and an offset.
     *		selection.setTo( paragraph, offset );
     *
     * Creates a range inside an {@link module:engine/model/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     *		selection.setTo( paragraph, 'in' );
     *
     * Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends just after the item.
     *
     *		selection.setTo( paragraph, 'on' );
     *
     * `Selection#setTo()`' method allow passing additional options (`backward`) as the last argument.
     *
     *		// Sets backward selection.
     *		const selection = writer.createSelection( range, { backward: true } );
     *
     * @param {module:engine/model/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     */
    setTo(...args) {
        let [selectable, placeOrOffset, options] = args;
        if (typeof placeOrOffset == 'object') {
            options = placeOrOffset;
            placeOrOffset = undefined;
        }
        if (selectable === null) {
            this._setRanges([]);
        }
        else if (selectable instanceof selection_Selection) {
            this._setRanges(selectable.getRanges(), selectable.isBackward);
        }
        else if (selectable && typeof selectable.getRanges == 'function') {
            // We assume that the selectable is a DocumentSelection.
            // It can't be imported here, because it would lead to circular imports.
            this._setRanges(selectable.getRanges(), selectable.isBackward);
        }
        else if (selectable instanceof range_Range) {
            this._setRanges([selectable], !!options && !!options.backward);
        }
        else if (selectable instanceof position_Position) {
            this._setRanges([new range_Range(selectable)]);
        }
        else if (selectable instanceof model_node_Node) {
            const backward = !!options && !!options.backward;
            let range;
            if (placeOrOffset == 'in') {
                range = range_Range._createIn(selectable);
            }
            else if (placeOrOffset == 'on') {
                range = range_Range._createOn(selectable);
            }
            else if (placeOrOffset !== undefined) {
                range = new range_Range(position_Position._createAt(selectable, placeOrOffset));
            }
            else {
                /**
                 * selection.setTo requires the second parameter when the first parameter is a node.
                 *
                 * @error model-selection-setto-required-second-parameter
                 */
                throw new CKEditorError('model-selection-setto-required-second-parameter', [this, selectable]);
            }
            this._setRanges([range], backward);
        }
        else if (isIterable(selectable)) {
            // We assume that the selectable is an iterable of ranges.
            this._setRanges(selectable, options && !!options.backward);
        }
        else {
            /**
             * Cannot set the selection to the given place.
             *
             * Invalid parameters were specified when setting the selection. Common issues:
             *
             * * A {@link module:engine/model/textproxy~TextProxy} instance was passed instead of
             * a real {@link module:engine/model/text~Text}.
             * * View nodes were passed instead of model nodes.
             * * `null`/`undefined` was passed.
             *
             * @error model-selection-setto-not-selectable
             */
            throw new CKEditorError('model-selection-setto-not-selectable', [this, selectable]);
        }
    }
    /**
     * Replaces all ranges that were added to the selection with given array of ranges. Last range of the array
     * is treated like the last added range and is used to set {@link module:engine/model/selection~Selection#anchor} and
     * {@link module:engine/model/selection~Selection#focus}. Accepts a flag describing in which direction the selection is made.
     *
     * @protected
     * @fires change:range
     * @param {Iterable.<module:engine/model/range~Range>} newRanges Ranges to set.
     * @param {Boolean} [isLastBackward=false] Flag describing if last added range was selected forward - from start to end (`false`)
     * or backward - from end to start (`true`).
     */
    _setRanges(newRanges, isLastBackward = false) {
        const ranges = Array.from(newRanges);
        // Check whether there is any range in new ranges set that is different than all already added ranges.
        const anyNewRange = ranges.some(newRange => {
            if (!(newRange instanceof range_Range)) {
                /**
                 * Selection range set to an object that is not an instance of {@link module:engine/model/range~Range}.
                 *
                 * Only {@link module:engine/model/range~Range} instances can be used to set a selection.
                 * Common mistakes leading to this error are:
                 *
                 * * using DOM `Range` object,
                 * * incorrect CKEditor 5 installation with multiple `ckeditor5-engine` packages having different versions.
                 *
                 * @error model-selection-set-ranges-not-range
                 */
                throw new CKEditorError('model-selection-set-ranges-not-range', [this, newRanges]);
            }
            return this._ranges.every(oldRange => {
                return !oldRange.isEqual(newRange);
            });
        });
        // Don't do anything if nothing changed.
        if (ranges.length === this._ranges.length && !anyNewRange) {
            return;
        }
        this._replaceAllRanges(ranges);
        this._lastRangeBackward = !!isLastBackward;
        this.fire('change:range', { directChange: true });
    }
    /**
     * Moves {@link module:engine/model/selection~Selection#focus} to the specified location.
     *
     * The location can be specified in the same form as
     * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.
     *
     * @fires change:range
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/model/item~Item model item}.
     */
    setFocus(itemOrPosition, offset) {
        if (this.anchor === null) {
            /**
             * Cannot set selection focus if there are no ranges in selection.
             *
             * @error model-selection-setfocus-no-ranges
             */
            throw new CKEditorError('model-selection-setfocus-no-ranges', [this, itemOrPosition]);
        }
        const newFocus = position_Position._createAt(itemOrPosition, offset);
        if (newFocus.compareWith(this.focus) == 'same') {
            return;
        }
        const anchor = this.anchor;
        if (this._ranges.length) {
            this._popRange();
        }
        if (newFocus.compareWith(anchor) == 'before') {
            this._pushRange(new range_Range(newFocus, anchor));
            this._lastRangeBackward = true;
        }
        else {
            this._pushRange(new range_Range(anchor, newFocus));
            this._lastRangeBackward = false;
        }
        this.fire('change:range', { directChange: true });
    }
    /**
     * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.
     *
     * @param {String} key Key of attribute to look for.
     * @returns {*} Attribute value or `undefined`.
     */
    getAttribute(key) {
        return this._attrs.get(key);
    }
    /**
     * Returns iterable that iterates over this selection's attributes.
     *
     * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
     * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
     *
     * @returns {Iterable.<*>}
     */
    getAttributes() {
        return this._attrs.entries();
    }
    /**
     * Returns iterable that iterates over this selection's attribute keys.
     *
     * @returns {Iterable.<String>}
     */
    getAttributeKeys() {
        return this._attrs.keys();
    }
    /**
     * Checks if the selection has an attribute for given key.
     *
     * @param {String} key Key of attribute to check.
     * @returns {Boolean} `true` if attribute with given key is set on selection, `false` otherwise.
     */
    hasAttribute(key) {
        return this._attrs.has(key);
    }
    /**
     * Removes an attribute with given key from the selection.
     *
     * If given attribute was set on the selection, fires the {@link #event:change:range} event with
     * removed attribute key.
     *
     * @fires change:attribute
     * @param {String} key Key of attribute to remove.
     */
    removeAttribute(key) {
        if (this.hasAttribute(key)) {
            this._attrs.delete(key);
            this.fire('change:attribute', { attributeKeys: [key], directChange: true });
        }
    }
    /**
     * Sets attribute on the selection. If attribute with the same key already is set, it's value is overwritten.
     *
     * If the attribute value has changed, fires the {@link #event:change:range} event with
     * the attribute key.
     *
     * @fires change:attribute
     * @param {String} key Key of attribute to set.
     * @param {*} value Attribute value.
     */
    setAttribute(key, value) {
        if (this.getAttribute(key) !== value) {
            this._attrs.set(key, value);
            this.fire('change:attribute', { attributeKeys: [key], directChange: true });
        }
    }
    /**
     * Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only
     * one range in the selection, and that range contains exactly one element.
     * Returns `null` if there is no selected element.
     *
     * @returns {module:engine/model/element~Element|null}
     */
    getSelectedElement() {
        if (this.rangeCount !== 1) {
            return null;
        }
        return this.getFirstRange().getContainedElement();
    }
    /**
     * Gets elements of type {@link module:engine/model/schema~Schema#isBlock "block"} touched by the selection.
     *
     * This method's result can be used for example to apply block styling to all blocks covered by this selection.
     *
     * **Note:** `getSelectedBlocks()` returns blocks that are nested in other non-block elements
     * but will not return blocks nested in other blocks.
     *
     * In this case the function will return exactly all 3 paragraphs (note: `<blockQuote>` is not a block itself):
     *
     *		<paragraph>[a</paragraph>
     *		<blockQuote>
     *			<paragraph>b</paragraph>
     *		</blockQuote>
     *		<paragraph>c]d</paragraph>
     *
     * In this case the paragraph will also be returned, despite the collapsed selection:
     *
     *		<paragraph>[]a</paragraph>
     *
     * In such a scenario, however, only blocks A, B & E will be returned as blocks C & D are nested in block B:
     *
     *		[<blockA></blockA>
     *		<blockB>
     *			<blockC></blockC>
     *			<blockD></blockD>
     *		</blockB>
     *		<blockE></blockE>]
     *
     * If the selection is inside a block all the inner blocks (A & B) are returned:
     *
     * 		<block>
     *			<blockA>[a</blockA>
     * 			<blockB>b]</blockB>
     * 		</block>
     *
     * **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective
     * this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.
     *
     *		<paragraph>[a</paragraph>
     *		<paragraph>b</paragraph>
     *		<paragraph>]c</paragraph> // this block will not be returned
     *
     * @returns {Iterable.<module:engine/model/element~Element>}
     */
    *getSelectedBlocks() {
        const visited = new WeakSet();
        for (const range of this.getRanges()) {
            // Get start block of range in case of a collapsed range.
            const startBlock = getParentBlock(range.start, visited);
            if (startBlock && isTopBlockInRange(startBlock, range)) {
                yield startBlock;
            }
            for (const value of range.getWalker()) {
                const block = value.item;
                if (value.type == 'elementEnd' && isUnvisitedTopBlock(block, visited, range)) {
                    yield block;
                }
            }
            const endBlock = getParentBlock(range.end, visited);
            // #984. Don't return the end block if the range ends right at its beginning.
            if (endBlock && !range.end.isTouching(position_Position._createAt(endBlock, 0)) && isTopBlockInRange(endBlock, range)) {
                yield endBlock;
            }
        }
    }
    /**
     * Checks whether the selection contains the entire content of the given element. This means that selection must start
     * at a position {@link module:engine/model/position~Position#isTouching touching} the element's start and ends at position
     * touching the element's end.
     *
     * By default, this method will check whether the entire content of the selection's current root is selected.
     * Useful to check if e.g. the user has just pressed <kbd>Ctrl</kbd> + <kbd>A</kbd>.
     *
     * @param {module:engine/model/element~Element} [element=this.anchor.root]
     * @returns {Boolean}
     */
    containsEntireContent(element = this.anchor.root) {
        const limitStartPosition = position_Position._createAt(element, 0);
        const limitEndPosition = position_Position._createAt(element, 'end');
        return limitStartPosition.isTouching(this.getFirstPosition()) &&
            limitEndPosition.isTouching(this.getLastPosition());
    }
    /**
     * Adds given range to internal {@link #_ranges ranges array}. Throws an error
     * if given range is intersecting with any range that is already stored in this selection.
     *
     * @protected
     * @param {module:engine/model/range~Range} range Range to add.
     */
    _pushRange(range) {
        this._checkRange(range);
        this._ranges.push(new range_Range(range.start, range.end));
    }
    /**
     * Checks if given range intersects with ranges that are already in the selection. Throws an error if it does.
     *
     * @protected
     * @param {module:engine/model/range~Range} range Range to check.
     */
    _checkRange(range) {
        for (let i = 0; i < this._ranges.length; i++) {
            if (range.isIntersecting(this._ranges[i])) {
                /**
                 * Trying to add a range that intersects with another range in the selection.
                 *
                 * @error model-selection-range-intersects
                 * @param {module:engine/model/range~Range} addedRange Range that was added to the selection.
                 * @param {module:engine/model/range~Range} intersectingRange Range in the selection that intersects with `addedRange`.
                 */
                throw new CKEditorError('model-selection-range-intersects', [this, range], { addedRange: range, intersectingRange: this._ranges[i] });
            }
        }
    }
    /**
     * Replaces all the ranges by the given ones.
     * Uses {@link #_popRange _popRange} and {@link #_pushRange _pushRange} to ensure proper ranges removal and addition.
     *
     * @param {Array.<module:engine/model/range~Range>} ranges
     * @protected
     */
    _replaceAllRanges(ranges) {
        this._removeAllRanges();
        for (const range of ranges) {
            this._pushRange(range);
        }
    }
    /**
     * Deletes ranges from internal range array. Uses {@link #_popRange _popRange} to
     * ensure proper ranges removal.
     *
     * @protected
     */
    _removeAllRanges() {
        while (this._ranges.length > 0) {
            this._popRange();
        }
    }
    /**
     * Removes most recently added range from the selection.
     *
     * @protected
     */
    _popRange() {
        this._ranges.pop();
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		selection.is( 'selection' ); // -> true
 *		selection.is( 'model:selection' ); // -> true
 *
 *		selection.is( 'view:selection' ); // -> false
 *		selection.is( 'range' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
selection_Selection.prototype.is = function (type) {
    return type === 'selection' || type === 'model:selection';
};
// Checks whether the given element extends $block in the schema and has a parent (is not a root).
// Marks it as already visited.
function isUnvisitedBlock(element, visited) {
    if (visited.has(element)) {
        return false;
    }
    visited.add(element);
    return element.root.document.model.schema.isBlock(element) && !!element.parent;
}
// Checks if the given element is a $block was not previously visited and is a top block in a range.
function isUnvisitedTopBlock(element, visited, range) {
    return isUnvisitedBlock(element, visited) && isTopBlockInRange(element, range);
}
// Finds the lowest element in position's ancestors which is a block.
// It will search until first ancestor that is a limit element.
// Marks all ancestors as already visited to not include any of them later on.
function getParentBlock(position, visited) {
    const element = position.parent;
    const schema = element.root.document.model.schema;
    const ancestors = position.parent.getAncestors({ parentFirst: true, includeSelf: true });
    let hasParentLimit = false;
    const block = ancestors.find((element) => {
        // Stop searching after first parent node that is limit element.
        if (hasParentLimit) {
            return false;
        }
        hasParentLimit = schema.isLimit(element);
        return !hasParentLimit && isUnvisitedBlock(element, visited);
    });
    // Mark all ancestors of this position's parent, because find() might've stopped early and
    // the found block may be a child of another block.
    ancestors.forEach(element => visited.add(element));
    return block;
}
// Checks if the blocks is not nested in other block inside a range.
//
// @param {module:engine/model/element~Element} block Block to check.
// @param {module:engine/model/range~Range} range Range to check.
function isTopBlockInRange(block, range) {
    const parentBlock = findAncestorBlock(block);
    if (!parentBlock) {
        return true;
    }
    // Add loose flag to check as parentRange can be equal to range.
    const isParentInRange = range.containsRange(range_Range._createOn(parentBlock), true);
    return !isParentInRange;
}
// Returns first ancestor block of a node.
//
// @param {module:engine/model/node~Node} node
// @returns {module:engine/model/node~Node|undefined}
function findAncestorBlock(node) {
    const schema = node.root.document.model.schema;
    let parent = node.parent;
    while (parent) {
        if (schema.isBlock(parent)) {
            return parent;
        }
        parent = parent.parent;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/liverange.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/model/liverange
 */


/**
 * `LiveRange` is a type of {@link module:engine/model/range~Range Range}
 * that updates itself as {@link module:engine/model/document~Document document}
 * is changed through operations. It may be used as a bookmark.
 *
 * **Note:** Be very careful when dealing with `LiveRange`. Each `LiveRange` instance bind events that might
 * have to be unbound. Use {@link module:engine/model/liverange~LiveRange#detach detach} whenever you don't need `LiveRange` anymore.
 */
class LiveRange extends EmitterMixin(range_Range) {
    /**
     * Creates a live range.
     *
     * @see module:engine/model/range~Range
     */
    constructor(start, end) {
        super(start, end);
        bindWithDocument.call(this);
    }
    /**
     * Unbinds all events previously bound by `LiveRange`. Use it whenever you don't need `LiveRange` instance
     * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
     * referring to it).
     */
    detach() {
        this.stopListening();
    }
    /**
     * Creates a {@link module:engine/model/range~Range range instance} that is equal to this live range.
     *
     * @returns {module:engine/model/range~Range}
     */
    toRange() {
        return new range_Range(this.start, this.end);
    }
    /**
     * Creates a `LiveRange` instance that is equal to the given range.
     *
     * @param {module:engine/model/range~Range} range
     * @returns {module:engine/model/liverange~LiveRange}
     */
    static fromRange(range) {
        return new LiveRange(range.start, range.end);
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		liveRange.is( 'range' ); // -> true
 *		liveRange.is( 'model:range' ); // -> true
 *		liveRange.is( 'liveRange' ); // -> true
 *		liveRange.is( 'model:liveRange' ); // -> true
 *
 *		liveRange.is( 'view:range' ); // -> false
 *		liveRange.is( 'documentSelection' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
LiveRange.prototype.is = function (type) {
    return type === 'liveRange' || type === 'model:liveRange' ||
        // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
        type == 'range' || type === 'model:range';
};
// Binds this `LiveRange` to the {@link module:engine/model/document~Document document}
// that owns this range's {@link module:engine/model/range~Range#root root}.
//
// @private
function bindWithDocument() {
    this.listenTo(this.root.document.model, 'applyOperation', (event, args) => {
        const operation = args[0];
        if (!operation.isDocumentOperation) {
            return;
        }
        transform.call(this, operation);
    }, { priority: 'low' });
}
// Updates this range accordingly to the updates applied to the model. Bases on change events.
//
// @private
// @param {module:engine/model/operation/operation~Operation} operation Executed operation.
function transform(operation) {
    // Transform the range by the operation. Join the result ranges if needed.
    const ranges = this.getTransformedByOperation(operation);
    const result = range_Range._createFromRanges(ranges);
    const boundariesChanged = !result.isEqual(this);
    const contentChanged = doesOperationChangeRangeContent(this, operation);
    let deletionPosition = null;
    if (boundariesChanged) {
        // If range boundaries have changed, fire `change:range` event.
        //
        if (result.root.rootName == '$graveyard') {
            // If the range was moved to the graveyard root, set `deletionPosition`.
            if (operation.type == 'remove') {
                deletionPosition = operation.sourcePosition;
            }
            else {
                // Merge operation.
                deletionPosition = operation.deletionPosition;
            }
        }
        const oldRange = this.toRange();
        this.start = result.start;
        this.end = result.end;
        this.fire('change:range', oldRange, { deletionPosition });
    }
    else if (contentChanged) {
        // If range boundaries have not changed, but there was change inside the range, fire `change:content` event.
        this.fire('change:content', this.toRange(), { deletionPosition });
    }
}
// Checks whether given operation changes something inside the range (even if it does not change boundaries).
//
// @private
// @param {module:engine/model/range~Range} range Range to check.
// @param {module:engine/model/operation/operation~Operation} operation Executed operation.
// @returns {Boolean}
function doesOperationChangeRangeContent(range, operation) {
    switch (operation.type) {
        case 'insert':
            return range.containsPosition(operation.position);
        case 'move':
        case 'remove':
        case 'reinsert':
        case 'merge':
            return range.containsPosition(operation.sourcePosition) ||
                range.start.isEqual(operation.sourcePosition) ||
                range.containsPosition(operation.targetPosition);
        case 'split':
            return range.containsPosition(operation.splitPosition) || range.containsPosition(operation.insertionPosition);
    }
    return false;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/documentselection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/model/documentselection
 */










const storePrefix = 'selection:';
/**
 * `DocumentSelection` is a special selection which is used as the
 * {@link module:engine/model/document~Document#selection document's selection}.
 * There can be only one instance of `DocumentSelection` per document.
 *
 * Document selection can only be changed by using the {@link module:engine/model/writer~Writer} instance
 * inside the {@link module:engine/model/model~Model#change `change()`} block, as it provides a secure way to modify model.
 *
 * `DocumentSelection` is automatically updated upon changes in the {@link module:engine/model/document~Document document}
 * to always contain valid ranges. Its attributes are inherited from the text unless set explicitly.
 *
 * Differences between {@link module:engine/model/selection~Selection} and `DocumentSelection` are:
 * * there is always a range in `DocumentSelection` - even if no ranges were added there is a "default range"
 * present in the selection,
 * * ranges added to this selection updates automatically when the document changes,
 * * attributes of `DocumentSelection` are updated automatically according to selection ranges.
 *
 * Since `DocumentSelection` uses {@link module:engine/model/liverange~LiveRange live ranges}
 * and is updated when {@link module:engine/model/document~Document document}
 * changes, it cannot be set on {@link module:engine/model/node~Node nodes}
 * that are inside {@link module:engine/model/documentfragment~DocumentFragment document fragment}.
 * If you need to represent a selection in document fragment,
 * use {@link module:engine/model/selection~Selection Selection class} instead.
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class documentselection_DocumentSelection extends EmitterMixin(typecheckable_TypeCheckable) {
    /**
     * Creates an empty live selection for given {@link module:engine/model/document~Document}.
     *
     * @param {module:engine/model/document~Document} doc Document which owns this selection.
     */
    constructor(doc) {
        super();
        /**
         * Selection used internally by that class (`DocumentSelection` is a proxy to that selection).
         *
         * @protected
         */
        this._selection = new LiveSelection(doc);
        this._selection.delegate('change:range').to(this);
        this._selection.delegate('change:attribute').to(this);
        this._selection.delegate('change:marker').to(this);
    }
    /**
     * Returns whether the selection is collapsed. Selection is collapsed when there is exactly one range which is
     * collapsed.
     *
     * @readonly
     * @type {Boolean}
     */
    get isCollapsed() {
        return this._selection.isCollapsed;
    }
    /**
     * Selection anchor. Anchor may be described as a position where the most recent part of the selection starts.
     * Together with {@link #focus} they define the direction of selection, which is important
     * when expanding/shrinking selection. Anchor is always {@link module:engine/model/range~Range#start start} or
     * {@link module:engine/model/range~Range#end end} position of the most recently added range.
     *
     * Is set to `null` if there are no ranges in selection.
     *
     * @see #focus
     * @readonly
     * @type {module:engine/model/position~Position|null}
     */
    get anchor() {
        return this._selection.anchor;
    }
    /**
     * Selection focus. Focus is a position where the selection ends.
     *
     * Is set to `null` if there are no ranges in selection.
     *
     * @see #anchor
     * @readonly
     * @type {module:engine/model/position~Position|null}
     */
    get focus() {
        return this._selection.focus;
    }
    /**
     * Returns number of ranges in selection.
     *
     * @readonly
     * @type {Number}
     */
    get rangeCount() {
        return this._selection.rangeCount;
    }
    /**
     * Describes whether `Documentselection` has own range(s) set, or if it is defaulted to
     * {@link module:engine/model/document~Document#_getDefaultRange document's default range}.
     *
     * @readonly
     * @type {Boolean}
     */
    get hasOwnRange() {
        return this._selection.hasOwnRange;
    }
    /**
     * Specifies whether the {@link #focus}
     * precedes {@link #anchor}.
     *
     * @readonly
     * @type {Boolean}
     */
    get isBackward() {
        return this._selection.isBackward;
    }
    /**
     * Describes whether the gravity is overridden (using {@link module:engine/model/writer~Writer#overrideSelectionGravity}) or not.
     *
     * Note that the gravity remains overridden as long as will not be restored the same number of times as it was overridden.
     *
     * @readonly
     * @returns {Boolean}
     */
    get isGravityOverridden() {
        return this._selection.isGravityOverridden;
    }
    /**
     * A collection of selection {@link module:engine/model/markercollection~Marker markers}.
     * Marker is a selection marker when selection range is inside the marker range.
     *
     * **Note**: Only markers from {@link ~DocumentSelection#observeMarkers observed markers groups} are collected.
     *
     * @readonly
     * @type {module:utils/collection~Collection}
     */
    get markers() {
        return this._selection.markers;
    }
    /**
     * Used for the compatibility with the {@link module:engine/model/selection~Selection#isEqual} method.
     *
     * @internal
     * @protected
     */
    get _ranges() {
        return this._selection._ranges;
    }
    /**
     * Returns an iterable that iterates over copies of selection ranges.
     *
     * @returns {Iterable.<module:engine/model/range~Range>}
     */
    getRanges() {
        return this._selection.getRanges();
    }
    /**
     * Returns the first position in the selection.
     * First position is the position that {@link module:engine/model/position~Position#isBefore is before}
     * any other position in the selection.
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/position~Position|null}
     */
    getFirstPosition() {
        return this._selection.getFirstPosition();
    }
    /**
     * Returns the last position in the selection.
     * Last position is the position that {@link module:engine/model/position~Position#isAfter is after}
     * any other position in the selection.
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/position~Position|null}
     */
    getLastPosition() {
        return this._selection.getLastPosition();
    }
    /**
     * Returns a copy of the first range in the selection.
     * First range is the one which {@link module:engine/model/range~Range#start start} position
     * {@link module:engine/model/position~Position#isBefore is before} start position of all other ranges
     * (not to confuse with the first range added to the selection).
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/range~Range|null}
     */
    getFirstRange() {
        return this._selection.getFirstRange();
    }
    /**
     * Returns a copy of the last range in the selection.
     * Last range is the one which {@link module:engine/model/range~Range#end end} position
     * {@link module:engine/model/position~Position#isAfter is after} end position of all other ranges (not to confuse with the range most
     * recently added to the selection).
     *
     * Returns `null` if there are no ranges in selection.
     *
     * @returns {module:engine/model/range~Range|null}
     */
    getLastRange() {
        return this._selection.getLastRange();
    }
    /**
     * Gets elements of type {@link module:engine/model/schema~Schema#isBlock "block"} touched by the selection.
     *
     * This method's result can be used for example to apply block styling to all blocks covered by this selection.
     *
     * **Note:** `getSelectedBlocks()` returns blocks that are nested in other non-block elements
     * but will not return blocks nested in other blocks.
     *
     * In this case the function will return exactly all 3 paragraphs (note: `<blockQuote>` is not a block itself):
     *
     *		<paragraph>[a</paragraph>
     *		<blockQuote>
     *			<paragraph>b</paragraph>
     *		</blockQuote>
     *		<paragraph>c]d</paragraph>
     *
     * In this case the paragraph will also be returned, despite the collapsed selection:
     *
     *		<paragraph>[]a</paragraph>
     *
     * In such a scenario, however, only blocks A, B & E will be returned as blocks C & D are nested in block B:
     *
     *		[<blockA></blockA>
     *		<blockB>
     *			<blockC></blockC>
     *			<blockD></blockD>
     *		</blockB>
     *		<blockE></blockE>]
     *
     * If the selection is inside a block all the inner blocks (A & B) are returned:
     *
     * 		<block>
     *			<blockA>[a</blockA>
     * 			<blockB>b]</blockB>
     * 		</block>
     *
     * **Special case**: If a selection ends at the beginning of a block, that block is not returned as from user perspective
     * this block wasn't selected. See [#984](https://github.com/ckeditor/ckeditor5-engine/issues/984) for more details.
     *
     *		<paragraph>[a</paragraph>
     *		<paragraph>b</paragraph>
     *		<paragraph>]c</paragraph> // this block will not be returned
     *
     * @returns {Iterable.<module:engine/model/element~Element>}
     */
    getSelectedBlocks() {
        return this._selection.getSelectedBlocks();
    }
    /**
     * Returns the selected element. {@link module:engine/model/element~Element Element} is considered as selected if there is only
     * one range in the selection, and that range contains exactly one element.
     * Returns `null` if there is no selected element.
     *
     * @returns {module:engine/model/element~Element|null}
     */
    getSelectedElement() {
        return this._selection.getSelectedElement();
    }
    /**
     * Checks whether the selection contains the entire content of the given element. This means that selection must start
     * at a position {@link module:engine/model/position~Position#isTouching touching} the element's start and ends at position
     * touching the element's end.
     *
     * By default, this method will check whether the entire content of the selection's current root is selected.
     * Useful to check if e.g. the user has just pressed <kbd>Ctrl</kbd> + <kbd>A</kbd>.
     *
     * @param {module:engine/model/element~Element} [element=this.anchor.root]
     * @returns {Boolean}
     */
    containsEntireContent(element) {
        return this._selection.containsEntireContent(element);
    }
    /**
     * Unbinds all events previously bound by document selection.
     */
    destroy() {
        this._selection.destroy();
    }
    /**
     * Returns iterable that iterates over this selection's attribute keys.
     *
     * @returns {Iterable.<String>}
     */
    getAttributeKeys() {
        return this._selection.getAttributeKeys();
    }
    /**
     * Returns iterable that iterates over this selection's attributes.
     *
     * Attributes are returned as arrays containing two items. First one is attribute key and second is attribute value.
     * This format is accepted by native `Map` object and also can be passed in `Node` constructor.
     *
     * @returns {Iterable.<*>}
     */
    getAttributes() {
        return this._selection.getAttributes();
    }
    /**
     * Gets an attribute value for given key or `undefined` if that attribute is not set on the selection.
     *
     * @param {String} key Key of attribute to look for.
     * @returns {*} Attribute value or `undefined`.
     */
    getAttribute(key) {
        return this._selection.getAttribute(key);
    }
    /**
     * Checks if the selection has an attribute for given key.
     *
     * @param {String} key Key of attribute to check.
     * @returns {Boolean} `true` if attribute with given key is set on selection, `false` otherwise.
     */
    hasAttribute(key) {
        return this._selection.hasAttribute(key);
    }
    /**
     * Refreshes selection attributes and markers according to the current position in the model.
     */
    refresh() {
        this._selection.updateMarkers();
        this._selection._updateAttributes(false);
    }
    /**
     * Registers a marker group prefix or a marker name to be collected in the
     * {@link ~DocumentSelection#markers selection markers collection}.
     *
     * See also {@link module:engine/model/markercollection~MarkerCollection#getMarkersGroup `MarkerCollection#getMarkersGroup()`}.
     *
     * @param {String} prefixOrName The marker group prefix or marker name.
     */
    observeMarkers(prefixOrName) {
        this._selection.observeMarkers(prefixOrName);
    }
    /**
     * Moves {@link module:engine/model/documentselection~DocumentSelection#focus} to the specified location.
     * Should be used only within the {@link module:engine/model/writer~Writer#setSelectionFocus} method.
     *
     * The location can be specified in the same form as
     * {@link module:engine/model/writer~Writer#createPositionAt writer.createPositionAt()} parameters.
     *
     * @see module:engine/model/writer~Writer#setSelectionFocus
     * @internal
     * @protected
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/model/item~Item model item}.
     */
    _setFocus(itemOrPosition, offset) {
        this._selection.setFocus(itemOrPosition, offset);
    }
    /**
     * Sets this selection's ranges and direction to the specified location based on the given
     * {@link module:engine/model/selection~Selectable selectable}.
     * Should be used only within the {@link module:engine/model/writer~Writer#setSelection} method.
     *
     * @see module:engine/model/writer~Writer#setSelection
     * @internal
     * @protected
     * @param {module:engine/model/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     */
    _setTo(...args) {
        this._selection.setTo(...args);
    }
    /**
     * Sets attribute on the selection. If attribute with the same key already is set, it's value is overwritten.
     * Should be used only within the {@link module:engine/model/writer~Writer#setSelectionAttribute} method.
     *
     * @see module:engine/model/writer~Writer#setSelectionAttribute
     * @internal
     * @protected
     * @param {String} key Key of the attribute to set.
     * @param {*} value Attribute value.
     */
    _setAttribute(key, value) {
        this._selection.setAttribute(key, value);
    }
    /**
     * Removes an attribute with given key from the selection.
     * If the given attribute was set on the selection, fires the {@link module:engine/model/selection~Selection#event:change:range}
     * event with removed attribute key.
     * Should be used only within the {@link module:engine/model/writer~Writer#removeSelectionAttribute} method.
     *
     * @see module:engine/model/writer~Writer#removeSelectionAttribute
     * @internal
     * @protected
     * @param {String} key Key of the attribute to remove.
     */
    _removeAttribute(key) {
        this._selection.removeAttribute(key);
    }
    /**
     * Returns an iterable that iterates through all selection attributes stored in current selection's parent.
     *
     * @protected
     * @returns {Iterable.<*>}
     */
    _getStoredAttributes() {
        return this._selection.getStoredAttributes();
    }
    /**
     * Temporarily changes the gravity of the selection from the left to the right.
     *
     * The gravity defines from which direction the selection inherits its attributes. If it's the default left
     * gravity, the selection (after being moved by the the user) inherits attributes from its left hand side.
     * This method allows to temporarily override this behavior by forcing the gravity to the right.
     *
     * It returns an unique identifier which is required to restore the gravity. It guarantees the symmetry
     * of the process.
     *
     * @see module:engine/model/writer~Writer#overrideSelectionGravity
     * @internal
     * @protected
     * @returns {String} The unique id which allows restoring the gravity.
     */
    _overrideGravity() {
        return this._selection.overrideGravity();
    }
    /**
     * Restores the {@link ~DocumentSelection#_overrideGravity overridden gravity}.
     *
     * Restoring the gravity is only possible using the unique identifier returned by
     * {@link ~DocumentSelection#_overrideGravity}. Note that the gravity remains overridden as long as won't be restored
     * the same number of times it was overridden.
     *
     * @see module:engine/model/writer~Writer#restoreSelectionGravity
     * @internal
     * @protected
     * @param {String} uid The unique id returned by {@link #_overrideGravity}.
     */
    _restoreGravity(uid) {
        this._selection.restoreGravity(uid);
    }
    /**
     * Generates and returns an attribute key for selection attributes store, basing on original attribute key.
     *
     * @internal
     * @protected
     * @param {String} key Attribute key to convert.
     * @returns {String} Converted attribute key, applicable for selection store.
     */
    static _getStoreAttributeKey(key) {
        return storePrefix + key;
    }
    /**
     * Checks whether the given attribute key is an attribute stored on an element.
     *
     * @protected
     * @param {String} key
     * @returns {Boolean}
     */
    static _isStoreAttributeKey(key) {
        return key.startsWith(storePrefix);
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		selection.is( 'selection' ); // -> true
 *		selection.is( 'documentSelection' ); // -> true
 *		selection.is( 'model:selection' ); // -> true
 *		selection.is( 'model:documentSelection' ); // -> true
 *
 *		selection.is( 'view:selection' ); // -> false
 *		selection.is( 'element' ); // -> false
 *		selection.is( 'node' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
documentselection_DocumentSelection.prototype.is = function (type) {
    return type === 'selection' ||
        type == 'model:selection' ||
        type == 'documentSelection' ||
        type == 'model:documentSelection';
};
// `LiveSelection` is used internally by {@link module:engine/model/documentselection~DocumentSelection} and shouldn't be used directly.
//
// LiveSelection` is automatically updated upon changes in the {@link module:engine/model/document~Document document}
// to always contain valid ranges. Its attributes are inherited from the text unless set explicitly.
//
// Differences between {@link module:engine/model/selection~Selection} and `LiveSelection` are:
// * there is always a range in `LiveSelection` - even if no ranges were added there is a "default range"
// present in the selection,
// * ranges added to this selection updates automatically when the document changes,
// * attributes of `LiveSelection` are updated automatically according to selection ranges.
//
// @extends module:engine/model/selection~Selection
//
class LiveSelection extends selection_Selection {
    // Creates an empty live selection for given {@link module:engine/model/document~Document}.
    // @param {module:engine/model/document~Document} doc Document which owns this selection.
    constructor(doc) {
        super();
        // List of selection markers.
        // Marker is a selection marker when selection range is inside the marker range.
        //
        // @type {module:utils/collection~Collection}
        this.markers = new Collection({ idProperty: 'name' });
        // Document which owns this selection.
        //
        // @protected
        // @member {module:engine/model/model~Model}
        this._model = doc.model;
        // Document which owns this selection.
        //
        // @protected
        // @member {module:engine/model/document~Document}
        this._document = doc;
        // Keeps mapping of attribute name to priority with which the attribute got modified (added/changed/removed)
        // last time. Possible values of priority are: `'low'` and `'normal'`.
        //
        // Priorities are used by internal `LiveSelection` mechanisms. All attributes set using `LiveSelection`
        // attributes API are set with `'normal'` priority.
        //
        // @private
        // @member {Map} module:engine/model/liveselection~LiveSelection#_attributePriority
        this._attributePriority = new Map();
        // Position to which the selection should be set if the last selection range was moved to the graveyard.
        // @private
        // @member {module:engine/model/position~Position} module:engine/model/liveselection~LiveSelection#_selectionRestorePosition
        this._selectionRestorePosition = null;
        // Flag that informs whether the selection ranges have changed. It is changed on true when `LiveRange#change:range` event is fired.
        // @private
        // @member {Array} module:engine/model/liveselection~LiveSelection#_hasChangedRange
        this._hasChangedRange = false;
        // Each overriding gravity adds an UID to the set and each removal removes it.
        // Gravity is overridden when there's at least one UID in the set.
        // Gravity is restored when the set is empty.
        // This is to prevent conflicts when gravity is overridden by more than one feature at the same time.
        // @private
        // @type {Set}
        this._overriddenGravityRegister = new Set();
        // Prefixes of marker names that should affect `LiveSelection#markers` collection.
        // @private
        // @type {Set}
        this._observedMarkers = new Set();
        // Ensure selection is correct after each operation.
        this.listenTo(this._model, 'applyOperation', (evt, args) => {
            const operation = args[0];
            if (!operation.isDocumentOperation || operation.type == 'marker' || operation.type == 'rename' || operation.type == 'noop') {
                return;
            }
            // Fix selection if the last range was removed from it and we have a position to which we can restore the selection.
            if (this._ranges.length == 0 && this._selectionRestorePosition) {
                this._fixGraveyardSelection(this._selectionRestorePosition);
            }
            // "Forget" the restore position even if it was not "used".
            this._selectionRestorePosition = null;
            if (this._hasChangedRange) {
                this._hasChangedRange = false;
                this.fire('change:range', { directChange: false });
            }
        }, { priority: 'lowest' });
        // Ensure selection is correct and up to date after each range change.
        this.on('change:range', () => {
            this._validateSelectionRanges(this.getRanges());
        });
        // Update markers data stored by the selection after each marker change.
        // This handles only marker changes done through marker operations (not model tree changes).
        this.listenTo(this._model.markers, 'update', (evt, marker, oldRange, newRange) => {
            this._updateMarker(marker, newRange);
        });
        // Ensure selection is up to date after each change block.
        this.listenTo(this._document, 'change', (evt, batch) => {
            clearAttributesStoredInElement(this._model, batch);
        });
    }
    get isCollapsed() {
        const length = this._ranges.length;
        return length === 0 ? this._document._getDefaultRange().isCollapsed : super.isCollapsed;
    }
    get anchor() {
        return super.anchor || this._document._getDefaultRange().start;
    }
    get focus() {
        return super.focus || this._document._getDefaultRange().end;
    }
    get rangeCount() {
        return this._ranges.length ? this._ranges.length : 1;
    }
    // Describes whether `LiveSelection` has own range(s) set, or if it is defaulted to
    // {@link module:engine/model/document~Document#_getDefaultRange document's default range}.
    //
    // @readonly
    // @type {Boolean}
    get hasOwnRange() {
        return this._ranges.length > 0;
    }
    // When set to `true` then selection attributes on node before the caret won't be taken
    // into consideration while updating selection attributes.
    //
    // @protected
    // @type {Boolean}
    get isGravityOverridden() {
        return !!this._overriddenGravityRegister.size;
    }
    // Unbinds all events previously bound by live selection.
    destroy() {
        for (let i = 0; i < this._ranges.length; i++) {
            this._ranges[i].detach();
        }
        this.stopListening();
    }
    *getRanges() {
        if (this._ranges.length) {
            yield* super.getRanges();
        }
        else {
            yield this._document._getDefaultRange();
        }
    }
    getFirstRange() {
        return super.getFirstRange() || this._document._getDefaultRange();
    }
    getLastRange() {
        return super.getLastRange() || this._document._getDefaultRange();
    }
    setTo(...args) {
        super.setTo(...args);
        this._updateAttributes(true);
        this.updateMarkers();
    }
    setFocus(itemOrPosition, offset) {
        super.setFocus(itemOrPosition, offset);
        this._updateAttributes(true);
        this.updateMarkers();
    }
    setAttribute(key, value) {
        if (this._setAttribute(key, value)) {
            // Fire event with exact data.
            const attributeKeys = [key];
            this.fire('change:attribute', { attributeKeys, directChange: true });
        }
    }
    removeAttribute(key) {
        if (this._removeAttribute(key)) {
            // Fire event with exact data.
            const attributeKeys = [key];
            this.fire('change:attribute', { attributeKeys, directChange: true });
        }
    }
    overrideGravity() {
        const overrideUid = uid();
        // Remember that another overriding has been requested. It will need to be removed
        // before the gravity is to be restored.
        this._overriddenGravityRegister.add(overrideUid);
        if (this._overriddenGravityRegister.size === 1) {
            this._updateAttributes(true);
        }
        return overrideUid;
    }
    restoreGravity(uid) {
        if (!this._overriddenGravityRegister.has(uid)) {
            /**
             * Restoring gravity for an unknown UID is not possible. Make sure you are using a correct
             * UID obtained from the {@link module:engine/model/writer~Writer#overrideSelectionGravity} to restore.
             *
             * @error document-selection-gravity-wrong-restore
             * @param {String} uid The unique identifier returned by
             * {@link module:engine/model/documentselection~DocumentSelection#_overrideGravity}.
             */
            throw new CKEditorError('document-selection-gravity-wrong-restore', this, { uid });
        }
        this._overriddenGravityRegister.delete(uid);
        // Restore gravity only when all overriding have been restored.
        if (!this.isGravityOverridden) {
            this._updateAttributes(true);
        }
    }
    observeMarkers(prefixOrName) {
        this._observedMarkers.add(prefixOrName);
        this.updateMarkers();
    }
    _replaceAllRanges(ranges) {
        this._validateSelectionRanges(ranges);
        super._replaceAllRanges(ranges);
    }
    _popRange() {
        this._ranges.pop().detach();
    }
    _pushRange(range) {
        const liveRange = this._prepareRange(range);
        // `undefined` is returned when given `range` is in graveyard root.
        if (liveRange) {
            this._ranges.push(liveRange);
        }
    }
    _validateSelectionRanges(ranges) {
        for (const range of ranges) {
            if (!this._document._validateSelectionRange(range)) {
                /**
                 * Range from {@link module:engine/model/documentselection~DocumentSelection document selection}
                 * starts or ends at incorrect position.
                 *
                 * @error document-selection-wrong-position
                 * @param {module:engine/model/range~Range} range
                 */
                throw new CKEditorError('document-selection-wrong-position', this, { range });
            }
        }
    }
    // Prepares given range to be added to selection. Checks if it is correct,
    // converts it to {@link module:engine/model/liverange~LiveRange LiveRange}
    // and sets listeners listening to the range's change event.
    //
    // @private
    // @param {module:engine/model/range~Range} range
    _prepareRange(range) {
        this._checkRange(range);
        if (range.root == this._document.graveyard) {
            // @if CK_DEBUG // console.warn( 'Trying to add a Range that is in the graveyard root. Range rejected.' );
            return;
        }
        const liveRange = LiveRange.fromRange(range);
        // If selection range is moved to the graveyard remove it from the selection object.
        // Also, save some data that can be used to restore selection later, on `Model#applyOperation` event.
        liveRange.on('change:range', (evt, oldRange, data) => {
            this._hasChangedRange = true;
            if (liveRange.root == this._document.graveyard) {
                this._selectionRestorePosition = data.deletionPosition;
                const index = this._ranges.indexOf(liveRange);
                this._ranges.splice(index, 1);
                liveRange.detach();
            }
        });
        return liveRange;
    }
    updateMarkers() {
        if (!this._observedMarkers.size) {
            return;
        }
        const markers = [];
        let changed = false;
        for (const marker of this._model.markers) {
            const markerGroup = marker.name.split(':', 1)[0];
            if (!this._observedMarkers.has(markerGroup)) {
                continue;
            }
            const markerRange = marker.getRange();
            for (const selectionRange of this.getRanges()) {
                if (markerRange.containsRange(selectionRange, !selectionRange.isCollapsed)) {
                    markers.push(marker);
                }
            }
        }
        const oldMarkers = Array.from(this.markers);
        for (const marker of markers) {
            if (!this.markers.has(marker)) {
                this.markers.add(marker);
                changed = true;
            }
        }
        for (const marker of Array.from(this.markers)) {
            if (!markers.includes(marker)) {
                this.markers.remove(marker);
                changed = true;
            }
        }
        if (changed) {
            this.fire('change:marker', { oldMarkers, directChange: false });
        }
    }
    _updateMarker(marker, markerRange) {
        const markerGroup = marker.name.split(':', 1)[0];
        if (!this._observedMarkers.has(markerGroup)) {
            return;
        }
        let changed = false;
        const oldMarkers = Array.from(this.markers);
        const hasMarker = this.markers.has(marker);
        if (!markerRange) {
            if (hasMarker) {
                this.markers.remove(marker);
                changed = true;
            }
        }
        else {
            let contained = false;
            for (const selectionRange of this.getRanges()) {
                if (markerRange.containsRange(selectionRange, !selectionRange.isCollapsed)) {
                    contained = true;
                    break;
                }
            }
            if (contained && !hasMarker) {
                this.markers.add(marker);
                changed = true;
            }
            else if (!contained && hasMarker) {
                this.markers.remove(marker);
                changed = true;
            }
        }
        if (changed) {
            this.fire('change:marker', { oldMarkers, directChange: false });
        }
    }
    // Updates this selection attributes according to its ranges and the {@link module:engine/model/document~Document model document}.
    //
    // @protected
    // @param {Boolean} clearAll
    // @fires change:attribute
    _updateAttributes(clearAll) {
        const newAttributes = toMap(this._getSurroundingAttributes());
        const oldAttributes = toMap(this.getAttributes());
        if (clearAll) {
            // If `clearAll` remove all attributes and reset priorities.
            this._attributePriority = new Map();
            this._attrs = new Map();
        }
        else {
            // If not, remove only attributes added with `low` priority.
            for (const [key, priority] of this._attributePriority) {
                if (priority == 'low') {
                    this._attrs.delete(key);
                    this._attributePriority.delete(key);
                }
            }
        }
        this._setAttributesTo(newAttributes);
        // Let's evaluate which attributes really changed.
        const changed = [];
        // First, loop through all attributes that are set on selection right now.
        // Check which of them are different than old attributes.
        for (const [newKey, newValue] of this.getAttributes()) {
            if (!oldAttributes.has(newKey) || oldAttributes.get(newKey) !== newValue) {
                changed.push(newKey);
            }
        }
        // Then, check which of old attributes got removed.
        for (const [oldKey] of oldAttributes) {
            if (!this.hasAttribute(oldKey)) {
                changed.push(oldKey);
            }
        }
        // Fire event with exact data (fire only if anything changed).
        if (changed.length > 0) {
            this.fire('change:attribute', { attributeKeys: changed, directChange: false });
        }
    }
    // Internal method for setting `LiveSelection` attribute. Supports attribute priorities (through `directChange`
    // parameter).
    //
    // @private
    // @param {String} key Attribute key.
    // @param {*} value Attribute value.
    // @param {Boolean} [directChange=true] `true` if the change is caused by `Selection` API, `false` if change
    // is caused by `Batch` API.
    // @returns {Boolean} Whether value has changed.
    _setAttribute(key, value, directChange = true) {
        const priority = directChange ? 'normal' : 'low';
        if (priority == 'low' && this._attributePriority.get(key) == 'normal') {
            // Priority too low.
            return false;
        }
        const oldValue = super.getAttribute(key);
        // Don't do anything if value has not changed.
        if (oldValue === value) {
            return false;
        }
        this._attrs.set(key, value);
        // Update priorities map.
        this._attributePriority.set(key, priority);
        return true;
    }
    // Internal method for removing `LiveSelection` attribute. Supports attribute priorities (through `directChange`
    // parameter).
    //
    // NOTE: Even if attribute is not present in the selection but is provided to this method, it's priority will
    // be changed according to `directChange` parameter.
    //
    // @private
    // @param {String} key Attribute key.
    // @param {Boolean} [directChange=true] `true` if the change is caused by `Selection` API, `false` if change
    // is caused by `Batch` API.
    // @returns {Boolean} Whether attribute was removed. May not be true if such attributes didn't exist or the
    // existing attribute had higher priority.
    _removeAttribute(key, directChange = true) {
        const priority = directChange ? 'normal' : 'low';
        if (priority == 'low' && this._attributePriority.get(key) == 'normal') {
            // Priority too low.
            return false;
        }
        // Update priorities map.
        this._attributePriority.set(key, priority);
        // Don't do anything if value has not changed.
        if (!super.hasAttribute(key)) {
            return false;
        }
        this._attrs.delete(key);
        return true;
    }
    // Internal method for setting multiple `LiveSelection` attributes. Supports attribute priorities (through
    // `directChange` parameter).
    //
    // @private
    // @param {Map.<String,*>} attrs Iterable object containing attributes to be set.
    // @returns {Set.<String>} Changed attribute keys.
    _setAttributesTo(attrs) {
        const changed = new Set();
        for (const [oldKey, oldValue] of this.getAttributes()) {
            // Do not remove attribute if attribute with same key and value is about to be set.
            if (attrs.get(oldKey) === oldValue) {
                continue;
            }
            // All rest attributes will be removed so changed attributes won't change .
            this._removeAttribute(oldKey, false);
        }
        for (const [key, value] of attrs) {
            // Attribute may not be set because of attributes or because same key/value is already added.
            const gotAdded = this._setAttribute(key, value, false);
            if (gotAdded) {
                changed.add(key);
            }
        }
        return changed;
    }
    // Returns an iterable that iterates through all selection attributes stored in current selection's parent.
    //
    // @public
    // @returns {Iterable.<*>}
    *getStoredAttributes() {
        const selectionParent = this.getFirstPosition().parent;
        if (this.isCollapsed && selectionParent.isEmpty) {
            for (const key of selectionParent.getAttributeKeys()) {
                if (key.startsWith(storePrefix)) {
                    const realKey = key.substr(storePrefix.length);
                    yield [realKey, selectionParent.getAttribute(key)];
                }
            }
        }
    }
    // Checks model text nodes that are closest to the selection's first position and returns attributes of first
    // found element. If there are no text nodes in selection's first position parent, it returns selection
    // attributes stored in that parent.
    //
    // @private
    // @returns {Iterable.<*>} Collection of attributes.
    _getSurroundingAttributes() {
        const position = this.getFirstPosition();
        const schema = this._model.schema;
        let attrs = null;
        if (!this.isCollapsed) {
            // 1. If selection is a range...
            const range = this.getFirstRange();
            // ...look for a first character node in that range and take attributes from it.
            for (const value of range) {
                // If the item is an object, we don't want to get attributes from its children.
                if (value.item.is('element') && schema.isObject(value.item)) {
                    break;
                }
                if (value.type == 'text') {
                    attrs = value.item.getAttributes();
                    break;
                }
            }
        }
        else {
            // 2. If the selection is a caret or the range does not contain a character node...
            const nodeBefore = position.textNode ? position.textNode : position.nodeBefore;
            const nodeAfter = position.textNode ? position.textNode : position.nodeAfter;
            // When gravity is overridden then don't take node before into consideration.
            if (!this.isGravityOverridden) {
                // ...look at the node before caret and take attributes from it if it is a character node.
                attrs = getAttrsIfCharacter(nodeBefore);
            }
            // 3. If not, look at the node after caret...
            if (!attrs) {
                attrs = getAttrsIfCharacter(nodeAfter);
            }
            // 4. If not, try to find the first character on the left, that is in the same node.
            // When gravity is overridden then don't take node before into consideration.
            if (!this.isGravityOverridden && !attrs) {
                let node = nodeBefore;
                while (node && !schema.isInline(node) && !attrs) {
                    node = node.previousSibling;
                    attrs = getAttrsIfCharacter(node);
                }
            }
            // 5. If not found, try to find the first character on the right, that is in the same node.
            if (!attrs) {
                let node = nodeAfter;
                while (node && !schema.isInline(node) && !attrs) {
                    node = node.nextSibling;
                    attrs = getAttrsIfCharacter(node);
                }
            }
            // 6. If not found, selection should retrieve attributes from parent.
            if (!attrs) {
                attrs = this.getStoredAttributes();
            }
        }
        return attrs;
    }
    // Fixes the selection after all its ranges got removed.
    //
    // @private
    // @param {module:engine/model/position~Position} deletionPosition Position where the deletion happened.
    _fixGraveyardSelection(deletionPosition) {
        // Find a range that is a correct selection range and is closest to the position where the deletion happened.
        const selectionRange = this._model.schema.getNearestSelectionRange(deletionPosition);
        // If nearest valid selection range has been found - add it in the place of old range.
        if (selectionRange) {
            // Check the range, convert it to live range, bind events, etc.
            this._pushRange(selectionRange);
        }
        // If nearest valid selection range cannot be found don't add any range. Selection will be set to the default range.
    }
}
// Helper function for {@link module:engine/model/liveselection~LiveSelection#_updateAttributes}.
//
// It takes model item, checks whether it is a text node (or text proxy) and, if so, returns it's attributes. If not, returns `null`.
//
// @param {module:engine/model/item~Item|null}  node
// @returns {Boolean}
function getAttrsIfCharacter(node) {
    if (node instanceof textproxy_TextProxy || node instanceof model_text_Text) {
        return node.getAttributes();
    }
    return null;
}
// Removes selection attributes from element which is not empty anymore.
//
// @param {module:engine/model/model~Model} model
// @param {module:engine/model/batch~Batch} batch
function clearAttributesStoredInElement(model, batch) {
    const differ = model.document.differ;
    for (const entry of differ.getChanges()) {
        if (entry.type != 'insert') {
            continue;
        }
        const changeParent = entry.position.parent;
        const isNoLongerEmpty = entry.length === changeParent.maxOffset;
        if (isNoLongerEmpty) {
            model.enqueueChange(batch, writer => {
                const storedAttributes = Array.from(changeParent.getAttributeKeys())
                    .filter(key => key.startsWith(storePrefix));
                for (const key of storedAttributes) {
                    writer.removeAttribute(key, changeParent);
                }
            });
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/conversionhelpers.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/conversion/conversionhelpers
 */
/**
 * Base class for conversion helpers.
 */
class ConversionHelpers {
    /**
     * Creates a conversion helpers instance.
     *
     * @param {Array.<module:engine/conversion/downcastdispatcher~DowncastDispatcher|
     * module:engine/conversion/upcastdispatcher~UpcastDispatcher>} dispatchers
     */
    constructor(dispatchers) {
        this._dispatchers = dispatchers;
    }
    /**
     * Registers a conversion helper.
     *
     * **Note**: See full usage example in the `{@link module:engine/conversion/conversion~Conversion#for conversion.for()}`
     * method description.
     *
     * @param {Function} conversionHelper The function to be called on event.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers|module:engine/conversion/upcasthelpers~UpcastHelpers}
     */
    add(conversionHelper) {
        for (const dispatcher of this._dispatchers) {
            conversionHelper(dispatcher);
        }
        return this;
    }
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/cloneDeep.js


/** Used to compose bitmasks for cloning. */
var cloneDeep_CLONE_DEEP_FLAG = 1,
    cloneDeep_CLONE_SYMBOLS_FLAG = 4;

/**
 * This method is like `_.clone` except that it recursively clones `value`.
 *
 * @static
 * @memberOf _
 * @since 1.0.0
 * @category Lang
 * @param {*} value The value to recursively clone.
 * @returns {*} Returns the deep cloned value.
 * @see _.clone
 * @example
 *
 * var objects = [{ 'a': 1 }, { 'b': 2 }];
 *
 * var deep = _.cloneDeep(objects);
 * console.log(deep[0] === objects[0]);
 * // => false
 */
function cloneDeep(value) {
  return _baseClone(value, cloneDeep_CLONE_DEEP_FLAG | cloneDeep_CLONE_SYMBOLS_FLAG);
}

/* harmony default export */ const lodash_es_cloneDeep = (cloneDeep);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/downcasthelpers.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Contains downcast (model-to-view) converters for {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}.
 *
 * @module engine/conversion/downcasthelpers
 */










/**
 * Downcast conversion helper functions.
 *
 * Learn more about {@glink framework/guides/deep-dive/conversion/downcast downcast helpers}.
 *
 * @extends module:engine/conversion/conversionhelpers~ConversionHelpers
 */
class DowncastHelpers extends ConversionHelpers {
    /**
     * Model element to view element conversion helper.
     *
     * This conversion results in creating a view element. For example, model `<paragraph>Foo</paragraph>` becomes `<p>Foo</p>` in the view.
     *
     *		editor.conversion.for( 'downcast' ).elementToElement( {
     *			model: 'paragraph',
     *			view: 'p'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).elementToElement( {
     *			model: 'paragraph',
     *			view: 'div',
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).elementToElement( {
     *			model: 'fancyParagraph',
     *			view: {
     *				name: 'p',
     *				classes: 'fancy'
     *			}
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).elementToElement( {
     *			model: 'heading',
     *			view: ( modelElement, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				return writer.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) );
     *			}
     *		} );
     *
     * The element-to-element conversion supports the reconversion mechanism. It can be enabled by using either the `attributes` or
     * the `children` props on a model description. You will find a couple examples below.
     *
     * In order to reconvert an element if any of its direct children have been added or removed, use the `children` property on a `model`
     * description. For example, this model:
     *
     *		<box>
     *			<paragraph>Some text.</paragraph>
     *		</box>
     *
     * will be converted into this structure in the view:
     *
     *		<div class="box" data-type="single">
     *			<p>Some text.</p>
     *		</div>
     *
     * But if more items were inserted in the model:
     *
     *		<box>
     *			<paragraph>Some text.</paragraph>
     *			<paragraph>Other item.</paragraph>
     *		</box>
     *
     * it will be converted into this structure in the view (note the element `data-type` change):
     *
     *		<div class="box" data-type="multiple">
     *			<p>Some text.</p>
     *			<p>Other item.</p>
     *		</div>
     *
     * Such a converter would look like this (note that the `paragraph` elements are converted separately):
     *
     *		editor.conversion.for( 'downcast' ).elementToElement( {
     *			model: {
     *	 			name: 'box',
     *	 			children: true
     *			},
     *			view: ( modelElement, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				return writer.createContainerElement( 'div', {
     *					class: 'box',
     *					'data-type': modelElement.childCount == 1 ? 'single' : 'multiple'
     *				} );
     *			}
     *		} );
     *
     * In order to reconvert element if any of its attributes have been updated, use the `attributes` property on a `model`
     * description. For example, this model:
     *
     *		<heading level="2">Some text.</heading>
     *
     * will be converted into this structure in the view:
     *
     *		<h2>Some text.</h2>
     *
     * But if the `heading` element's `level` attribute has been updated to `3` for example, then
     * it will be converted into this structure in the view:
     *
     *		<h3>Some text.</h3>
     *
     * Such a converter would look as follows:
     *
     *		editor.conversion.for( 'downcast' ).elementToElement( {
     *			model: {
     *	 			name: 'heading',
     *	 			attributes: 'level'
     *			},
     *			view: ( modelElement, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				return writer.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) );
     *			}
     *		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * You can read more about the element-to-element conversion in the
     * {@glink framework/guides/deep-dive/conversion/downcast downcast conversion} guide.
     *
     * @method #elementToElement
     * @param {Object} config Conversion configuration.
     * @param {String|Object} config.model The description or a name of the model element to convert.
     * @param {String|Array.<String>} [config.model.attributes] The list of attribute names that should be consumed while creating
     * the view element. Note that the view will be reconverted if any of the listed attributes changes.
     * @param {Boolean} [config.model.children] Specifies whether the view element requires reconversion if the list
     * of the model child nodes changed.
     * @param {module:engine/view/elementdefinition~ElementDefinition|module:engine/conversion/downcasthelpers~ElementCreatorFunction}
     * config.view A view element definition or a function that takes the model element and
     * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
     * as parameters and returns a view container element.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
     */
    elementToElement(config) {
        return this.add(downcastElementToElement(config));
    }
    /**
     * The model element to view structure (several elements) conversion helper.
     *
     * This conversion results in creating a view structure with one or more slots defined for the child nodes.
     * For example, a model `<table>` may become this structure in the view:
     *
     *		<figure class="table">
     *			<table>
     *				<tbody>${ slot for table rows }</tbody>
     *			</table>
     *		</figure>
     *
     * The children of the model's `<table>` element will be inserted into the `<tbody>` element.
     * If the `elementToElement()` helper was used, the children would be inserted into the `<figure>`.
     *
     * An example converter that converts the following model structure:
     *
     *		<wrappedParagraph>Some text.</wrappedParagraph>
     *
     * into this structure in the view:
     *
     *		<div class="wrapper">
     *			<p>Some text.</p>
     *		</div>
     *
     * would look like this:
     *
     *		editor.conversion.for( 'downcast' ).elementToStructure( {
     *			model: 'wrappedParagraph',
     *			view: ( modelElement, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				const wrapperViewElement = writer.createContainerElement( 'div', { class: 'wrapper' } );
     *				const paragraphViewElement = writer.createContainerElement( 'p' );
     *
     *				writer.insert( writer.createPositionAt( wrapperViewElement, 0 ), paragraphViewElement );
     *				writer.insert( writer.createPositionAt( paragraphViewElement, 0 ), writer.createSlot() );
     *
     *				return wrapperViewElement;
     *			}
     *		} );
     *
     * The `slorFor()` function can also take a callback that allows filtering which children of the model element
     * should be converted into this slot.
     *
     * Imagine a table feature where for this model structure:
     *
     *		<table headingRows="1">
     *			<tableRow> ... table cells 1 ... </tableRow>
     *			<tableRow> ... table cells 2 ... </tableRow>
     *			<tableRow> ... table cells 3 ... </tableRow>
     *			<caption>Caption text</caption>
     *		</table>
     *
     * we want to generate this view structure:
     *
     *		<figure class="table">
     *			<table>
     *				<thead>
     *					<tr> ... table cells 1 ... </tr>
     *				</thead>
     *				<tbody>
     *					<tr> ... table cells 2 ... </tr>
     *					<tr> ... table cells 3 ... </tr>
     *				</tbody>
     *			</table>
     *			<figcaption>Caption text</figcaption>
     *		</figure>
     *
     * The converter has to take the `headingRows` attribute into consideration when allocating the `<tableRow>` elements
     * into the `<tbody>` and `<thead>` elements. Hence, we need two slots and need to define proper filter callbacks for them.
     *
     * Additionally, all elements other than `<tableRow>` should be placed outside the `<table>` tag.
     * In the example above, this will handle the table caption.
     *
     * Such a converter would look like this:
     *
     *		editor.conversion.for( 'downcast' ).elementToStructure( {
     *			model: {
     *				name: 'table',
     *				attributes: [ 'headingRows' ]
     *			},
     *			view: ( modelElement, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				const figureElement = writer.createContainerElement( 'figure', { class: 'table' } );
     *				const tableElement = writer.createContainerElement( 'table' );
     *
     *				writer.insert( writer.createPositionAt( figureElement, 0 ), tableElement );
     *
     *				const headingRows = modelElement.getAttribute( 'headingRows' ) || 0;
     *
     *				if ( headingRows > 0 ) {
     *					const tableHead = writer.createContainerElement( 'thead' );
     *
     *					const headSlot = writer.createSlot( node => node.is( 'element', 'tableRow' ) && node.index < headingRows );
     *
     *					writer.insert( writer.createPositionAt( tableElement, 'end' ), tableHead );
     *					writer.insert( writer.createPositionAt( tableHead, 0 ), headSlot );
     *				}
     *
     *				if ( headingRows < tableUtils.getRows( table ) ) {
     *					const tableBody = writer.createContainerElement( 'tbody' );
     *
     *					const bodySlot = writer.createSlot( node => node.is( 'element', 'tableRow' ) && node.index >= headingRows );
     *
     *					writer.insert( writer.createPositionAt( tableElement, 'end' ), tableBody );
     *					writer.insert( writer.createPositionAt( tableBody, 0 ), bodySlot );
     *				}
     *
     *				const restSlot = writer.createSlot( node => !node.is( 'element', 'tableRow' ) );
     *
     *				writer.insert( writer.createPositionAt( figureElement, 'end' ), restSlot );
     *
     *				return figureElement;
     *			}
     *		} );
     *
     * Note: The children of a model element that's being converted must be allocated in the same order in the view
     * in which they are placed in the model.
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #elementToStructure
     * @param {Object} config Conversion configuration.
     * @param {String|Object} config.model The description or a name of the model element to convert.
     * @param {String} [config.model.name] The name of the model element to convert.
     * @param {String|Array.<String>} [config.model.attributes] The list of attribute names that should be consumed while creating
     * the view structure. Note that the view will be reconverted if any of the listed attributes will change.
     * @param {module:engine/conversion/downcasthelpers~StructureCreatorFunction} config.view A function
     * that takes the model element and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast
     * conversion API} as parameters and returns a view container element with slots for model child nodes to be converted into.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
     */
    elementToStructure(config) {
        return this.add(downcastElementToStructure(config));
    }
    /**
     * Model attribute to view element conversion helper.
     *
     * This conversion results in wrapping view nodes with a view attribute element. For example, a model text node with
     * `"Foo"` as data and the `bold` attribute becomes `<strong>Foo</strong>` in the view.
     *
     *		editor.conversion.for( 'downcast' ).attributeToElement( {
     *			model: 'bold',
     *			view: 'strong'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToElement( {
     *			model: 'bold',
     *			view: 'b',
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToElement( {
     *			model: 'invert',
     *			view: {
     *				name: 'span',
     *				classes: [ 'font-light', 'bg-dark' ]
     *			}
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToElement( {
     *			model: {
     *				key: 'fontSize',
     *				values: [ 'big', 'small' ]
     *			},
     *			view: {
     *				big: {
     *					name: 'span',
     *					styles: {
     *						'font-size': '1.2em'
     *					}
     *				},
     *				small: {
     *					name: 'span',
     *					styles: {
     *						'font-size': '0.8em'
     *					}
     *				}
     *			}
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToElement( {
     *			model: 'bold',
     *			view: ( modelAttributeValue, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				return writer.createAttributeElement( 'span', {
     *					style: 'font-weight:' + modelAttributeValue
     *				} );
     *			}
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToElement( {
     *			model: {
     *				key: 'color',
     *				name: '$text'
     *			},
     *			view: ( modelAttributeValue, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				return writer.createAttributeElement( 'span', {
     *					style: 'color:' + modelAttributeValue
     *				} );
     *			}
     *		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #attributeToElement
     * @param {Object} config Conversion configuration.
     * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values }` object. `values` is an array
     * of `String`s with possible values if the model attribute is an enumerable.
     * @param {module:engine/view/elementdefinition~ElementDefinition|Object|
     * module:engine/conversion/downcasthelpers~AttributeElementCreatorFunction} config.view A view element definition or a function
     * that takes the model attribute value and
     * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as parameters and returns a view
     * attribute element. If `config.model.values` is given, `config.view` should be an object assigning values from `config.model.values`
     * to view element definitions or functions.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
     */
    attributeToElement(config) {
        return this.add(downcastAttributeToElement(config));
    }
    /**
     * Model attribute to view attribute conversion helper.
     *
     * This conversion results in adding an attribute to a view node, basing on an attribute from a model node. For example,
     * `<imageInline src='foo.jpg'></imageInline>` is converted to `<img src='foo.jpg'></img>`.
     *
     *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
     *			model: 'source',
     *			view: 'src'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
     *			model: 'source',
     *			view: 'href',
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
     *			model: {
     *				name: 'imageInline',
     *				key: 'source'
     *			},
     *			view: 'src'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
     *			model: {
     *				name: 'styled',
     *				values: [ 'dark', 'light' ]
     *			},
     *			view: {
     *				dark: {
     *					key: 'class',
     *					value: [ 'styled', 'styled-dark' ]
     *				},
     *				light: {
     *					key: 'class',
     *					value: [ 'styled', 'styled-light' ]
     *				}
     *			}
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
     *			model: 'styled',
     *			view: modelAttributeValue => ( {
     *				key: 'class',
     *				value: 'styled-' + modelAttributeValue
     *			} )
     *		} );
     *
     * **Note**: Downcasting to a style property requires providing `value` as an object:
     *
     *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
     *			model: 'lineHeight',
     *			view: modelAttributeValue => ( {
     *				key: 'style',
     *				value: {
     *					'line-height': modelAttributeValue,
     *					'border-bottom': '1px dotted #ba2'
     *				}
     *			} )
     *		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #attributeToAttribute
     * @param {Object} config Conversion configuration.
     * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values, [ name ] }` object describing
     * the attribute key, possible values and, optionally, an element name to convert from.
     * @param {String|Object|module:engine/conversion/downcasthelpers~AttributeCreatorFunction} config.view A view attribute key,
     * or a `{ key, value }` object or a function that takes the model attribute value and
     * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
     * as parameters and returns a `{ key, value }` object. If the `key` is `'class'`, the `value` can be a `String` or an
     * array of `String`s. If the `key` is `'style'`, the `value` is an object with key-value pairs. In other cases, `value` is a `String`.
     * If `config.model.values` is set, `config.view` should be an object assigning values from `config.model.values` to
     * `{ key, value }` objects or a functions.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
     */
    attributeToAttribute(config) {
        return this.add(downcastAttributeToAttribute(config));
    }
    /**
     * Model marker to view element conversion helper.
     *
     * **Note**: This method should be used mainly for editing the downcast and it is recommended
     * to use the {@link #markerToData `#markerToData()`} helper instead.
     *
     * This helper may produce invalid HTML code (e.g. a span between table cells).
     * It should only be used when you are sure that the produced HTML will be semantically correct.
     *
     * This conversion results in creating a view element on the boundaries of the converted marker. If the converted marker
     * is collapsed, only one element is created. For example, a model marker set like this: `<paragraph>F[oo b]ar</paragraph>`
     * becomes `<p>F<span data-marker="search"></span>oo b<span data-marker="search"></span>ar</p>` in the view.
     *
     *		editor.conversion.for( 'editingDowncast' ).markerToElement( {
     *			model: 'search',
     *			view: 'marker-search'
     *		} );
     *
     *		editor.conversion.for( 'editingDowncast' ).markerToElement( {
     *			model: 'search',
     *			view: 'search-result',
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'editingDowncast' ).markerToElement( {
     *			model: 'search',
     *			view: {
     *				name: 'span',
     *				attributes: {
     *					'data-marker': 'search'
     *				}
     *			}
     *		} );
     *
     *		editor.conversion.for( 'editingDowncast' ).markerToElement( {
     *			model: 'search',
     *			view: ( markerData, conversionApi ) => {
     *				const { writer } = conversionApi;
     *
     *				return writer.createUIElement( 'span', {
     *					'data-marker': 'search',
     *					'data-start': markerData.isOpening
     *				} );
     *			}
     *		} );
     *
     * If a function is passed as the `config.view` parameter, it will be used to generate both boundary elements. The function
     * receives the `data` object and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
     * as a parameters and should return an instance of the
     * {@link module:engine/view/uielement~UIElement view UI element}. The `data` object and
     * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi `conversionApi`} are passed from
     * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}. Additionally,
     * the `data.isOpening` parameter is passed, which is set to `true` for the marker start boundary element, and `false` for
     * the marker end boundary element.
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #markerToElement
     * @param {Object} config Conversion configuration.
     * @param {String} config.model The name of the model marker (or model marker group) to convert.
     * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function that
     * takes the model marker data and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
     * as a parameters and returns a view UI element.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
     */
    markerToElement(config) {
        return this.add(downcastMarkerToElement(config));
    }
    /**
     * Model marker to highlight conversion helper.
     *
     * This conversion results in creating a highlight on view nodes. For this kind of conversion,
     * the {@link module:engine/conversion/downcasthelpers~HighlightDescriptor} should be provided.
     *
     * For text nodes, a `<span>` {@link module:engine/view/attributeelement~AttributeElement} is created and it wraps all text nodes
     * in the converted marker range. For example, a model marker set like this: `<paragraph>F[oo b]ar</paragraph>` becomes
     * `<p>F<span class="comment">oo b</span>ar</p>` in the view.
     *
     * {@link module:engine/view/containerelement~ContainerElement} may provide a custom way of handling highlight. Most often,
     * the element itself is given classes and attributes described in the highlight descriptor (instead of being wrapped in `<span>`).
     * For example, a model marker set like this:
     * `[<imageInline src="foo.jpg"></imageInline>]` becomes `<img src="foo.jpg" class="comment"></img>` in the view.
     *
     * For container elements, the conversion is two-step. While the converter processes the highlight descriptor and passes it
     * to a container element, it is the container element instance itself that applies values from the highlight descriptor.
     * So, in a sense, the converter takes care of stating what should be applied on what, while the element decides how to apply that.
     *
     *		editor.conversion.for( 'downcast' ).markerToHighlight( { model: 'comment', view: { classes: 'comment' } } );
     *
     *		editor.conversion.for( 'downcast' ).markerToHighlight( {
     *			model: 'comment',
     *			view: { classes: 'comment' },
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'downcast' ).markerToHighlight( {
     *			model: 'comment',
     *			view: ( data, conversionApi ) => {
     *				// Assuming that the marker name is in a form of comment:commentType:commentId.
     *				const [ , commentType, commentId ] = data.markerName.split( ':' );
     *
     *				return {
     *					classes: [ 'comment', 'comment-' + commentType ],
     *					attributes: { 'data-comment-id': commentId }
     *				};
     *			}
     *		} );
     *
     * If a function is passed as the `config.view` parameter, it will be used to generate the highlight descriptor. The function
     * receives the `data` object and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
     * as the parameters and should return a
     * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor}.
     * The `data` object properties are passed from {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}.
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #markerToHighlight
     * @param {Object} config Conversion configuration.
     * @param {String} config.model The name of the model marker (or model marker group) to convert.
     * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} config.view A highlight descriptor
     * that will be used for highlighting or a function that takes the model marker data and
     * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as a parameters
     * and returns a highlight descriptor.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
     */
    markerToHighlight(config) {
        return this.add(downcastMarkerToHighlight(config));
    }
    /**
     * Model marker converter for data downcast.
     *
     * This conversion creates a representation for model marker boundaries in the view:
     *
     * * If the marker boundary is before or after a model element, a view attribute is set on a corresponding view element.
     * * In other cases, a view element with the specified tag name is inserted at the corresponding view position.
     *
     * Typically, the marker names use the `group:uniqueId:otherData` convention. For example: `comment:e34zfk9k2n459df53sjl34:zx32c`.
     * The default configuration for this conversion is that the first part is the `group` part and the rest of
     * the marker name becomes the `name` part.
     *
     * Tag and attribute names and values are generated from the marker name:
     *
     * * The templates for attributes are `data-[group]-start-before="[name]"`, `data-[group]-start-after="[name]"`,
     * `data-[group]-end-before="[name]"` and `data-[group]-end-after="[name]"`.
     * * The templates for view elements are `<[group]-start name="[name]">` and `<[group]-end name="[name]">`.
     *
     * Attributes mark whether the given marker's start or end boundary is before or after the given element.
     * The `data-[group]-start-before` and `data-[group]-end-after` attributes are favored.
     * The other two are used when the former two cannot be used.
     *
     * The conversion configuration can take a function that will generate different group and name parts.
     * If such a function is set as the `config.view` parameter, it is passed a marker name and it is expected to return an object with two
     * properties: `group` and `name`. If the function returns a falsy value, the conversion will not take place.
     *
     * Basic usage:
     *
     *		// Using the default conversion.
     *		// In this case, all markers with names starting with 'comment:' will be converted.
     *		// The `group` parameter will be set to `comment`.
     *		// The `name` parameter will be the rest of the marker name (without the `:`).
     *		editor.conversion.for( 'dataDowncast' ).markerToData( {
     *			model: 'comment'
     *		} );
     *
     * An example of a view that may be generated by this conversion (assuming a marker with the name `comment:commentId:uid` marked
     * by `[]`):
     *
     *		// Model:
     *		<paragraph>Foo[bar</paragraph>
     *		<imageBlock src="abc.jpg"></imageBlock>]
     *
     *		// View:
     *		<p>Foo<comment-start name="commentId:uid"></comment-start>bar</p>
     *		<figure data-comment-end-after="commentId:uid" class="image"><img src="abc.jpg" /></figure>
     *
     * In the example above, the comment starts before "bar" and ends after the image.
     *
     * If the `name` part is empty, the following view may be generated:
     *
     *		<p>Foo <myMarker-start></myMarker-start>bar</p>
     *		<figure data-myMarker-end-after="" class="image"><img src="abc.jpg" /></figure>
     *
     * **Note:** A situation where some markers have the `name` part and some do not, is incorrect and should be avoided.
     *
     * Examples where `data-group-start-after` and `data-group-end-before` are used:
     *
     *		// Model:
     *		<blockQuote>[]<paragraph>Foo</paragraph></blockQuote>
     *
     *		// View:
     *		<blockquote><p data-group-end-before="name" data-group-start-before="name">Foo</p></blockquote>
     *
     * Similarly, when a marker is collapsed after the last element:
     *
     *		// Model:
     *		<blockQuote><paragraph>Foo</paragraph>[]</blockQuote>
     *
     *		// View:
     *		<blockquote><p data-group-end-after="name" data-group-start-after="name">Foo</p></blockquote>
     *
     * When there are multiple markers from the same group stored in the same attribute of the same element, their
     * name parts are put together in the attribute value, for example: `data-group-start-before="name1,name2,name3"`.
     *
     * Other examples of usage:
     *
     *		// Using a custom function which is the same as the default conversion:
     *		editor.conversion.for( 'dataDowncast' ).markerToData( {
     *			model: 'comment'
     *			view: markerName => ( {
     *				group: 'comment',
     *				name: markerName.substr( 8 ) // Removes 'comment:' part.
     *			} )
     *		} );
     *
     *		// Using the converter priority:
     *		editor.conversion.for( 'dataDowncast' ).markerToData( {
     *			model: 'comment'
     *			view: markerName => ( {
     *				group: 'comment',
     *				name: markerName.substr( 8 ) // Removes 'comment:' part.
     *			} ),
     *			converterPriority: 'high'
     *		} );
     *
     * This kind of conversion is useful for saving data into the database, so it should be used in the data conversion pipeline.
     *
     * See the {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} API guide to learn how to
     * add a converter to the conversion process.
     *
     * @method #markerToData
     * @param {Object} config Conversion configuration.
     * @param {String} config.model The name of the model marker (or the model marker group) to convert.
     * @param {Function} [config.view] A function that takes the model marker name and
     * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as the parameters
     * and returns an object with the `group` and `name` properties.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
     */
    markerToData(config) {
        return this.add(downcastMarkerToData(config));
    }
}
/**
 * Function factory that creates a default downcast converter for text insertion changes.
 *
 * The converter automatically consumes the corresponding value from the consumables list and stops the event (see
 * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}).
 *
 *		modelDispatcher.on( 'insert:$text', insertText() );
 *
 * @returns {Function} Insert text event converter.
 */
function insertText() {
    return (evt, data, conversionApi) => {
        if (!conversionApi.consumable.consume(data.item, evt.name)) {
            return;
        }
        const viewWriter = conversionApi.writer;
        const viewPosition = conversionApi.mapper.toViewPosition(data.range.start);
        const viewText = viewWriter.createText(data.item.data);
        viewWriter.insert(viewPosition, viewText);
    };
}
/**
 * Function factory that creates a default downcast converter for triggering attributes and children conversion.
 *
 * @returns {Function} The converter.
 */
function insertAttributesAndChildren() {
    return (evt, data, conversionApi) => {
        conversionApi.convertAttributes(data.item);
        // Start converting children of the current item.
        // In case of reconversion children were already re-inserted or converted separately.
        if (!data.reconversion && data.item.is('element') && !data.item.isEmpty) {
            conversionApi.convertChildren(data.item);
        }
    };
}
/**
 * Function factory that creates a default downcast converter for node remove changes.
 *
 *		modelDispatcher.on( 'remove', remove() );
 *
 * @returns {Function} Remove event converter.
 */
function downcasthelpers_remove() {
    return (evt, data, conversionApi) => {
        // Find the view range start position by mapping the model position at which the remove happened.
        const viewStart = conversionApi.mapper.toViewPosition(data.position);
        const modelEnd = data.position.getShiftedBy(data.length);
        const viewEnd = conversionApi.mapper.toViewPosition(modelEnd, { isPhantom: true });
        const viewRange = conversionApi.writer.createRange(viewStart, viewEnd);
        // Trim the range to remove in case some UI elements are on the view range boundaries.
        const removed = conversionApi.writer.remove(viewRange.getTrimmed());
        // After the range is removed, unbind all view elements from the model.
        // Range inside view document fragment is used to unbind deeply.
        for (const child of conversionApi.writer.createRangeIn(removed).getItems()) {
            conversionApi.mapper.unbindViewElement(child, { defer: true });
        }
    };
}
/**
 * Creates a `<span>` {@link module:engine/view/attributeelement~AttributeElement view attribute element} from the information
 * provided by the {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor} object. If the priority
 * is not provided in the descriptor, the default priority will be used.
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
 * @returns {module:engine/view/attributeelement~AttributeElement}
 */
function createViewElementFromHighlightDescriptor(writer, descriptor) {
    const viewElement = writer.createAttributeElement('span', descriptor.attributes);
    if (descriptor.classes) {
        viewElement._addClass(descriptor.classes);
    }
    if (typeof descriptor.priority === 'number') {
        viewElement._priority = descriptor.priority;
    }
    viewElement._id = descriptor.id;
    return viewElement;
}
/**
 * Function factory that creates a converter which converts a non-collapsed {@link module:engine/model/selection~Selection model selection}
 * to a {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate
 * value from the `consumable` object and maps model positions from the selection to view positions.
 *
 *		modelDispatcher.on( 'selection', convertRangeSelection() );
 *
 * @returns {Function} Selection converter.
 */
function convertRangeSelection() {
    return (evt, data, conversionApi) => {
        const selection = data.selection;
        if (selection.isCollapsed) {
            return;
        }
        if (!conversionApi.consumable.consume(selection, 'selection')) {
            return;
        }
        const viewRanges = [];
        for (const range of selection.getRanges()) {
            viewRanges.push(conversionApi.mapper.toViewRange(range));
        }
        conversionApi.writer.setSelection(viewRanges, { backward: selection.isBackward });
    };
}
/**
 * Function factory that creates a converter which converts a collapsed {@link module:engine/model/selection~Selection model selection} to
 * a {@link module:engine/view/documentselection~DocumentSelection view selection}. The converter consumes appropriate
 * value from the `consumable` object, maps the model selection position to the view position and breaks
 * {@link module:engine/view/attributeelement~AttributeElement attribute elements} at the selection position.
 *
 *		modelDispatcher.on( 'selection', convertCollapsedSelection() );
 *
 * An example of the view state before and after converting the collapsed selection:
 *
 *		   <p><strong>f^oo<strong>bar</p>
 *		-> <p><strong>f</strong>^<strong>oo</strong>bar</p>
 *
 * By breaking attribute elements like `<strong>`, the selection is in a correct element. Then, when the selection attribute is
 * converted, broken attributes might be merged again, or the position where the selection is may be wrapped
 * with different, appropriate attribute elements.
 *
 * See also {@link module:engine/conversion/downcasthelpers~clearAttributes} which does a clean-up
 * by merging attributes.
 *
 * @returns {Function} Selection converter.
 */
function convertCollapsedSelection() {
    return (evt, data, conversionApi) => {
        const selection = data.selection;
        if (!selection.isCollapsed) {
            return;
        }
        if (!conversionApi.consumable.consume(selection, 'selection')) {
            return;
        }
        const viewWriter = conversionApi.writer;
        const modelPosition = selection.getFirstPosition();
        const viewPosition = conversionApi.mapper.toViewPosition(modelPosition);
        const brokenPosition = viewWriter.breakAttributes(viewPosition);
        viewWriter.setSelection(brokenPosition);
    };
}
/**
 * Function factory that creates a converter which clears artifacts after the previous
 * {@link module:engine/model/selection~Selection model selection} conversion. It removes all empty
 * {@link module:engine/view/attributeelement~AttributeElement view attribute elements} and merges sibling attributes at all start and end
 * positions of all ranges.
 *
 *		   <p><strong>^</strong></p>
 *		-> <p>^</p>
 *
 *		   <p><strong>foo</strong>^<strong>bar</strong>bar</p>
 *		-> <p><strong>foo^bar<strong>bar</p>
 *
 *		   <p><strong>foo</strong><em>^</em><strong>bar</strong>bar</p>
 *		-> <p><strong>foo^bar<strong>bar</p>
 *
 * This listener should be assigned before any converter for the new selection:
 *
 *		modelDispatcher.on( 'selection', clearAttributes() );
 *
 * See {@link module:engine/conversion/downcasthelpers~convertCollapsedSelection}
 * which does the opposite by breaking attributes in the selection position.
 *
 * @returns {Function} Selection converter.
 */
function clearAttributes() {
    return (evt, data, conversionApi) => {
        const viewWriter = conversionApi.writer;
        const viewSelection = viewWriter.document.selection;
        for (const range of viewSelection.getRanges()) {
            // Not collapsed selection should not have artifacts.
            if (range.isCollapsed) {
                // Position might be in the node removed by the view writer.
                if (range.end.parent.isAttached()) {
                    conversionApi.writer.mergeAttributes(range.start);
                }
            }
        }
        viewWriter.setSelection(null);
    };
}
/**
 * Function factory that creates a converter which converts the set/change/remove attribute changes from the model to the view.
 * It can also be used to convert selection attributes. In that case, an empty attribute element will be created and the
 * selection will be put inside it.
 *
 * Attributes from the model are converted to a view element that will be wrapping these view nodes that are bound to
 * model elements having the given attribute. This is useful for attributes like `bold` that may be set on text nodes in the model
 * but are represented as an element in the view:
 *
 *		[paragraph]              MODEL ====> VIEW        <p>
 *			|- a {bold: true}                             |- <b>
 *			|- b {bold: true}                             |   |- ab
 *			|- c                                          |- c
 *
 * Passed `Function` will be provided with the attribute value and then all the parameters of the
 * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute `attribute` event}.
 * It is expected that the function returns an {@link module:engine/view/element~Element}.
 * The result of the function will be the wrapping element.
 * When the provided `Function` does not return any element, no conversion will take place.
 *
 * The converter automatically consumes the corresponding value from the consumables list and stops the event (see
 * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}).
 *
 *		modelDispatcher.on( 'attribute:bold', wrap( ( modelAttributeValue, { writer } ) => {
 *			return writer.createAttributeElement( 'strong' );
 *		} );
 *
 * @protected
 * @param {Function} elementCreator Function returning a view element that will be used for wrapping.
 * @returns {Function} Set/change attribute converter.
 */
function wrap(elementCreator) {
    return (evt, data, conversionApi) => {
        if (!conversionApi.consumable.test(data.item, evt.name)) {
            return;
        }
        // Recreate current wrapping node. It will be used to unwrap view range if the attribute value has changed
        // or the attribute was removed.
        const oldViewElement = elementCreator(data.attributeOldValue, conversionApi, data);
        // Create node to wrap with.
        const newViewElement = elementCreator(data.attributeNewValue, conversionApi, data);
        if (!oldViewElement && !newViewElement) {
            return;
        }
        conversionApi.consumable.consume(data.item, evt.name);
        const viewWriter = conversionApi.writer;
        const viewSelection = viewWriter.document.selection;
        if (data.item instanceof selection_Selection || data.item instanceof documentselection_DocumentSelection) {
            // Selection attribute conversion.
            viewWriter.wrap(viewSelection.getFirstRange(), newViewElement);
        }
        else {
            // Node attribute conversion.
            let viewRange = conversionApi.mapper.toViewRange(data.range);
            // First, unwrap the range from current wrapper.
            if (data.attributeOldValue !== null && oldViewElement) {
                viewRange = viewWriter.unwrap(viewRange, oldViewElement);
            }
            if (data.attributeNewValue !== null && newViewElement) {
                viewWriter.wrap(viewRange, newViewElement);
            }
        }
    };
}
/**
 * Function factory that creates a converter which converts node insertion changes from the model to the view.
 * The function passed will be provided with all the parameters of the dispatcher's
 * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert `insert` event}.
 * It is expected that the function returns an {@link module:engine/view/element~Element}.
 * The result of the function will be inserted into the view.
 *
 * The converter automatically consumes the corresponding value from the consumables list and binds the model and view elements.
 *
 *		downcastDispatcher.on(
 *			'insert:myElem',
 *			insertElement( ( modelItem, { writer } ) => {
 *				const text = writer.createText( 'myText' );
 *				const myElem = writer.createElement( 'myElem', { myAttr: 'my-' + modelItem.getAttribute( 'myAttr' ) }, text );
 *
 *				// Do something fancy with `myElem` using `modelItem` or other parameters.
 *
 *				return myElem;
 *			}
 *		) );
 *
 * @protected
 * @param {Function} elementCreator Function returning a view element, which will be inserted.
 * @param {module:engine/conversion/downcasthelpers~ConsumerFunction} [consumer] Function defining element consumption process.
 * By default this function just consume passed item insertion.
 * @returns {Function} Insert element event converter.
 */
function insertElement(elementCreator, consumer = defaultConsumer) {
    return (evt, data, conversionApi) => {
        if (!consumer(data.item, conversionApi.consumable, { preflight: true })) {
            return;
        }
        const viewElement = elementCreator(data.item, conversionApi, data);
        if (!viewElement) {
            return;
        }
        // Consume an element insertion and all present attributes that are specified as a reconversion triggers.
        consumer(data.item, conversionApi.consumable);
        const viewPosition = conversionApi.mapper.toViewPosition(data.range.start);
        conversionApi.mapper.bindElements(data.item, viewElement);
        conversionApi.writer.insert(viewPosition, viewElement);
        // Convert attributes before converting children.
        conversionApi.convertAttributes(data.item);
        // Convert children or reinsert previous view elements.
        reinsertOrConvertNodes(viewElement, data.item.getChildren(), conversionApi, { reconversion: data.reconversion });
    };
}
/**
 * Function factory that creates a converter which converts a single model node insertion to a view structure.
 *
 * It is expected that the passed element creator function returns an {@link module:engine/view/element~Element} with attached slots
 * created with `writer.createSlot()` to indicate where child nodes should be converted.
 *
 * @see module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure
 *
 * @protected
 * @param {module:engine/conversion/downcasthelpers~StructureCreatorFunction} elementCreator Function returning a view structure,
 * which will be inserted.
 * @param {module:engine/conversion/downcasthelpers~ConsumerFunction} consumer A callback that is expected to consume all the consumables
 * that were used by the element creator.
 * @returns {Function} Insert element event converter.
*/
function insertStructure(elementCreator, consumer) {
    return (evt, data, conversionApi) => {
        if (!consumer(data.item, conversionApi.consumable, { preflight: true })) {
            return;
        }
        const slotsMap = new Map();
        conversionApi.writer._registerSlotFactory(createSlotFactory(data.item, slotsMap, conversionApi));
        // View creation.
        const viewElement = elementCreator(data.item, conversionApi, data);
        conversionApi.writer._clearSlotFactory();
        if (!viewElement) {
            return;
        }
        // Check if all children are covered by slots and there is no child that landed in multiple slots.
        validateSlotsChildren(data.item, slotsMap, conversionApi);
        // Consume an element insertion and all present attributes that are specified as a reconversion triggers.
        consumer(data.item, conversionApi.consumable);
        const viewPosition = conversionApi.mapper.toViewPosition(data.range.start);
        conversionApi.mapper.bindElements(data.item, viewElement);
        conversionApi.writer.insert(viewPosition, viewElement);
        // Convert attributes before converting children.
        conversionApi.convertAttributes(data.item);
        // Fill view slots with previous view elements or create new ones.
        fillSlots(viewElement, slotsMap, conversionApi, { reconversion: data.reconversion });
    };
}
/**
 * Function factory that creates a converter which converts marker adding change to the
 * {@link module:engine/view/uielement~UIElement view UI element}.
 *
 * The view UI element that will be added to the view depends on the passed parameter. See {@link ~insertElement}.
 * In case of a non-collapsed range, the UI element will not wrap nodes but separate elements will be placed at the beginning
 * and at the end of the range.
 *
 * This converter binds created UI elements with the marker name using {@link module:engine/conversion/mapper~Mapper#bindElementToMarker}.
 *
 * @protected
 * @param {module:engine/view/uielement~UIElement|Function} elementCreator A view UI element or a function returning the view element
 * that will be inserted.
 * @returns {Function} Insert element event converter.
 */
function insertUIElement(elementCreator) {
    return (evt, data, conversionApi) => {
        // Create two view elements. One will be inserted at the beginning of marker, one at the end.
        // If marker is collapsed, only "opening" element will be inserted.
        data.isOpening = true;
        const viewStartElement = elementCreator(data, conversionApi);
        data.isOpening = false;
        const viewEndElement = elementCreator(data, conversionApi);
        if (!viewStartElement || !viewEndElement) {
            return;
        }
        const markerRange = data.markerRange;
        // Marker that is collapsed has consumable build differently that non-collapsed one.
        // For more information see `addMarker` event description.
        // If marker's range is collapsed - check if it can be consumed.
        if (markerRange.isCollapsed && !conversionApi.consumable.consume(markerRange, evt.name)) {
            return;
        }
        // If marker's range is not collapsed - consume all items inside.
        for (const value of markerRange) {
            if (!conversionApi.consumable.consume(value.item, evt.name)) {
                return;
            }
        }
        const mapper = conversionApi.mapper;
        const viewWriter = conversionApi.writer;
        // Add "opening" element.
        viewWriter.insert(mapper.toViewPosition(markerRange.start), viewStartElement);
        conversionApi.mapper.bindElementToMarker(viewStartElement, data.markerName);
        // Add "closing" element only if range is not collapsed.
        if (!markerRange.isCollapsed) {
            viewWriter.insert(mapper.toViewPosition(markerRange.end), viewEndElement);
            conversionApi.mapper.bindElementToMarker(viewEndElement, data.markerName);
        }
        evt.stop();
    };
}
// Function factory that returns a default downcast converter for removing a {@link module:engine/view/uielement~UIElement UI element}
// based on marker remove change.
//
// This converter unbinds elements from the marker name.
//
// @returns {Function} Removed UI element converter.
function removeUIElement() {
    return (evt, data, conversionApi) => {
        const elements = conversionApi.mapper.markerNameToElements(data.markerName);
        if (!elements) {
            return;
        }
        for (const element of elements) {
            conversionApi.mapper.unbindElementFromMarkerName(element, data.markerName);
            conversionApi.writer.clear(conversionApi.writer.createRangeOn(element), element);
        }
        conversionApi.writer.clearClonedElementsGroup(data.markerName);
        evt.stop();
    };
}
// Function factory that creates a default converter for model markers.
//
// See {@link DowncastHelpers#markerToData} for more information what type of view is generated.
//
// This converter binds created UI elements and affected view elements with the marker name
// using {@link module:engine/conversion/mapper~Mapper#bindElementToMarker}.
//
// @returns {Function} Add marker converter.
function insertMarkerData(viewCreator) {
    return (evt, data, conversionApi) => {
        const viewMarkerData = viewCreator(data.markerName, conversionApi);
        if (!viewMarkerData) {
            return;
        }
        const markerRange = data.markerRange;
        if (!conversionApi.consumable.consume(markerRange, evt.name)) {
            return;
        }
        // Adding closing data first to keep the proper order in the view.
        handleMarkerBoundary(markerRange, false, conversionApi, data, viewMarkerData);
        handleMarkerBoundary(markerRange, true, conversionApi, data, viewMarkerData);
        evt.stop();
    };
}
// Helper function for `insertMarkerData()` that marks a marker boundary at the beginning or end of given `range`.
function handleMarkerBoundary(range, isStart, conversionApi, data, viewMarkerData) {
    const modelPosition = isStart ? range.start : range.end;
    const elementAfter = modelPosition.nodeAfter && modelPosition.nodeAfter.is('element') ? modelPosition.nodeAfter : null;
    const elementBefore = modelPosition.nodeBefore && modelPosition.nodeBefore.is('element') ? modelPosition.nodeBefore : null;
    if (elementAfter || elementBefore) {
        let modelElement;
        let isBefore;
        // If possible, we want to add `data-group-start-before` and `data-group-end-after` attributes.
        if (isStart && elementAfter || !isStart && !elementBefore) {
            // [<elementAfter>...</elementAfter> -> <elementAfter data-group-start-before="...">...</elementAfter>
            // <parent>]<elementAfter> -> <parent><elementAfter data-group-end-before="...">
            modelElement = elementAfter;
            isBefore = true;
        }
        else {
            // <elementBefore>...</elementBefore>] -> <elementBefore data-group-end-after="...">...</elementBefore>
            // </elementBefore>[</parent> -> </elementBefore data-group-start-after="..."></parent>
            modelElement = elementBefore;
            isBefore = false;
        }
        const viewElement = conversionApi.mapper.toViewElement(modelElement);
        // In rare circumstances, the model element may be not mapped to any view element and that would cause an error.
        // One of those situations is a soft break inside code block.
        if (viewElement) {
            insertMarkerAsAttribute(viewElement, isStart, isBefore, conversionApi, data, viewMarkerData);
            return;
        }
    }
    const viewPosition = conversionApi.mapper.toViewPosition(modelPosition);
    insertMarkerAsElement(viewPosition, isStart, conversionApi, data, viewMarkerData);
}
// Helper function for `insertMarkerData()` that marks a marker boundary in the view as an attribute on a view element.
function insertMarkerAsAttribute(viewElement, isStart, isBefore, conversionApi, data, viewMarkerData) {
    const attributeName = `data-${viewMarkerData.group}-${isStart ? 'start' : 'end'}-${isBefore ? 'before' : 'after'}`;
    const markerNames = viewElement.hasAttribute(attributeName) ? viewElement.getAttribute(attributeName).split(',') : [];
    // Adding marker name at the beginning to have the same order in the attribute as there is with marker elements.
    markerNames.unshift(viewMarkerData.name);
    conversionApi.writer.setAttribute(attributeName, markerNames.join(','), viewElement);
    conversionApi.mapper.bindElementToMarker(viewElement, data.markerName);
}
// Helper function for `insertMarkerData()` that marks a marker boundary in the view as a separate view ui element.
function insertMarkerAsElement(position, isStart, conversionApi, data, viewMarkerData) {
    const viewElementName = `${viewMarkerData.group}-${isStart ? 'start' : 'end'}`;
    const attrs = viewMarkerData.name ? { 'name': viewMarkerData.name } : null;
    const viewElement = conversionApi.writer.createUIElement(viewElementName, attrs);
    conversionApi.writer.insert(position, viewElement);
    conversionApi.mapper.bindElementToMarker(viewElement, data.markerName);
}
// Function factory that creates a converter for removing a model marker data added by the {@link #insertMarkerData} converter.
//
// @returns {Function} Remove marker converter.
function removeMarkerData(viewCreator) {
    return (evt, data, conversionApi) => {
        const viewData = viewCreator(data.markerName, conversionApi);
        if (!viewData) {
            return;
        }
        const elements = conversionApi.mapper.markerNameToElements(data.markerName);
        if (!elements) {
            return;
        }
        for (const element of elements) {
            conversionApi.mapper.unbindElementFromMarkerName(element, data.markerName);
            if (element.is('containerElement')) {
                removeMarkerFromAttribute(`data-${viewData.group}-start-before`, element);
                removeMarkerFromAttribute(`data-${viewData.group}-start-after`, element);
                removeMarkerFromAttribute(`data-${viewData.group}-end-before`, element);
                removeMarkerFromAttribute(`data-${viewData.group}-end-after`, element);
            }
            else {
                conversionApi.writer.clear(conversionApi.writer.createRangeOn(element), element);
            }
        }
        conversionApi.writer.clearClonedElementsGroup(data.markerName);
        evt.stop();
        function removeMarkerFromAttribute(attributeName, element) {
            if (element.hasAttribute(attributeName)) {
                const markerNames = new Set(element.getAttribute(attributeName).split(','));
                markerNames.delete(viewData.name);
                if (markerNames.size == 0) {
                    conversionApi.writer.removeAttribute(attributeName, element);
                }
                else {
                    conversionApi.writer.setAttribute(attributeName, Array.from(markerNames).join(','), element);
                }
            }
        }
    };
}
// Function factory that creates a converter which converts the set/change/remove attribute changes from the model to the view.
//
// Attributes from the model are converted to the view element attributes in the view. You may provide a custom function to generate
// a key-value attribute pair to add/change/remove. If not provided, model attributes will be converted to view element
// attributes on a one-to-one basis.
//
// *Note:** The provided attribute creator should always return the same `key` for a given attribute from the model.
//
// The converter automatically consumes the corresponding value from the consumables list and stops the event (see
// {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher}).
//
//		modelDispatcher.on( 'attribute:customAttr:myElem', changeAttribute( ( value, data ) => {
//			// Change attribute key from `customAttr` to `class` in the view.
//			const key = 'class';
//			let value = data.attributeNewValue;
//
//			// Force attribute value to 'empty' if the model element is empty.
//			if ( data.item.childCount === 0 ) {
//				value = 'empty';
//			}
//
//			// Return the key-value pair.
//			return { key, value };
//		} ) );
//
// @param {Function} [attributeCreator] Function returning an object with two properties: `key` and `value`, which
// represent the attribute key and attribute value to be set on a {@link module:engine/view/element~Element view element}.
// The function is passed the model attribute value as the first parameter and additional data about the change as the second parameter.
// @returns {Function} Set/change attribute converter.
function changeAttribute(attributeCreator) {
    return (evt, data, conversionApi) => {
        if (!conversionApi.consumable.test(data.item, evt.name)) {
            return;
        }
        const oldAttribute = attributeCreator(data.attributeOldValue, conversionApi, data);
        const newAttribute = attributeCreator(data.attributeNewValue, conversionApi, data);
        if (!oldAttribute && !newAttribute) {
            return;
        }
        conversionApi.consumable.consume(data.item, evt.name);
        const viewElement = conversionApi.mapper.toViewElement(data.item);
        const viewWriter = conversionApi.writer;
        // If model item cannot be mapped to a view element, it means item is not an `Element` instance but a `TextProxy` node.
        // Only elements can have attributes in a view so do not proceed for anything else (#1587).
        if (!viewElement) {
            /**
             * This error occurs when a {@link module:engine/model/textproxy~TextProxy text node's} attribute is to be downcasted
             * by an {@link module:engine/conversion/conversion~Conversion#attributeToAttribute `Attribute to Attribute converter`}.
             * In most cases it is caused by converters misconfiguration when only "generic" converter is defined:
             *
             *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
             *			model: 'attribute-name',
             *			view: 'attribute-name'
             *		} ) );
             *
             * and given attribute is used on text node, for example:
             *
             *		model.change( writer => {
             *			writer.insertText( 'Foo', { 'attribute-name': 'bar' }, parent, 0 );
             *		} );
             *
             * In such cases, to convert the same attribute for both {@link module:engine/model/element~Element}
             * and {@link module:engine/model/textproxy~TextProxy `Text`} nodes, text specific
             * {@link module:engine/conversion/conversion~Conversion#attributeToElement `Attribute to Element converter`}
             * with higher {@link module:utils/priorities~PriorityString priority} must also be defined:
             *
             *		editor.conversion.for( 'downcast' ).attributeToElement( {
             *			model: {
             *				key: 'attribute-name',
             *				name: '$text'
             *			},
             *			view: ( value, { writer } ) => {
             *				return writer.createAttributeElement( 'span', { 'attribute-name': value } );
             *			},
             *			converterPriority: 'high'
             *		} ) );
             *
             * @error conversion-attribute-to-attribute-on-text
             */
            throw new CKEditorError('conversion-attribute-to-attribute-on-text', conversionApi.dispatcher, data);
        }
        // First remove the old attribute if there was one.
        if (data.attributeOldValue !== null && oldAttribute) {
            if (oldAttribute.key == 'class') {
                const classes = toArray(oldAttribute.value);
                for (const className of classes) {
                    viewWriter.removeClass(className, viewElement);
                }
            }
            else if (oldAttribute.key == 'style') {
                const keys = Object.keys(oldAttribute.value);
                for (const key of keys) {
                    viewWriter.removeStyle(key, viewElement);
                }
            }
            else {
                viewWriter.removeAttribute(oldAttribute.key, viewElement);
            }
        }
        // Then set the new attribute.
        if (data.attributeNewValue !== null && newAttribute) {
            if (newAttribute.key == 'class') {
                const classes = toArray(newAttribute.value);
                for (const className of classes) {
                    viewWriter.addClass(className, viewElement);
                }
            }
            else if (newAttribute.key == 'style') {
                const keys = Object.keys(newAttribute.value);
                for (const key of keys) {
                    viewWriter.setStyle(key, newAttribute.value[key], viewElement);
                }
            }
            else {
                viewWriter.setAttribute(newAttribute.key, newAttribute.value, viewElement);
            }
        }
    };
}
// Function factory that creates a converter which converts the text inside marker's range. The converter wraps the text with
// {@link module:engine/view/attributeelement~AttributeElement} created from the provided descriptor.
// See {link module:engine/conversion/downcasthelpers~createViewElementFromHighlightDescriptor}.
//
// It can also be used to convert the selection that is inside a marker. In that case, an empty attribute element will be
// created and the selection will be put inside it.
//
// If the highlight descriptor does not provide the `priority` property, `10` will be used.
//
// If the highlight descriptor does not provide the `id` property, the name of the marker will be used.
//
// This converter binds the created {@link module:engine/view/attributeelement~AttributeElement attribute elemens} with the marker name
// using the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker} method.
//
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} highlightDescriptor
// @returns {Function}
function highlightText(highlightDescriptor) {
    return (evt, data, conversionApi) => {
        if (!data.item) {
            return;
        }
        if (!(data.item instanceof selection_Selection || data.item instanceof documentselection_DocumentSelection) && !data.item.is('$textProxy')) {
            return;
        }
        const descriptor = prepareDescriptor(highlightDescriptor, data, conversionApi);
        if (!descriptor) {
            return;
        }
        if (!conversionApi.consumable.consume(data.item, evt.name)) {
            return;
        }
        const viewWriter = conversionApi.writer;
        const viewElement = createViewElementFromHighlightDescriptor(viewWriter, descriptor);
        const viewSelection = viewWriter.document.selection;
        if (data.item instanceof selection_Selection || data.item instanceof documentselection_DocumentSelection) {
            viewWriter.wrap(viewSelection.getFirstRange(), viewElement);
        }
        else {
            const viewRange = conversionApi.mapper.toViewRange(data.range);
            const rangeAfterWrap = viewWriter.wrap(viewRange, viewElement);
            for (const element of rangeAfterWrap.getItems()) {
                if (element.is('attributeElement') && element.isSimilar(viewElement)) {
                    conversionApi.mapper.bindElementToMarker(element, data.markerName);
                    // One attribute element is enough, because all of them are bound together by the view writer.
                    // Mapper uses this binding to get all the elements no matter how many of them are registered in the mapper.
                    break;
                }
            }
        }
    };
}
// Converter function factory. It creates a function which applies the marker's highlight to an element inside the marker's range.
//
// The converter checks if an element has the `addHighlight` function stored as a
// {@link module:engine/view/element~Element#_setCustomProperty custom property} and, if so, uses it to apply the highlight.
// In such case the converter will consume all element's children, assuming that they were handled by the element itself.
//
// When the `addHighlight` custom property is not present, the element is not converted in any special way.
// This means that converters will proceed to convert the element's child nodes.
//
// If the highlight descriptor does not provide the `priority` property, `10` will be used.
//
// If the highlight descriptor does not provide the `id` property, the name of the marker will be used.
//
// This converter binds altered {@link module:engine/view/containerelement~ContainerElement container elements} with the marker name using
// the {@link module:engine/conversion/mapper~Mapper#bindElementToMarker} method.
//
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} highlightDescriptor
// @returns {Function}
function highlightElement(highlightDescriptor) {
    return (evt, data, conversionApi) => {
        if (!data.item) {
            return;
        }
        if (!(data.item instanceof element_Element)) {
            return;
        }
        const descriptor = prepareDescriptor(highlightDescriptor, data, conversionApi);
        if (!descriptor) {
            return;
        }
        if (!conversionApi.consumable.test(data.item, evt.name)) {
            return;
        }
        const viewElement = conversionApi.mapper.toViewElement(data.item);
        if (viewElement && viewElement.getCustomProperty('addHighlight')) {
            // Consume element itself.
            conversionApi.consumable.consume(data.item, evt.name);
            // Consume all children nodes.
            for (const value of range_Range._createIn(data.item)) {
                conversionApi.consumable.consume(value.item, evt.name);
            }
            const addHighlightCallback = viewElement.getCustomProperty('addHighlight');
            addHighlightCallback(viewElement, descriptor, conversionApi.writer);
            conversionApi.mapper.bindElementToMarker(viewElement, data.markerName);
        }
    };
}
// Function factory that creates a converter which converts the removing model marker to the view.
//
// Both text nodes and elements are handled by this converter but they are handled a bit differently.
//
// Text nodes are unwrapped using the {@link module:engine/view/attributeelement~AttributeElement attribute element} created from the
// provided highlight descriptor. See {link module:engine/conversion/downcasthelpers~HighlightDescriptor}.
//
// For elements, the converter checks if an element has the `removeHighlight` function stored as a
// {@link module:engine/view/element~Element#_setCustomProperty custom property}. If so, it uses it to remove the highlight.
// In such case, the children of that element will not be converted.
//
// When `removeHighlight` is not present, the element is not converted in any special way.
// The converter will proceed to convert the element's child nodes instead.
//
// If the highlight descriptor does not provide the `priority` property, `10` will be used.
//
// If the highlight descriptor does not provide the `id` property, the name of the marker will be used.
//
// This converter unbinds elements from the marker name.
//
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} highlightDescriptor
// @returns {Function}
function removeHighlight(highlightDescriptor) {
    return (evt, data, conversionApi) => {
        // This conversion makes sense only for non-collapsed range.
        if (data.markerRange.isCollapsed) {
            return;
        }
        const descriptor = prepareDescriptor(highlightDescriptor, data, conversionApi);
        if (!descriptor) {
            return;
        }
        // View element that will be used to unwrap `AttributeElement`s.
        const viewHighlightElement = createViewElementFromHighlightDescriptor(conversionApi.writer, descriptor);
        // Get all elements bound with given marker name.
        const elements = conversionApi.mapper.markerNameToElements(data.markerName);
        if (!elements) {
            return;
        }
        for (const element of elements) {
            conversionApi.mapper.unbindElementFromMarkerName(element, data.markerName);
            if (element.is('attributeElement')) {
                conversionApi.writer.unwrap(conversionApi.writer.createRangeOn(element), viewHighlightElement);
            }
            else {
                // if element.is( 'containerElement' ).
                const removeHighlightCallback = element.getCustomProperty('removeHighlight');
                removeHighlightCallback(element, descriptor.id, conversionApi.writer);
            }
        }
        conversionApi.writer.clearClonedElementsGroup(data.markerName);
        evt.stop();
    };
}
// Model element to view element conversion helper.
//
// See {@link ~DowncastHelpers#elementToElement `.elementToElement()` downcast helper} for examples and config params description.
//
// @param {Object} config Conversion configuration.
// @param {String|Object} config.model The description or a name of the model element to convert.
// @param {String|Array.<String>} [config.model.attributes] List of attributes triggering element reconversion.
// @param {Boolean} [config.model.children] Should reconvert element if the list of model child nodes changed.
// @param {module:engine/view/elementdefinition~ElementDefinition|module:engine/conversion/downcasthelpers~ElementCreatorFunction}
// config.view
// @returns {Function} Conversion helper.
function downcastElementToElement(config) {
    const model = normalizeModelElementConfig(config.model);
    const view = normalizeToElementConfig(config.view, 'container');
    // Trigger reconversion on children list change if element is a subject to any reconversion.
    // This is required to be able to trigger Differ#refreshItem() on a direct child of the reconverted element.
    if (model.attributes.length) {
        model.children = true;
    }
    return (dispatcher) => {
        dispatcher.on(`insert:${model.name}`, insertElement(view, createConsumer(model)), { priority: config.converterPriority || 'normal' });
        if (model.children || model.attributes.length) {
            dispatcher.on('reduceChanges', createChangeReducer(model), { priority: 'low' });
        }
    };
}
// Model element to view structure conversion helper.
//
// See {@link ~DowncastHelpers#elementToStructure `.elementToStructure()` downcast helper} for examples and config params description.
//
// @param {Object} config Conversion configuration.
// @param {String|Object} config.model
// @param {String} [config.model.name]
// @param {Array.<String>} [config.model.attributes]
// @param {module:engine/conversion/downcasthelpers~StructureCreatorFunction} config.view
// @returns {Function} Conversion helper.
function downcastElementToStructure(config) {
    const model = normalizeModelElementConfig(config.model);
    const view = normalizeToElementConfig(config.view, 'container');
    // Trigger reconversion on children list change because it always needs to use slots to put children in proper places.
    // This is required to be able to trigger Differ#refreshItem() on a direct child of the reconverted element.
    model.children = true;
    return (dispatcher) => {
        if (dispatcher._conversionApi.schema.checkChild(model.name, '$text')) {
            /**
             * This error occurs when a {@link module:engine/model/element~Element model element} is downcasted
             * via {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToStructure} helper but the element was
             * allowed to host `$text` by the {@link module:engine/model/schema~Schema model schema}.
             *
             * For instance, this may be the result of `myElement` allowing the content of
             * {@glink framework/guides/deep-dive/schema#generic-items `$block`} in its schema definition:
             *
             *		// Element definition in schema.
             *		schema.register( 'myElement', {
             *			allowContentOf: '$block',
             *
             *			// ...
             *		} );
             *
             *		// ...
             *
             *		// Conversion of myElement with the use of elementToStructure().
             *		editor.conversion.for( 'downcast' ).elementToStructure( {
             *			model: 'myElement',
             *			view: ( modelElement, { writer } ) => {
             *				// ...
             *			}
             *		} );
             *
             * In such case, {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement `elementToElement()`} helper
             * can be used instead to get around this problem:
             *
             *		editor.conversion.for( 'downcast' ).elementToElement( {
             *			model: 'myElement',
             *			view: ( modelElement, { writer } ) => {
             *				// ...
             *			}
             *		} );
             *
             * @error conversion-element-to-structure-disallowed-text
             * @param {String} elementName The name of the element the structure is to be created for.
             */
            throw new CKEditorError('conversion-element-to-structure-disallowed-text', dispatcher, { elementName: model.name });
        }
        dispatcher.on(`insert:${model.name}`, insertStructure(view, createConsumer(model)), { priority: config.converterPriority || 'normal' });
        dispatcher.on('reduceChanges', createChangeReducer(model), { priority: 'low' });
    };
}
// Model attribute to view element conversion helper.
//
// See {@link ~DowncastHelpers#attributeToElement `.attributeToElement()` downcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values }` object. `values` is an array
// of `String`s with possible values if the model attribute is an enumerable.
// @param {module:engine/view/elementdefinition~ElementDefinition|module:engine/conversion/downcasthelpers~AttributeElementCreatorFunction|
// Object} config.view A view element definition or a function that takes the model attribute value and
// {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer} as parameters and returns a view attribute element.
// If `config.model.values` is given, `config.view` should be an object assigning values from `config.model.values` to view element
// definitions or functions.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
// @returns {Function} Conversion helper.
function downcastAttributeToElement(config) {
    config = lodash_es_cloneDeep(config);
    let model = config.model;
    if (typeof model == 'string') {
        model = { key: model };
    }
    let eventName = `attribute:${model.key}`;
    if (model.name) {
        eventName += ':' + model.name;
    }
    if (model.values) {
        for (const modelValue of model.values) {
            config.view[modelValue] = normalizeToElementConfig(config.view[modelValue], 'attribute');
        }
    }
    else {
        config.view = normalizeToElementConfig(config.view, 'attribute');
    }
    const elementCreator = getFromAttributeCreator(config);
    return (dispatcher) => {
        dispatcher.on(eventName, wrap(elementCreator), { priority: config.converterPriority || 'normal' });
    };
}
// Model attribute to view attribute conversion helper.
//
// See {@link ~DowncastHelpers#attributeToAttribute `.attributeToAttribute()` downcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values, [ name ] }` object describing
// the attribute key, possible values and, optionally, an element name to convert from.
// @param {String|Object|module:engine/conversion/downcasthelpers~AttributeCreatorFunction} config.view A view attribute key,
// or a `{ key, value }` object or a function that takes the model attribute value and returns a `{ key, value }` object.
// If `key` is `'class'`, `value` can be a `String` or an array of `String`s. If `key` is `'style'`, `value` is an object with
// key-value pairs. In other cases, `value` is a `String`.
// If `config.model.values` is set, `config.view` should be an object assigning values from `config.model.values` to
// `{ key, value }` objects or a functions.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
// @returns {Function} Conversion helper.
function downcastAttributeToAttribute(config) {
    config = lodash_es_cloneDeep(config);
    let model = config.model;
    if (typeof model == 'string') {
        model = { key: model };
    }
    let eventName = `attribute:${model.key}`;
    if (model.name) {
        eventName += ':' + model.name;
    }
    if (model.values) {
        for (const modelValue of model.values) {
            config.view[modelValue] = normalizeToAttributeConfig(config.view[modelValue]);
        }
    }
    else {
        config.view = normalizeToAttributeConfig(config.view);
    }
    const elementCreator = getFromAttributeCreator(config);
    return (dispatcher) => {
        dispatcher.on(eventName, changeAttribute(elementCreator), { priority: config.converterPriority || 'normal' });
    };
}
// Model marker to view element conversion helper.
//
// See {@link ~DowncastHelpers#markerToElement `.markerToElement()` downcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {String} config.model The name of the model marker (or model marker group) to convert.
// @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function
// that takes the model marker data as a parameter and returns a view UI element.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
// @returns {Function} Conversion helper.
function downcastMarkerToElement(config) {
    const view = normalizeToElementConfig(config.view, 'ui');
    return (dispatcher) => {
        dispatcher.on(`addMarker:${config.model}`, insertUIElement(view), { priority: config.converterPriority || 'normal' });
        dispatcher.on(`removeMarker:${config.model}`, removeUIElement(), { priority: config.converterPriority || 'normal' });
    };
}
// Model marker to view data conversion helper.
//
// See {@link ~DowncastHelpers#markerToData `markerToData()` downcast helper} to learn more.
//
// @param {Object} config
// @param {String} config.model
// @param {Function} [config.view]
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal']
// @returns {Function} Conversion helper.
function downcastMarkerToData(config) {
    config = lodash_es_cloneDeep(config);
    const group = config.model;
    let view = config.view;
    // Default conversion.
    if (!view) {
        view = markerName => ({
            group,
            name: markerName.substr(config.model.length + 1)
        });
    }
    return (dispatcher) => {
        dispatcher.on(`addMarker:${group}`, insertMarkerData(view), { priority: config.converterPriority || 'normal' });
        dispatcher.on(`removeMarker:${group}`, removeMarkerData(view), { priority: config.converterPriority || 'normal' });
    };
}
// Model marker to highlight conversion helper.
//
// See {@link ~DowncastHelpers#markerToElement `.markerToElement()` downcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {String} config.model The name of the model marker (or model marker group) to convert.
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} config.view A highlight descriptor
// that will be used for highlighting or a function that takes the model marker data as a parameter and returns a highlight descriptor.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
// @returns {Function} Conversion helper.
function downcastMarkerToHighlight(config) {
    return (dispatcher) => {
        dispatcher.on(`addMarker:${config.model}`, highlightText(config.view), { priority: config.converterPriority || 'normal' });
        dispatcher.on(`addMarker:${config.model}`, highlightElement(config.view), { priority: config.converterPriority || 'normal' });
        dispatcher.on(`removeMarker:${config.model}`, removeHighlight(config.view), { priority: config.converterPriority || 'normal' });
    };
}
// Takes `config.model`, and converts it to an object with normalized structure.
//
// @param {String|Object} model Model configuration or element name.
// @param {String} model.name
// @param {Array.<String>} [model.attributes]
// @param {Boolean} [model.children]
// @returns {Object}
function normalizeModelElementConfig(model) {
    if (typeof model == 'string') {
        model = { name: model };
    }
    // List of attributes that should trigger reconversion.
    if (!model.attributes) {
        model.attributes = [];
    }
    else if (!Array.isArray(model.attributes)) {
        model.attributes = [model.attributes];
    }
    // Whether a children insertion/deletion should trigger reconversion.
    model.children = !!model.children;
    return model;
}
// Takes `config.view`, and if it is an {@link module:engine/view/elementdefinition~ElementDefinition}, converts it
// to a function (because lower level converters accept only element creator functions).
//
// @param {module:engine/view/elementdefinition~ElementDefinition|Function} view View configuration.
// @param {'container'|'attribute'|'ui'} viewElementType View element type to create.
// @returns {Function} Element creator function to use in lower level converters.
function normalizeToElementConfig(view, viewElementType) {
    if (typeof view == 'function') {
        // If `view` is already a function, don't do anything.
        return view;
    }
    return ((modelData, conversionApi) => createViewElementFromDefinition(view, conversionApi, viewElementType));
}
// Creates a view element instance from the provided {@link module:engine/view/elementdefinition~ElementDefinition} and class.
//
// @param {module:engine/view/elementdefinition~ElementDefinition} viewElementDefinition
// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
// @param {'container'|'attribute'|'ui'} viewElementType
// @returns {module:engine/view/element~Element}
function createViewElementFromDefinition(viewElementDefinition, conversionApi, viewElementType) {
    if (typeof viewElementDefinition == 'string') {
        // If `viewElementDefinition` is given as a `String`, normalize it to an object with `name` property.
        viewElementDefinition = { name: viewElementDefinition };
    }
    let element;
    const viewWriter = conversionApi.writer;
    const attributes = Object.assign({}, viewElementDefinition.attributes);
    if (viewElementType == 'container') {
        element = viewWriter.createContainerElement(viewElementDefinition.name, attributes);
    }
    else if (viewElementType == 'attribute') {
        const options = {
            priority: viewElementDefinition.priority || AttributeElement.DEFAULT_PRIORITY
        };
        element = viewWriter.createAttributeElement(viewElementDefinition.name, attributes, options);
    }
    else {
        // 'ui'.
        element = viewWriter.createUIElement(viewElementDefinition.name, attributes);
    }
    if (viewElementDefinition.styles) {
        const keys = Object.keys(viewElementDefinition.styles);
        for (const key of keys) {
            viewWriter.setStyle(key, viewElementDefinition.styles[key], element);
        }
    }
    if (viewElementDefinition.classes) {
        const classes = viewElementDefinition.classes;
        if (typeof classes == 'string') {
            viewWriter.addClass(classes, element);
        }
        else {
            for (const className of classes) {
                viewWriter.addClass(className, element);
            }
        }
    }
    return element;
}
function getFromAttributeCreator(config) {
    if (config.model.values) {
        return ((modelAttributeValue, conversionApi, data) => {
            const view = config.view[modelAttributeValue];
            if (view) {
                return view(modelAttributeValue, conversionApi, data);
            }
            return null;
        });
    }
    else {
        return config.view;
    }
}
// Takes the configuration, adds default parameters if they do not exist and normalizes other parameters to be used in downcast converters
// for generating a view attribute.
//
// @param {Object} view View configuration.
function normalizeToAttributeConfig(view) {
    if (typeof view == 'string') {
        return modelAttributeValue => ({ key: view, value: modelAttributeValue });
    }
    else if (typeof view == 'object') {
        // { key, value, ... }
        if (view.value) {
            return () => view;
        }
        // { key, ... }
        else {
            return modelAttributeValue => ({ key: view.key, value: modelAttributeValue });
        }
    }
    else {
        // function.
        return view;
    }
}
// Helper function for `highlight`. Prepares the actual descriptor object using value passed to the converter.
function prepareDescriptor(highlightDescriptor, data, conversionApi) {
    // If passed descriptor is a creator function, call it. If not, just use passed value.
    const descriptor = typeof highlightDescriptor == 'function' ?
        highlightDescriptor(data, conversionApi) :
        highlightDescriptor;
    if (!descriptor) {
        return null;
    }
    // Apply default descriptor priority.
    if (!descriptor.priority) {
        descriptor.priority = 10;
    }
    // Default descriptor id is marker name.
    if (!descriptor.id) {
        descriptor.id = data.markerName;
    }
    return descriptor;
}
// Creates a function that checks a single differ diff item whether it should trigger reconversion.
//
// @param {Object} model A normalized `config.model` converter configuration.
// @param {String} model.name The name of element.
// @param {Array.<String>} model.attributes The list of attribute names that should trigger reconversion.
// @param {Boolean} [model.children] Whether the child list change should trigger reconversion.
// @returns {Function}
function createChangeReducerCallback(model) {
    return (node, change) => {
        if (!node.is('element', model.name)) {
            return false;
        }
        if (change.type == 'attribute') {
            if (model.attributes.includes(change.attributeKey)) {
                return true;
            }
        }
        else {
            /* istanbul ignore else: This is always true because otherwise it would not register a reducer callback. */
            if (model.children) {
                return true;
            }
        }
        return false;
    };
}
// Creates a `reduceChanges` event handler for reconversion.
//
// @param {Object} model A normalized `config.model` converter configuration.
// @param {String} model.name The name of element.
// @param {Array.<String>} model.attributes The list of attribute names that should trigger reconversion.
// @param {Boolean} [model.children] Whether the child list change should trigger reconversion.
// @returns {Function}
function createChangeReducer(model) {
    const shouldReplace = createChangeReducerCallback(model);
    return (evt, data) => {
        const reducedChanges = [];
        if (!data.reconvertedElements) {
            data.reconvertedElements = new Set();
        }
        for (const change of data.changes) {
            // For attribute use node affected by the change.
            // For insert or remove use parent element because we need to check if it's added/removed child.
            const node = change.type == 'attribute' ? change.range.start.nodeAfter : change.position.parent;
            if (!node || !shouldReplace(node, change)) {
                reducedChanges.push(change);
                continue;
            }
            // If it's already marked for reconversion, so skip this change, otherwise add the diff items.
            if (!data.reconvertedElements.has(node)) {
                data.reconvertedElements.add(node);
                const position = position_Position._createBefore(node);
                reducedChanges.push({
                    type: 'remove',
                    name: node.name,
                    position,
                    length: 1
                }, {
                    type: 'reinsert',
                    name: node.name,
                    position,
                    length: 1
                });
            }
        }
        data.changes = reducedChanges;
    };
}
// Creates a function that checks if an element and its watched attributes can be consumed and consumes them.
//
// @param {Object} model A normalized `config.model` converter configuration.
// @param {String} model.name The name of element.
// @param {Array.<String>} model.attributes The list of attribute names that should trigger reconversion.
// @param {Boolean} [model.children] Whether the child list change should trigger reconversion.
// @returns {module:engine/conversion/downcasthelpers~ConsumerFunction}
function createConsumer(model) {
    return (node, consumable, options = {}) => {
        const events = ['insert'];
        // Collect all set attributes that are triggering conversion.
        for (const attributeName of model.attributes) {
            if (node.hasAttribute(attributeName)) {
                events.push(`attribute:${attributeName}`);
            }
        }
        if (!events.every(event => consumable.test(node, event))) {
            return false;
        }
        if (!options.preflight) {
            events.forEach(event => consumable.consume(node, event));
        }
        return true;
    };
}
// Creates a function that create view slots.
//
// @param {module:engine/model/element~Element} element
// @param {Map.<module:engine/view/element~Element,Array.<module:engine/model/node~Node>>} slotsMap
// @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
// @returns {Function} Exposed by writer as createSlot().
function createSlotFactory(element, slotsMap, conversionApi) {
    return (writer, modeOrFilter = 'children') => {
        const slot = writer.createContainerElement('$slot');
        let children = null;
        if (modeOrFilter === 'children') {
            children = Array.from(element.getChildren());
        }
        else if (typeof modeOrFilter == 'function') {
            children = Array.from(element.getChildren()).filter(element => modeOrFilter(element));
        }
        else {
            /**
             * Unknown slot mode was provided to `writer.createSlot()` in downcast converter.
             *
             * @error conversion-slot-mode-unknown
             */
            throw new CKEditorError('conversion-slot-mode-unknown', conversionApi.dispatcher, { modeOrFilter });
        }
        slotsMap.set(slot, children);
        return slot;
    };
}
// Checks if all children are covered by slots and there is no child that landed in multiple slots.
//
// @param {module:engine/model/element~Element}
// @param {Map.<module:engine/view/element~Element,Array.<module:engine/model/node~Node>>} slotsMap
// @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
function validateSlotsChildren(element, slotsMap, conversionApi) {
    const childrenInSlots = Array.from(slotsMap.values()).flat();
    const uniqueChildrenInSlots = new Set(childrenInSlots);
    if (uniqueChildrenInSlots.size != childrenInSlots.length) {
        /**
         * Filters provided to `writer.createSlot()` overlap (at least two filters accept the same child element).
         *
         * @error conversion-slot-filter-overlap
         * @param {module:engine/model/element~Element} element The element of which children would not be properly
         * allocated to multiple slots.
         */
        throw new CKEditorError('conversion-slot-filter-overlap', conversionApi.dispatcher, { element });
    }
    if (uniqueChildrenInSlots.size != element.childCount) {
        /**
         * Filters provided to `writer.createSlot()` are incomplete and exclude at least one children element (one of
         * the children elements would not be assigned to any of the slots).
         *
         * @error conversion-slot-filter-incomplete
         * @param {module:engine/model/element~Element} element The element of which children would not be properly
         * allocated to multiple slots.
         */
        throw new CKEditorError('conversion-slot-filter-incomplete', conversionApi.dispatcher, { element });
    }
}
// Fill slots with appropriate view elements.
//
// @param {module:engine/view/element~Element} viewElement
// @param {Map.<module:engine/view/element~Element,Array.<module:engine/model/node~Node>>} slotsMap
// @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
// @param {Object} options
// @param {Boolean} [options.reconversion]
function fillSlots(viewElement, slotsMap, conversionApi, options) {
    // Set temporary position mapping to redirect child view elements into a proper slots.
    conversionApi.mapper.on('modelToViewPosition', toViewPositionMapping, { priority: 'highest' });
    let currentSlot = null;
    let currentSlotNodes = null;
    // Fill slots with nested view nodes.
    for ([currentSlot, currentSlotNodes] of slotsMap) {
        reinsertOrConvertNodes(viewElement, currentSlotNodes, conversionApi, options);
        conversionApi.writer.move(conversionApi.writer.createRangeIn(currentSlot), conversionApi.writer.createPositionBefore(currentSlot));
        conversionApi.writer.remove(currentSlot);
    }
    conversionApi.mapper.off('modelToViewPosition', toViewPositionMapping);
    function toViewPositionMapping(evt, data) {
        const element = data.modelPosition.nodeAfter;
        // Find the proper offset within the slot.
        const index = currentSlotNodes.indexOf(element);
        if (index < 0) {
            return;
        }
        data.viewPosition = data.mapper.findPositionIn(currentSlot, index);
    }
}
// Inserts view representation of `nodes` into the `viewElement` either by bringing back just removed view nodes
// or by triggering conversion for them.
//
// @param {module:engine/view/element~Element} viewElement
// @param {Iterable.<module:engine/model/element~Element>} modelNodes
// @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
// @param {Object} options
// @param {Boolean} [options.reconversion]
function reinsertOrConvertNodes(viewElement, modelNodes, conversionApi, options) {
    // Fill with nested view nodes.
    for (const modelChildNode of modelNodes) {
        // Try reinserting the view node for the specified model node...
        if (!reinsertNode(viewElement.root, modelChildNode, conversionApi, options)) {
            // ...or else convert the model element to the view.
            conversionApi.convertItem(modelChildNode);
        }
    }
}
// Checks if the view for the given model element could be reused and reinserts it to the view.
//
// @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} viewRoot
// @param {module:engine/model/element~Element} modelElement
// @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
// @param {Object} options
// @param {Boolean} [options.reconversion]
// @returns {Boolean} `false` if view element can't be reused.
function reinsertNode(viewRoot, modelNode, conversionApi, options) {
    const { writer, mapper } = conversionApi;
    // Don't reinsert if this is not a reconversion...
    if (!options.reconversion) {
        return false;
    }
    const viewChildNode = mapper.toViewElement(modelNode);
    // ...or there is no view to reinsert or it was already inserted to the view structure...
    if (!viewChildNode || viewChildNode.root == viewRoot) {
        return false;
    }
    // ...or it was strictly marked as not to be reused.
    if (!conversionApi.canReuseView(viewChildNode)) {
        return false;
    }
    // Otherwise reinsert the view node.
    writer.move(writer.createRangeOn(viewChildNode), mapper.toViewPosition(position_Position._createBefore(modelNode)));
    return true;
}
// The default consumer for insert events.
// @param {module:engine/model/item~Item} item Model item.
// @param {module:engine/conversion/modelconsumable~ModelConsumable} consumable The model consumable.
// @param {Object} [options]
// @param {Boolean} [options.preflight=false] Whether should consume or just check if can be consumed.
// @returns {Boolean}
function defaultConsumer(item, consumable, { preflight } = {}) {
    if (preflight) {
        return consumable.test(item, 'insert');
    }
    else {
        return consumable.consume(item, 'insert');
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/autoparagraphing.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/autoparagraphing
 */
/**
 * Fixes all empty roots.
 *
 * @protected
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {Boolean} `true` if any change has been applied, `false` otherwise.
 */
function autoParagraphEmptyRoots(writer) {
    const { schema, document } = writer.model;
    for (const rootName of document.getRootNames()) {
        const root = document.getRoot(rootName);
        if (root.isEmpty && !schema.checkChild(root, '$text')) {
            // If paragraph element is allowed in the root, create paragraph element.
            if (schema.checkChild(root, 'paragraph')) {
                writer.insertElement('paragraph', root);
                // Other roots will get fixed in the next post-fixer round. Those will be triggered
                // in the same batch no matter if this method was triggered by the post-fixing or not
                // (the above insertElement call will trigger the post-fixers).
                return true;
            }
        }
    }
    return false;
}
/**
 * Checks if the given node wrapped with a paragraph would be accepted by the schema in the given position.
 *
 * @internal
 * @protected
 * @param {module:engine/model/position~Position} position The position at which to check.
 * @param {module:engine/model/node~Node|String} nodeOrType The child node or child type to check.
 * @param {module:engine/model/schema~Schema} schema A schema instance used for element validation.
 */
function isParagraphable(position, nodeOrType, schema) {
    const context = schema.createContext(position);
    // When paragraph is allowed in this context...
    if (!schema.checkChild(context, 'paragraph')) {
        return false;
    }
    // And a node would be allowed in this paragraph...
    if (!schema.checkChild(context.push('paragraph'), nodeOrType)) {
        return false;
    }
    return true;
}
/**
 * Inserts a new paragraph at the given position and returns a position inside that paragraph.
 *
 * @protected
 * @param {module:engine/model/position~Position} position The position where a paragraph should be inserted.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {module:engine/model/position~Position} Position inside the created paragraph.
 */
function wrapInParagraph(position, writer) {
    const paragraph = writer.createElement('paragraph');
    writer.insert(paragraph, position);
    return writer.createPositionAt(paragraph, 0);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/upcasthelpers.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */





/**
 * Contains the {@link module:engine/view/view view} to {@link module:engine/model/model model} converters for
 * {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher}.
 *
 * @module engine/conversion/upcasthelpers
 */
/**
 * Upcast conversion helper functions.
 *
 * Learn more about {@glink framework/guides/deep-dive/conversion/upcast upcast helpers}.
 *
 * @extends module:engine/conversion/conversionhelpers~ConversionHelpers
 */
class UpcastHelpers extends ConversionHelpers {
    /**
     * View element to model element conversion helper.
     *
     * This conversion results in creating a model element. For example,
     * view `<p>Foo</p>` becomes `<paragraph>Foo</paragraph>` in the model.
     *
     * Keep in mind that the element will be inserted only if it is allowed
     * by {@link module:engine/model/schema~Schema schema} configuration.
     *
     *		editor.conversion.for( 'upcast' ).elementToElement( {
     *			view: 'p',
     *			model: 'paragraph'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToElement( {
     *			view: 'p',
     *			model: 'paragraph',
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToElement( {
     *			view: {
     *				name: 'p',
     *				classes: 'fancy'
     *			},
     *			model: 'fancyParagraph'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToElement( {
     * 			view: {
     *				name: 'p',
     *				classes: 'heading'
     * 			},
     * 			model: ( viewElement, conversionApi ) => {
     * 				const modelWriter = conversionApi.writer;
     *
     * 				return modelWriter.createElement( 'heading', { level: viewElement.getAttribute( 'data-level' ) } );
     * 			}
     * 		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #elementToElement
     * @param {Object} config Conversion configuration.
     * @param {module:engine/view/matcher~MatcherPattern} [config.view] Pattern matching all view elements which should be converted. If not
     * set, the converter will fire for every view element.
     * @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element instance or a
     * function that takes a view element and {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API}
     * and returns a model element. The model element will be inserted in the model.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
     */
    elementToElement(config) {
        return this.add(upcastElementToElement(config));
    }
    /**
     * View element to model attribute conversion helper.
     *
     * This conversion results in setting an attribute on a model node. For example, view `<strong>Foo</strong>` becomes
     * `Foo` {@link module:engine/model/text~Text model text node} with `bold` attribute set to `true`.
     *
     * This helper is meant to set a model attribute on all the elements that are inside the converted element:
     *
     *		<strong>Foo</strong>   -->   <strong><p>Foo</p></strong>   -->   <paragraph><$text bold="true">Foo</$text></paragraph>
     *
     * Above is a sample of HTML code, that goes through autoparagraphing (first step) and then is converted (second step).
     * Even though `<strong>` is over `<p>` element, `bold="true"` was added to the text. See
     * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#attributeToAttribute} for comparison.
     *
     * Keep in mind that the attribute will be set only if it is allowed by {@link module:engine/model/schema~Schema schema} configuration.
     *
     *		editor.conversion.for( 'upcast' ).elementToAttribute( {
     *			view: 'strong',
     *			model: 'bold'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToAttribute( {
     *			view: 'strong',
     *			model: 'bold',
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToAttribute( {
     *			view: {
     *				name: 'span',
     *				classes: 'bold'
     *			},
     *			model: 'bold'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToAttribute( {
     *			view: {
     *				name: 'span',
     *				classes: [ 'styled', 'styled-dark' ]
     *			},
     *			model: {
     *				key: 'styled',
     *				value: 'dark'
     *			}
     *		} );
     *
     * 		editor.conversion.for( 'upcast' ).elementToAttribute( {
     *			view: {
     *				name: 'span',
     *				styles: {
     *					'font-size': /[\s\S]+/
     *				}
     *			},
     *			model: {
     *				key: 'fontSize',
     *				value: ( viewElement, conversionApi ) => {
     *					const fontSize = viewElement.getStyle( 'font-size' );
     *					const value = fontSize.substr( 0, fontSize.length - 2 );
     *
     *					if ( value <= 10 ) {
     *						return 'small';
     *					} else if ( value > 12 ) {
     *						return 'big';
     *					}
     *
     *					return null;
     *				}
     *			}
     *		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #elementToAttribute
     * @param {Object} config Conversion configuration.
     * @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.
     * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
     * the model attribute. `value` property may be set as a function that takes a view element and
     * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.
     * If `String` is given, the model attribute value will be set to `true`.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
     * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
     */
    elementToAttribute(config) {
        return this.add(upcastElementToAttribute(config));
    }
    /**
     * View attribute to model attribute conversion helper.
     *
     * This conversion results in setting an attribute on a model node. For example, view `<img src="foo.jpg"></img>` becomes
     * `<imageBlock source="foo.jpg"></imageBlock>` in the model.
     *
     * This helper is meant to convert view attributes from view elements which got converted to the model, so the view attribute
     * is set only on the corresponding model node:
     *
     *		<div class="dark"><div>foo</div></div>    -->    <div dark="true"><div>foo</div></div>
     *
     * Above, `class="dark"` attribute is added only to the `<div>` elements that has it. This is in contrary to
     * {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToAttribute} which sets attributes for
     * all the children in the model:
     *
     *		<strong>Foo</strong>   -->   <strong><p>Foo</p></strong>   -->   <paragraph><$text bold="true">Foo</$text></paragraph>
     *
     * Above is a sample of HTML code, that goes through autoparagraphing (first step) and then is converted (second step).
     * Even though `<strong>` is over `<p>` element, `bold="true"` was added to the text.
     *
     * Keep in mind that the attribute will be set only if it is allowed by {@link module:engine/model/schema~Schema schema} configuration.
     *
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: 'src',
     *			model: 'source'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: { key: 'src' },
     *			model: 'source'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: { key: 'src' },
     *			model: 'source',
     *			converterPriority: 'normal'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: {
     *				key: 'data-style',
     *				value: /[\s\S]+/
     *			},
     *			model: 'styled'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: {
     *				name: 'img',
     *				key: 'class',
     *				value: 'styled-dark'
     *			},
     *			model: {
     *				key: 'styled',
     *				value: 'dark'
     *			}
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: {
     *				key: 'class',
     *				value: /styled-[\S]+/
     *			},
     *			model: {
     *				key: 'styled'
     *				value: ( viewElement, conversionApi ) => {
     *					const regexp = /styled-([\S]+)/;
     *					const match = viewElement.getAttribute( 'class' ).match( regexp );
     *
     *					return match[ 1 ];
     *				}
     *			}
     *		} );
     *
     * Converting styles works a bit differently as it requires `view.styles` to be an object and by default
     * a model attribute will be set to `true` by such a converter. You can set the model attribute to any value by providing the `value`
     * callback that returns the desired value.
     *
     *		// Default conversion of font-weight style will result in setting bold attribute to true.
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: {
     *				styles: {
     *					'font-weight': 'bold'
     *				}
     *			},
     *			model: 'bold'
     *		} );
     *
     *		// This converter will pass any style value to the `lineHeight` model attribute.
     *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
     *			view: {
     *				styles: {
     *					'line-height': /[\s\S]+/
     *				}
     *			},
     *			model: {
     *				key: 'lineHeight',
     *				value: ( viewElement, conversionApi ) => viewElement.getStyle( 'line-height' )
     *			}
     *		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #attributeToAttribute
     * @param {Object} config Conversion configuration.
     * @param {String|Object} config.view Specifies which view attribute will be converted. If a `String` is passed,
     * attributes with given key will be converted. If an `Object` is passed, it must have a required `key` property,
     * specifying view attribute key, and may have an optional `value` property, specifying view attribute value and optional `name`
     * property specifying a view element name from/on which the attribute should be converted. `value` can be given as a `String`,
     * a `RegExp` or a function callback, that takes view attribute value as the only parameter and returns `Boolean`.
     * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
     * the model attribute. `value` property may be set as a function that takes a view element and
     * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.
     * If `String` is given, the model attribute value will be same as view attribute value.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
     * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
     */
    attributeToAttribute(config) {
        return this.add(upcastAttributeToAttribute(config));
    }
    /**
     * View element to model marker conversion helper.
     *
     * This conversion results in creating a model marker. For example, if the marker was stored in a view as an element:
     * `<p>Fo<span data-marker="comment" data-comment-id="7"></span>o</p><p>B<span data-marker="comment" data-comment-id="7"></span>ar</p>`,
     * after the conversion is done, the marker will be available in
     * {@link module:engine/model/model~Model#markers model document markers}.
     *
     * **Note**: When this helper is used in the data upcast in combination with
     * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToData `#markerToData()`} in the data downcast,
     * then invalid HTML code (e.g. a span between table cells) may be produced by the latter converter.
     *
     * In most of the cases, the {@link #dataToMarker} should be used instead.
     *
     *		editor.conversion.for( 'upcast' ).elementToMarker( {
     *			view: 'marker-search',
     *			model: 'search'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToMarker( {
     *			view: 'marker-search',
     *			model: 'search',
     *			converterPriority: 'high'
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToMarker( {
     *			view: 'marker-search',
     *			model: ( viewElement, conversionApi ) => 'comment:' + viewElement.getAttribute( 'data-comment-id' )
     *		} );
     *
     *		editor.conversion.for( 'upcast' ).elementToMarker( {
     *			view: {
     *				name: 'span',
     *				attributes: {
     *					'data-marker': 'search'
     *				}
     *			},
     *			model: 'search'
     *		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #elementToMarker
     * @param {Object} config Conversion configuration.
     * @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.
     * @param {String|Function} config.model Name of the model marker, or a function that takes a view element and returns
     * a model marker name.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
     */
    elementToMarker(config) {
        return this.add(upcastElementToMarker(config));
    }
    /**
     * View-to-model marker conversion helper.
     *
     * Converts view data created by {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToData `#markerToData()`}
     * back to a model marker.
     *
     * This converter looks for specific view elements and view attributes that mark marker boundaries. See
     * {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToData `#markerToData()`} to learn what view data
     * is expected by this converter.
     *
     * The `config.view` property is equal to the marker group name to convert.
     *
     * By default, this converter creates markers with the `group:name` name convention (to match the default `markerToData` conversion).
     *
     * The conversion configuration can take a function that will generate a marker name.
     * If such function is set as the `config.model` parameter, it is passed the `name` part from the view element or attribute and it is
     * expected to return a string with the marker name.
     *
     * Basic usage:
     *
     *		// Using the default conversion.
     *		// In this case, all markers from the `comment` group will be converted.
     *		// The conversion will look for `<comment-start>` and `<comment-end>` tags and
     *		// `data-comment-start-before`, `data-comment-start-after`,
     *		// `data-comment-end-before` and `data-comment-end-after` attributes.
     *		editor.conversion.for( 'upcast' ).dataToMarker( {
     *			view: 'comment'
     *		} );
     *
     * An example of a model that may be generated by this conversion:
     *
     *		// View:
     *		<p>Foo<comment-start name="commentId:uid"></comment-start>bar</p>
     *		<figure data-comment-end-after="commentId:uid" class="image"><img src="abc.jpg" /></figure>
     *
     *		// Model:
     *		<paragraph>Foo[bar</paragraph>
     *		<imageBlock src="abc.jpg"></imageBlock>]
     *
     * Where `[]` are boundaries of a marker that will receive the `comment:commentId:uid` name.
     *
     * Other examples of usage:
     *
     *		// Using a custom function which is the same as the default conversion:
     *		editor.conversion.for( 'upcast' ).dataToMarker( {
     *			view: 'comment',
     *			model: ( name, conversionApi ) => 'comment:' + name,
     *		} );
     *
     *		// Using the converter priority:
     *		editor.conversion.for( 'upcast' ).dataToMarker( {
     *			view: 'comment',
     *			model: ( name, conversionApi ) => 'comment:' + name,
     *			converterPriority: 'high'
     *		} );
     *
     * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
     * to the conversion process.
     *
     * @method #dataToMarker
     * @param {Object} config Conversion configuration.
     * @param {String} config.view The marker group name to convert.
     * @param {Function} [config.model] A function that takes the `name` part from the view element or attribute and
     * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the marker name.
     * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
     * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
     */
    dataToMarker(config) {
        return this.add(upcastDataToMarker(config));
    }
}
/**
 * Function factory, creates a converter that converts {@link module:engine/view/documentfragment~DocumentFragment view document fragment}
 * or all children of {@link module:engine/view/element~Element} into
 * {@link module:engine/model/documentfragment~DocumentFragment model document fragment}.
 * This is the "entry-point" converter for upcast (view to model conversion). This converter starts the conversion of all children
 * of passed view document fragment. Those children {@link module:engine/view/node~Node view nodes} are then handled by other converters.
 *
 * This also a "default", last resort converter for all view elements that has not been converted by other converters.
 * When a view element is being converted to the model but it does not have converter specified, that view element
 * will be converted to {@link module:engine/model/documentfragment~DocumentFragment model document fragment} and returned.
 *
 * @returns {Function} Universal converter for view {@link module:engine/view/documentfragment~DocumentFragment fragments} and
 * {@link module:engine/view/element~Element elements} that returns
 * {@link module:engine/model/documentfragment~DocumentFragment model fragment} with children of converted view item.
 */
function convertToModelFragment() {
    return (evt, data, conversionApi) => {
        // Second argument in `consumable.consume` is discarded for ViewDocumentFragment but is needed for ViewElement.
        if (!data.modelRange && conversionApi.consumable.consume(data.viewItem, { name: true })) {
            const { modelRange, modelCursor } = conversionApi.convertChildren(data.viewItem, data.modelCursor);
            data.modelRange = modelRange;
            data.modelCursor = modelCursor;
        }
    };
}
/**
 * Function factory, creates a converter that converts {@link module:engine/view/text~Text} to {@link module:engine/model/text~Text}.
 *
 * @returns {Function} {@link module:engine/view/text~Text View text} converter.
 */
function convertText() {
    return (evt, data, { schema, consumable, writer }) => {
        let position = data.modelCursor;
        // When node is already converted then do nothing.
        if (!consumable.test(data.viewItem)) {
            return;
        }
        if (!schema.checkChild(position, '$text')) {
            if (!isParagraphable(position, '$text', schema)) {
                return;
            }
            // Do not auto-paragraph whitespaces.
            if (data.viewItem.data.trim().length == 0) {
                return;
            }
            position = wrapInParagraph(position, writer);
        }
        consumable.consume(data.viewItem);
        const text = writer.createText(data.viewItem.data);
        writer.insert(text, position);
        data.modelRange = writer.createRange(position, position.getShiftedBy(text.offsetSize));
        data.modelCursor = data.modelRange.end;
    };
}
/**
 * Function factory, creates a callback function which converts a {@link module:engine/view/selection~Selection
 * view selection} taken from the {@link module:engine/view/document~Document#event:selectionChange} event
 * and sets in on the {@link module:engine/model/document~Document#selection model}.
 *
 * **Note**: because there is no view selection change dispatcher nor any other advanced view selection to model
 * conversion mechanism, the callback should be set directly on view document.
 *
 *		view.document.on( 'selectionChange', convertSelectionChange( modelDocument, mapper ) );
 *
 * @param {module:engine/model/model~Model} model Data model.
 * @param {module:engine/conversion/mapper~Mapper} mapper Conversion mapper.
 * @returns {Function} {@link module:engine/view/document~Document#event:selectionChange} callback function.
 */
function convertSelectionChange(model, mapper) {
    return (evt, data) => {
        const viewSelection = data.newSelection;
        const ranges = [];
        for (const viewRange of viewSelection.getRanges()) {
            ranges.push(mapper.toModelRange(viewRange));
        }
        const modelSelection = model.createSelection(ranges, { backward: viewSelection.isBackward });
        if (!modelSelection.isEqual(model.document.selection)) {
            model.change(writer => {
                writer.setSelection(modelSelection);
            });
        }
    };
}
// View element to model element conversion helper.
//
// See {@link ~UpcastHelpers#elementToElement `.elementToElement()` upcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {module:engine/view/matcher~MatcherPattern} [config.view] Pattern matching all view elements which should be converted. If not
// set, the converter will fire for every view element.
// @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element
// instance or a function that takes a view element and returns a model element. The model element will be inserted in the model.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
// @returns {Function} Conversion helper.
function upcastElementToElement(config) {
    config = lodash_es_cloneDeep(config);
    const converter = prepareToElementConverter(config);
    const elementName = getViewElementNameFromConfig(config.view);
    const eventName = elementName ? `element:${elementName}` : 'element';
    return (dispatcher) => {
        dispatcher.on(eventName, converter, { priority: config.converterPriority || 'normal' });
    };
}
// View element to model attribute conversion helper.
//
// See {@link ~UpcastHelpers#elementToAttribute `.elementToAttribute()` upcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.
// @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
// the model attribute. `value` property may be set as a function that takes a view element and returns the value.
// If `String` is given, the model attribute value will be set to `true`.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
// @returns {Function} Conversion helper.
function upcastElementToAttribute(config) {
    config = lodash_es_cloneDeep(config);
    normalizeModelAttributeConfig(config);
    const converter = prepareToAttributeConverter(config, false);
    const elementName = getViewElementNameFromConfig(config.view);
    const eventName = elementName ? `element:${elementName}` : 'element';
    return (dispatcher) => {
        dispatcher.on(eventName, converter, { priority: config.converterPriority || 'low' });
    };
}
// View attribute to model attribute conversion helper.
//
// See {@link ~UpcastHelpers#attributeToAttribute `.attributeToAttribute()` upcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {String|Object} config.view Specifies which view attribute will be converted. If a `String` is passed,
// attributes with given key will be converted. If an `Object` is passed, it must have a required `key` property,
// specifying view attribute key, and may have an optional `value` property, specifying view attribute value and optional `name`
// property specifying a view element name from/on which the attribute should be converted. `value` can be given as a `String`,
// a `RegExp` or a function callback, that takes view attribute value as the only parameter and returns `Boolean`.
// @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
// the model attribute. `value` property may be set as a function that takes a view element and returns the value.
// If `String` is given, the model attribute value will be same as view attribute value.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
// @returns {Function} Conversion helper.
function upcastAttributeToAttribute(config) {
    config = lodash_es_cloneDeep(config);
    let viewKey = null;
    if (typeof config.view == 'string' || config.view.key) {
        viewKey = normalizeViewAttributeKeyValueConfig(config);
    }
    normalizeModelAttributeConfig(config, viewKey);
    const converter = prepareToAttributeConverter(config, true);
    return (dispatcher) => {
        dispatcher.on('element', converter, { priority: config.converterPriority || 'low' });
    };
}
// View element to model marker conversion helper.
//
// See {@link ~UpcastHelpers#elementToMarker `.elementToMarker()` upcast helper} for examples.
//
// @param {Object} config Conversion configuration.
// @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.
// @param {String|Function} config.model Name of the model marker, or a function that takes a view element and returns
// a model marker name.
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
// @returns {Function} Conversion helper.
function upcastElementToMarker(config) {
    const model = normalizeElementToMarkerModelConfig(config.model);
    return upcastElementToElement({ ...config, model });
}
// View data to model marker conversion helper.
//
// See {@link ~UpcastHelpers#dataToMarker} to learn more.
//
// @param {Object} config
// @param {String} config.view
// @param {Function} [config.model]
// @param {module:utils/priorities~PriorityString} [config.converterPriority='normal']
// @returns {Function} Conversion helper.
function upcastDataToMarker(config) {
    config = lodash_es_cloneDeep(config);
    // Default conversion.
    if (!config.model) {
        config.model = name => {
            return name ? config.view + ':' + name : config.view;
        };
    }
    const normalizedConfig = {
        view: config.view,
        model: config.model
    };
    const converterStart = prepareToElementConverter(normalizeDataToMarkerConfig(normalizedConfig, 'start'));
    const converterEnd = prepareToElementConverter(normalizeDataToMarkerConfig(normalizedConfig, 'end'));
    return (dispatcher) => {
        dispatcher.on(`element:${config.view}-start`, converterStart, { priority: config.converterPriority || 'normal' });
        dispatcher.on(`element:${config.view}-end`, converterEnd, { priority: config.converterPriority || 'normal' });
        // Below is a hack that is needed to properly handle `converterPriority` for both elements and attributes.
        // Attribute conversion needs to be performed *after* element conversion.
        // This converter handles both element conversion and attribute conversion, which means that if a single
        // `config.converterPriority` is used, it will lead to problems. For example, if the `'high'` priority is used,
        // the attribute conversion will be performed before a lot of element upcast converters.
        // On the other hand, we want to support `config.converterPriority` and converter overwriting.
        //
        // To make it work, we need to do some extra processing for priority for attribute converter.
        // Priority `'low'` value should be the base value and then we will change it depending on `config.converterPriority` value.
        //
        // This hack probably would not be needed if attributes are upcasted separately.
        //
        const basePriority = src_priorities.get('low');
        const maxPriority = src_priorities.get('highest');
        const priorityFactor = src_priorities.get(config.converterPriority) / maxPriority; // Number in range [ -1, 1 ].
        dispatcher.on('element', upcastAttributeToMarker(normalizedConfig), { priority: basePriority + priorityFactor });
    };
}
// Function factory, returns a callback function which converts view attributes to a model marker.
//
// The converter looks for elements with `data-group-start-before`, `data-group-start-after`, `data-group-end-before`
// and `data-group-end-after` attributes and inserts `$marker` model elements before/after those elements.
// `group` part is specified in `config.view`.
//
// @param {Object} config
// @param {String} config.view
// @param {Function} [config.model]
// @returns {Function} Marker converter.
function upcastAttributeToMarker(config) {
    return (evt, data, conversionApi) => {
        const attrName = `data-${config.view}`;
        // Check if any attribute for the given view item can be consumed before changing the conversion data
        // and consuming view items with these attributes.
        if (!conversionApi.consumable.test(data.viewItem, { attributes: attrName + '-end-after' }) &&
            !conversionApi.consumable.test(data.viewItem, { attributes: attrName + '-start-after' }) &&
            !conversionApi.consumable.test(data.viewItem, { attributes: attrName + '-end-before' }) &&
            !conversionApi.consumable.test(data.viewItem, { attributes: attrName + '-start-before' })) {
            return;
        }
        // This converter wants to add a model element, marking a marker, before/after an element (or maybe even group of elements).
        // To do that, we can use `data.modelRange` which is set on an element (or a group of elements) that has been upcasted.
        // But, if the processed view element has not been upcasted yet (it does not have been converted), we need to
        // fire conversion for its children first, then we will have `data.modelRange` available.
        if (!data.modelRange) {
            Object.assign(data, conversionApi.convertChildren(data.viewItem, data.modelCursor));
        }
        if (conversionApi.consumable.consume(data.viewItem, { attributes: attrName + '-end-after' })) {
            addMarkerElements(data.modelRange.end, data.viewItem.getAttribute(attrName + '-end-after').split(','));
        }
        if (conversionApi.consumable.consume(data.viewItem, { attributes: attrName + '-start-after' })) {
            addMarkerElements(data.modelRange.end, data.viewItem.getAttribute(attrName + '-start-after').split(','));
        }
        if (conversionApi.consumable.consume(data.viewItem, { attributes: attrName + '-end-before' })) {
            addMarkerElements(data.modelRange.start, data.viewItem.getAttribute(attrName + '-end-before').split(','));
        }
        if (conversionApi.consumable.consume(data.viewItem, { attributes: attrName + '-start-before' })) {
            addMarkerElements(data.modelRange.start, data.viewItem.getAttribute(attrName + '-start-before').split(','));
        }
        function addMarkerElements(position, markerViewNames) {
            for (const markerViewName of markerViewNames) {
                const markerName = config.model(markerViewName, conversionApi);
                const element = conversionApi.writer.createElement('$marker', { 'data-name': markerName });
                conversionApi.writer.insert(element, position);
                if (data.modelCursor.isEqual(position)) {
                    data.modelCursor = data.modelCursor.getShiftedBy(1);
                }
                else {
                    data.modelCursor = data.modelCursor._getTransformedByInsertion(position, 1);
                }
                data.modelRange = data.modelRange._getTransformedByInsertion(position, 1)[0];
            }
        }
    };
}
// Helper function for from-view-element conversion. Checks if `config.view` directly specifies converted view element's name
// and if so, returns it.
//
// @param {Object} config Conversion view config.
// @returns {String|null} View element name or `null` if name is not directly set.
function getViewElementNameFromConfig(viewConfig) {
    if (typeof viewConfig == 'string') {
        return viewConfig;
    }
    if (typeof viewConfig == 'object' && typeof viewConfig.name == 'string') {
        return viewConfig.name;
    }
    return null;
}
// Helper for to-model-element conversion. Takes a config object and returns a proper converter function.
//
// @param {Object} config Conversion configuration.
// @returns {Function} View to model converter.
function prepareToElementConverter(config) {
    const matcher = new Matcher(config.view);
    return (evt, data, conversionApi) => {
        const matcherResult = matcher.match(data.viewItem);
        if (!matcherResult) {
            return;
        }
        const match = matcherResult.match;
        // Force consuming element's name.
        match.name = true;
        if (!conversionApi.consumable.test(data.viewItem, match)) {
            return;
        }
        const modelElement = getModelElement(config.model, data.viewItem, conversionApi);
        if (!modelElement) {
            return;
        }
        if (!conversionApi.safeInsert(modelElement, data.modelCursor)) {
            return;
        }
        conversionApi.consumable.consume(data.viewItem, match);
        conversionApi.convertChildren(data.viewItem, modelElement);
        conversionApi.updateConversionResult(modelElement, data);
    };
}
// Helper function for upcasting-to-element converter. Takes the model configuration, the converted view element
// and a writer instance and returns a model element instance to be inserted in the model.
//
// @param {String|Function|module:engine/model/element~Element} model Model conversion configuration.
// @param {module:engine/view/node~Node} input The converted view node.
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi The upcast conversion API.
function getModelElement(model, input, conversionApi) {
    if (model instanceof Function) {
        return model(input, conversionApi);
    }
    else {
        return conversionApi.writer.createElement(model);
    }
}
// Helper function view-attribute-to-model-attribute helper. Normalizes `config.view` which was set as `String` or
// as an `Object` with `key`, `value` and `name` properties. Normalized `config.view` has is compatible with
// {@link module:engine/view/matcher~MatcherPattern}.
//
// @param {Object} config Conversion config.
// @returns {String} Key of the converted view attribute.
function normalizeViewAttributeKeyValueConfig(config) {
    if (typeof config.view == 'string') {
        config.view = { key: config.view };
    }
    const key = config.view.key;
    let normalized;
    if (key == 'class' || key == 'style') {
        const keyName = key == 'class' ? 'classes' : 'styles';
        normalized = {
            [keyName]: config.view.value
        };
    }
    else {
        const value = typeof config.view.value == 'undefined' ? /[\s\S]*/ : config.view.value;
        normalized = {
            attributes: {
                [key]: value
            }
        };
    }
    if (config.view.name) {
        normalized.name = config.view.name;
    }
    config.view = normalized;
    return key;
}
// Helper function that normalizes `config.model` in from-model-attribute conversion. `config.model` can be set
// as a `String`, an `Object` with only `key` property or an `Object` with `key` and `value` properties. Normalized
// `config.model` is an `Object` with `key` and `value` properties.
//
// @param {Object} config Conversion config.
// @param {String} viewAttributeKeyToCopy Key of the converted view attribute. If it is set, model attribute value
// will be equal to view attribute value.
function normalizeModelAttributeConfig(config, viewAttributeKeyToCopy = null) {
    const defaultModelValue = viewAttributeKeyToCopy === null ? true :
        (viewElement) => viewElement.getAttribute(viewAttributeKeyToCopy);
    const key = typeof config.model != 'object' ? config.model : config.model.key;
    const value = typeof config.model != 'object' || typeof config.model.value == 'undefined' ? defaultModelValue : config.model.value;
    config.model = { key, value };
}
// Helper for to-model-attribute conversion. Takes the model attribute name and conversion configuration and returns
// a proper converter function.
//
// @param {String} modelAttributeKey The key of the model attribute to set on a model node.
// @param {Object|Array.<Object>} config Conversion configuration. It is possible to provide multiple configurations in an array.
// @param {Boolean} shallow If set to `true` the attribute will be set only on top-level nodes. Otherwise, it will be set
// on all elements in the range.
function prepareToAttributeConverter(config, shallow) {
    const matcher = new Matcher(config.view);
    return (evt, data, conversionApi) => {
        // Converting an attribute of an element that has not been converted to anything does not make sense
        // because there will be nowhere to set that attribute on. At this stage, the element should've already
        // been converted (https://github.com/ckeditor/ckeditor5/issues/11000).
        if (!data.modelRange && shallow) {
            return;
        }
        const match = matcher.match(data.viewItem);
        // If there is no match, this callback should not do anything.
        if (!match) {
            return;
        }
        if (onlyViewNameIsDefined(config.view, data.viewItem)) {
            match.match.name = true;
        }
        else {
            // Do not test `name` consumable because it could get consumed already while upcasting some other attribute
            // on the same element (for example <span class="big" style="color: red">foo</span>).
            delete match.match.name;
        }
        // Try to consume appropriate values from consumable values list.
        if (!conversionApi.consumable.test(data.viewItem, match.match)) {
            return;
        }
        const modelKey = config.model.key;
        const modelValue = typeof config.model.value == 'function' ?
            config.model.value(data.viewItem, conversionApi) : config.model.value;
        // Do not convert if attribute building function returned falsy value.
        if (modelValue === null) {
            return;
        }
        // Since we are converting to attribute we need a range on which we will set the attribute.
        // If the range is not created yet, let's create it by converting children of the current node first.
        if (!data.modelRange) {
            // Convert children and set conversion result as a current data.
            Object.assign(data, conversionApi.convertChildren(data.viewItem, data.modelCursor));
        }
        // Set attribute on current `output`. `Schema` is checked inside this helper function.
        const attributeWasSet = setAttributeOn(data.modelRange, { key: modelKey, value: modelValue }, shallow, conversionApi);
        // It may happen that a converter will try to set an attribute that is not allowed in the given context.
        // In such a situation we cannot consume the attribute. See: https://github.com/ckeditor/ckeditor5/pull/9249#issuecomment-815658459.
        if (attributeWasSet) {
            // Verify if the element itself wasn't consumed yet. It could be consumed already while upcasting some other attribute
            // on the same element (for example <span class="big" style="color: red">foo</span>).
            // We need to consume it so other features (especially GHS) won't try to convert it.
            // Note that it's not tested by the other element-to-attribute converters whether an element was consumed before
            // (in case of converters that the element itself is just a context and not the primary information to convert).
            if (conversionApi.consumable.test(data.viewItem, { name: true })) {
                match.match.name = true;
            }
            conversionApi.consumable.consume(data.viewItem, match.match);
        }
    };
}
// Helper function that checks if element name should be consumed in attribute converters.
//
// @param {Object} config Conversion view config.
// @returns {Boolean}
function onlyViewNameIsDefined(viewConfig, viewItem) {
    // https://github.com/ckeditor/ckeditor5-engine/issues/1786
    const configToTest = typeof viewConfig == 'function' ? viewConfig(viewItem) : viewConfig;
    if (typeof configToTest == 'object' && !getViewElementNameFromConfig(configToTest)) {
        return false;
    }
    return !configToTest.classes && !configToTest.attributes && !configToTest.styles;
}
// Helper function for to-model-attribute converter. Sets model attribute on given range. Checks {@link module:engine/model/schema~Schema}
// to ensure proper model structure.
//
// If any node on the given range has already defined an attribute with the same name, its value will not be updated.
//
// @param {module:engine/model/range~Range} modelRange Model range on which attribute should be set.
// @param {Object} modelAttribute Model attribute to set.
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion API.
// @param {Boolean} shallow If set to `true` the attribute will be set only on top-level nodes. Otherwise, it will be set
// on all elements in the range.
// @returns {Boolean} `true` if attribute was set on at least one node from given `modelRange`.
function setAttributeOn(modelRange, modelAttribute, shallow, conversionApi) {
    let result = false;
    // Set attribute on each item in range according to Schema.
    for (const node of Array.from(modelRange.getItems({ shallow }))) {
        // Skip if not allowed.
        if (!conversionApi.schema.checkAttribute(node, modelAttribute.key)) {
            continue;
        }
        // Mark the node as consumed even if the attribute will not be updated because it's in a valid context (schema)
        // and would be converted if the attribute wouldn't be present. See #8921.
        result = true;
        // Do not override the attribute if it's already present.
        if (node.hasAttribute(modelAttribute.key)) {
            continue;
        }
        conversionApi.writer.setAttribute(modelAttribute.key, modelAttribute.value, node);
    }
    return result;
}
// Helper function for upcasting-to-marker conversion. Takes the config in a format requested by `upcastElementToMarker()`
// function and converts it to a format that is supported by `upcastElementToElement()` function.
//
// @param {Object} config Conversion configuration.
function normalizeElementToMarkerModelConfig(model) {
    return (viewElement, conversionApi) => {
        const markerName = typeof model == 'string' ? model : model(viewElement, conversionApi);
        return conversionApi.writer.createElement('$marker', { 'data-name': markerName });
    };
}
// Helper function for upcasting-to-marker conversion. Takes the config in a format requested by `upcastDataToMarker()`
// function and converts it to a format that is supported by `upcastElementToElement()` function.
//
// @param {Object} config Conversion configuration.
function normalizeDataToMarkerConfig(config, type) {
    const elementCreatorFunction = (viewElement, conversionApi) => {
        const viewName = viewElement.getAttribute('name');
        const markerName = config.model(viewName, conversionApi);
        return conversionApi.writer.createElement('$marker', { 'data-name': markerName });
    };
    return {
        // Upcast <markerGroup-start> and <markerGroup-end> elements.
        view: `${config.view}-${type}`,
        model: elementCreatorFunction
    };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/controller/editingcontroller.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/controller/editingcontroller
 */








// @if CK_DEBUG_ENGINE // const { dumpTrees, initDocumentDumping } = require( '../dev-utils/utils' );
/**
 * A controller for the editing pipeline. The editing pipeline controls the {@link ~EditingController#model model} rendering,
 * including selection handling. It also creates the {@link ~EditingController#view view} which builds a
 * browser-independent virtualization over the DOM elements. The editing controller also attaches default converters.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class EditingController extends Observable {
    /**
     * Creates an editing controller instance.
     *
     * @param {module:engine/model/model~Model} model Editing model.
     * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
     */
    constructor(model, stylesProcessor) {
        super();
        /**
         * Editor model.
         *
         * @readonly
         * @member {module:engine/model/model~Model}
         */
        this.model = model;
        /**
         * Editing view controller.
         *
         * @readonly
         * @member {module:engine/view/view~View}
         */
        this.view = new view_View(stylesProcessor);
        /**
         * A mapper that describes the model-view binding.
         *
         * @readonly
         * @member {module:engine/conversion/mapper~Mapper}
         */
        this.mapper = new Mapper();
        /**
         * Downcast dispatcher that converts changes from the model to the {@link #view editing view}.
         *
         * @readonly
         * @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher} #downcastDispatcher
         */
        this.downcastDispatcher = new DowncastDispatcher({
            mapper: this.mapper,
            schema: model.schema
        });
        const doc = this.model.document;
        const selection = doc.selection;
        const markers = this.model.markers;
        // When plugins listen on model changes (on selection change, post fixers, etc.) and change the view as a result of
        // the model's change, they might trigger view rendering before the conversion is completed (e.g. before the selection
        // is converted). We disable rendering for the length of the outermost model change() block to prevent that.
        //
        // See https://github.com/ckeditor/ckeditor5-engine/issues/1528
        this.listenTo(this.model, '_beforeChanges', () => {
            this.view._disableRendering(true);
        }, { priority: 'highest' });
        this.listenTo(this.model, '_afterChanges', () => {
            this.view._disableRendering(false);
        }, { priority: 'lowest' });
        // Whenever model document is changed, convert those changes to the view (using model.Document#differ).
        // Do it on 'low' priority, so changes are converted after other listeners did their job.
        // Also convert model selection.
        this.listenTo(doc, 'change', () => {
            this.view.change(writer => {
                this.downcastDispatcher.convertChanges(doc.differ, markers, writer);
                this.downcastDispatcher.convertSelection(selection, markers, writer);
            });
        }, { priority: 'low' });
        // Convert selection from the view to the model when it changes in the view.
        this.listenTo(this.view.document, 'selectionChange', convertSelectionChange(this.model, this.mapper));
        // Attach default model converters.
        this.downcastDispatcher.on('insert:$text', insertText(), { priority: 'lowest' });
        this.downcastDispatcher.on('insert', insertAttributesAndChildren(), { priority: 'lowest' });
        this.downcastDispatcher.on('remove', downcasthelpers_remove(), { priority: 'low' });
        // Attach default model selection converters.
        this.downcastDispatcher.on('selection', clearAttributes(), { priority: 'high' });
        this.downcastDispatcher.on('selection', convertRangeSelection(), { priority: 'low' });
        this.downcastDispatcher.on('selection', convertCollapsedSelection(), { priority: 'low' });
        // Binds {@link module:engine/view/document~Document#roots view roots collection} to
        // {@link module:engine/model/document~Document#roots model roots collection} so creating
        // model root automatically creates corresponding view root.
        this.view.document.roots.bindTo(this.model.document.roots).using(root => {
            // $graveyard is a special root that has no reflection in the view.
            if (root.rootName == '$graveyard') {
                return null;
            }
            const viewRoot = new RootEditableElement(this.view.document, root.name);
            viewRoot.rootName = root.rootName;
            this.mapper.bindElements(root, viewRoot);
            return viewRoot;
        });
        // @if CK_DEBUG_ENGINE // initDocumentDumping( this.model.document );
        // @if CK_DEBUG_ENGINE // initDocumentDumping( this.view.document );
        // @if CK_DEBUG_ENGINE // dumpTrees( this.model.document, this.model.document.version );
        // @if CK_DEBUG_ENGINE // dumpTrees( this.view.document, this.model.document.version );
        // @if CK_DEBUG_ENGINE // this.model.document.on( 'change', () => {
        // @if CK_DEBUG_ENGINE //	dumpTrees( this.view.document, this.model.document.version );
        // @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
    }
    /**
     * Removes all event listeners attached to the `EditingController`. Destroys all objects created
     * by `EditingController` that need to be destroyed.
     */
    destroy() {
        this.view.destroy();
        this.stopListening();
    }
    /**
     * Calling this method will refresh the marker by triggering the downcast conversion for it.
     *
     * Reconverting the marker is useful when you want to change its {@link module:engine/view/element~Element view element}
     * without changing any marker data. For instance:
     *
     *		let isCommentActive = false;
     *
     *		model.conversion.markerToHighlight( {
     *			model: 'comment',
     *			view: data => {
     *				const classes = [ 'comment-marker' ];
     *
     *				if ( isCommentActive ) {
     *					classes.push( 'comment-marker--active' );
     *				}
     *
     *				return { classes };
     *			}
     *		} );
     *
     *		// ...
     *
     *		// Change the property that indicates if marker is displayed as active or not.
     *		isCommentActive = true;
     *
     *		// Reconverting will downcast and synchronize the marker with the new isCommentActive state value.
     *		editor.editing.reconvertMarker( 'comment' );
     *
     * **Note**: If you want to reconvert a model item, use {@link #reconvertItem} instead.
     *
     * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of a marker to update, or a marker instance.
     */
    reconvertMarker(markerOrName) {
        const markerName = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
        const currentMarker = this.model.markers.get(markerName);
        if (!currentMarker) {
            /**
             * The marker with the provided name does not exist and cannot be reconverted.
             *
             * @error editingcontroller-reconvertmarker-marker-not-exist
             * @param {String} markerName The name of the reconverted marker.
             */
            throw new CKEditorError('editingcontroller-reconvertmarker-marker-not-exist', this, { markerName });
        }
        this.model.change(() => {
            this.model.markers._refresh(currentMarker);
        });
    }
    /**
     * Calling this method will downcast a model item on demand (by requesting a refresh in the {@link module:engine/model/differ~Differ}).
     *
     * You can use it if you want the view representation of a specific item updated as a response to external modifications. For instance,
     * when the view structure depends not only on the associated model data but also on some external state.
     *
     * **Note**: If you want to reconvert a model marker, use {@link #reconvertMarker} instead.
     *
     * @param {module:engine/model/item~Item} item Item to refresh.
     */
    reconvertItem(item) {
        this.model.change(() => {
            this.model.document.differ._refreshItem(item);
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/commandcollection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/commandcollection
 */

/**
 * Collection of commands. Its instance is available in {@link module:core/editor/editor~Editor#commands `editor.commands`}.
 */
class CommandCollection {
    /**
     * Creates collection instance.
     */
    constructor() {
        /**
         * Command map.
         *
         * @private
         * @member {Map}
         */
        this._commands = new Map();
    }
    /**
     * Registers a new command.
     *
     * @param {String} commandName The name of the command.
     * @param {module:core/command~Command} command
     */
    add(commandName, command) {
        this._commands.set(commandName, command);
    }
    /**
     * Retrieves a command from the collection.
     *
     * @param {String} commandName The name of the command.
     * @returns {module:core/command~Command}
     */
    get(commandName) {
        return this._commands.get(commandName);
    }
    /**
     * Executes a command.
     *
     * @param {String} commandName The name of the command.
     * @param {*} [...commandParams] Command parameters.
     * @returns {*} The value returned by the {@link module:core/command~Command#execute `command.execute()`}.
     */
    execute(commandName, ...args) {
        const command = this.get(commandName);
        if (!command) {
            /**
             * Command does not exist.
             *
             * @error commandcollection-command-not-found
             * @param {String} commandName Name of the command.
             */
            throw new CKEditorError('commandcollection-command-not-found', this, { commandName });
        }
        return command.execute(...args);
    }
    /**
     * Returns iterator of command names.
     *
     * @returns {Iterable.<String>}
     */
    *names() {
        yield* this._commands.keys();
    }
    /**
     * Returns iterator of command instances.
     *
     * @returns {Iterable.<module:core/command~Command>}
     */
    *commands() {
        yield* this._commands.values();
    }
    /**
     * Iterable interface.
     *
     * Returns `[ commandName, commandInstance ]` pairs.
     *
     * @returns {Iterator.<Array>}
     */
    [Symbol.iterator]() {
        return this._commands[Symbol.iterator]();
    }
    /**
     * Destroys all collection commands.
     */
    destroy() {
        for (const command of this.commands()) {
            command.destroy();
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/viewconsumable.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/conversion/viewconsumable
 */


/**
 * Class used for handling consumption of view {@link module:engine/view/element~Element elements},
 * {@link module:engine/view/text~Text text nodes} and {@link module:engine/view/documentfragment~DocumentFragment document fragments}.
 * Element's name and its parts (attributes, classes and styles) can be consumed separately. Consuming an element's name
 * does not consume its attributes, classes and styles.
 * To add items for consumption use {@link module:engine/conversion/viewconsumable~ViewConsumable#add add method}.
 * To test items use {@link module:engine/conversion/viewconsumable~ViewConsumable#test test method}.
 * To consume items use {@link module:engine/conversion/viewconsumable~ViewConsumable#consume consume method}.
 * To revert already consumed items use {@link module:engine/conversion/viewconsumable~ViewConsumable#revert revert method}.
 *
 *		viewConsumable.add( element, { name: true } ); // Adds element's name as ready to be consumed.
 *		viewConsumable.add( textNode ); // Adds text node for consumption.
 *		viewConsumable.add( docFragment ); // Adds document fragment for consumption.
 *		viewConsumable.test( element, { name: true }  ); // Tests if element's name can be consumed.
 *		viewConsumable.test( textNode ); // Tests if text node can be consumed.
 *		viewConsumable.test( docFragment ); // Tests if document fragment can be consumed.
 *		viewConsumable.consume( element, { name: true }  ); // Consume element's name.
 *		viewConsumable.consume( textNode ); // Consume text node.
 *		viewConsumable.consume( docFragment ); // Consume document fragment.
 *		viewConsumable.revert( element, { name: true }  ); // Revert already consumed element's name.
 *		viewConsumable.revert( textNode ); // Revert already consumed text node.
 *		viewConsumable.revert( docFragment ); // Revert already consumed document fragment.
 */
class ViewConsumable {
    /**
     * Creates new ViewConsumable.
     */
    constructor() {
        /**
         * Map of consumable elements. If {@link module:engine/view/element~Element element} is used as a key,
         * {@link module:engine/conversion/viewconsumable~ViewElementConsumables ViewElementConsumables} instance is stored as value.
         * For {@link module:engine/view/text~Text text nodes} and
         * {@link module:engine/view/documentfragment~DocumentFragment document fragments} boolean value is stored as value.
         *
         * @protected
         * @member {Map.<module:engine/conversion/viewconsumable~ViewElementConsumables|Boolean>}
        */
        this._consumables = new Map();
    }
    /**
     * Adds {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or
     * {@link module:engine/view/documentfragment~DocumentFragment document fragment} as ready to be consumed.
     *
     *		viewConsumable.add( p, { name: true } ); // Adds element's name to consume.
     *		viewConsumable.add( p, { attributes: 'name' } ); // Adds element's attribute.
     *		viewConsumable.add( p, { classes: 'foobar' } ); // Adds element's class.
     *		viewConsumable.add( p, { styles: 'color' } ); // Adds element's style
     *		viewConsumable.add( p, { attributes: 'name', styles: 'color' } ); // Adds attribute and style.
     *		viewConsumable.add( p, { classes: [ 'baz', 'bar' ] } ); // Multiple consumables can be provided.
     *		viewConsumable.add( textNode ); // Adds text node to consume.
     *		viewConsumable.add( docFragment ); // Adds document fragment to consume.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `viewconsumable-invalid-attribute` when `class` or `style`
     * attribute is provided - it should be handled separately by providing actual style/class.
     *
     *		viewConsumable.add( p, { attributes: 'style' } ); // This call will throw an exception.
     *		viewConsumable.add( p, { styles: 'color' } ); // This is properly handled style.
     *
     * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element
     * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.
     * @param {Boolean} consumables.name If set to true element's name will be included.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names.
     */
    add(element, consumables) {
        let elementConsumables;
        // For text nodes and document fragments just mark them as consumable.
        if (element.is('$text') || element.is('documentFragment')) {
            this._consumables.set(element, true);
            return;
        }
        // For elements create new ViewElementConsumables or update already existing one.
        if (!this._consumables.has(element)) {
            elementConsumables = new ViewElementConsumables(element);
            this._consumables.set(element, elementConsumables);
        }
        else {
            elementConsumables = this._consumables.get(element);
        }
        elementConsumables.add(consumables);
    }
    /**
     * Tests if {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or
     * {@link module:engine/view/documentfragment~DocumentFragment document fragment} can be consumed.
     * It returns `true` when all items included in method's call can be consumed. Returns `false` when
     * first already consumed item is found and `null` when first non-consumable item is found.
     *
     *		viewConsumable.test( p, { name: true } ); // Tests element's name.
     *		viewConsumable.test( p, { attributes: 'name' } ); // Tests attribute.
     *		viewConsumable.test( p, { classes: 'foobar' } ); // Tests class.
     *		viewConsumable.test( p, { styles: 'color' } ); // Tests style.
     *		viewConsumable.test( p, { attributes: 'name', styles: 'color' } ); // Tests attribute and style.
     *		viewConsumable.test( p, { classes: [ 'baz', 'bar' ] } ); // Multiple consumables can be tested.
     *		viewConsumable.test( textNode ); // Tests text node.
     *		viewConsumable.test( docFragment ); // Tests document fragment.
     *
     * Testing classes and styles as attribute will test if all added classes/styles can be consumed.
     *
     *		viewConsumable.test( p, { attributes: 'class' } ); // Tests if all added classes can be consumed.
     *		viewConsumable.test( p, { attributes: 'style' } ); // Tests if all added styles can be consumed.
     *
     * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element
     * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.
     * @param {Boolean} consumables.name If set to true element's name will be included.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names.
     * @returns {Boolean|null} Returns `true` when all items included in method's call can be consumed. Returns `false`
     * when first already consumed item is found and `null` when first non-consumable item is found.
     */
    test(element, consumables) {
        const elementConsumables = this._consumables.get(element);
        if (elementConsumables === undefined) {
            return null;
        }
        // For text nodes and document fragments return stored boolean value.
        if (element.is('$text') || element.is('documentFragment')) {
            return elementConsumables;
        }
        // For elements test consumables object.
        return elementConsumables.test(consumables);
    }
    /**
     * Consumes {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or
     * {@link module:engine/view/documentfragment~DocumentFragment document fragment}.
     * It returns `true` when all items included in method's call can be consumed, otherwise returns `false`.
     *
     *		viewConsumable.consume( p, { name: true } ); // Consumes element's name.
     *		viewConsumable.consume( p, { attributes: 'name' } ); // Consumes element's attribute.
     *		viewConsumable.consume( p, { classes: 'foobar' } ); // Consumes element's class.
     *		viewConsumable.consume( p, { styles: 'color' } ); // Consumes element's style.
     *		viewConsumable.consume( p, { attributes: 'name', styles: 'color' } ); // Consumes attribute and style.
     *		viewConsumable.consume( p, { classes: [ 'baz', 'bar' ] } ); // Multiple consumables can be consumed.
     *		viewConsumable.consume( textNode ); // Consumes text node.
     *		viewConsumable.consume( docFragment ); // Consumes document fragment.
     *
     * Consuming classes and styles as attribute will test if all added classes/styles can be consumed.
     *
     *		viewConsumable.consume( p, { attributes: 'class' } ); // Consume only if all added classes can be consumed.
     *		viewConsumable.consume( p, { attributes: 'style' } ); // Consume only if all added styles can be consumed.
     *
     * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element
     * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.
     * @param {Boolean} consumables.name If set to true element's name will be included.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names.
     * @returns {Boolean} Returns `true` when all items included in method's call can be consumed,
     * otherwise returns `false`.
     */
    consume(element, consumables) {
        if (this.test(element, consumables)) {
            if (element.is('$text') || element.is('documentFragment')) {
                // For text nodes and document fragments set value to false.
                this._consumables.set(element, false);
            }
            else {
                // For elements - consume consumables object.
                this._consumables.get(element).consume(consumables);
            }
            return true;
        }
        return false;
    }
    /**
     * Reverts {@link module:engine/view/element~Element view element}, {@link module:engine/view/text~Text text node} or
     * {@link module:engine/view/documentfragment~DocumentFragment document fragment} so they can be consumed once again.
     * Method does not revert items that were never previously added for consumption, even if they are included in
     * method's call.
     *
     *		viewConsumable.revert( p, { name: true } ); // Reverts element's name.
     *		viewConsumable.revert( p, { attributes: 'name' } ); // Reverts element's attribute.
     *		viewConsumable.revert( p, { classes: 'foobar' } ); // Reverts element's class.
     *		viewConsumable.revert( p, { styles: 'color' } ); // Reverts element's style.
     *		viewConsumable.revert( p, { attributes: 'name', styles: 'color' } ); // Reverts attribute and style.
     *		viewConsumable.revert( p, { classes: [ 'baz', 'bar' ] } ); // Multiple names can be reverted.
     *		viewConsumable.revert( textNode ); // Reverts text node.
     *		viewConsumable.revert( docFragment ); // Reverts document fragment.
     *
     * Reverting classes and styles as attribute will revert all classes/styles that were previously added for
     * consumption.
     *
     *		viewConsumable.revert( p, { attributes: 'class' } ); // Reverts all classes added for consumption.
     *		viewConsumable.revert( p, { attributes: 'style' } ); // Reverts all styles added for consumption.
     *
     * @param {module:engine/view/element~Element|module:engine/view/text~Text|module:engine/view/documentfragment~DocumentFragment} element
     * @param {Object} [consumables] Used only if first parameter is {@link module:engine/view/element~Element view element} instance.
     * @param {Boolean} consumables.name If set to true element's name will be included.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names.
     */
    revert(element, consumables) {
        const elementConsumables = this._consumables.get(element);
        if (elementConsumables !== undefined) {
            if (element.is('$text') || element.is('documentFragment')) {
                // For text nodes and document fragments - set consumable to true.
                this._consumables.set(element, true);
            }
            else {
                // For elements - revert items from consumables object.
                elementConsumables.revert(consumables);
            }
        }
    }
    /**
     * Creates consumable object from {@link module:engine/view/element~Element view element}. Consumable object will include
     * element's name and all its attributes, classes and styles.
     *
     * @static
     * @param {module:engine/view/element~Element} element
     * @returns {Object} consumables
     */
    static consumablesFromElement(element) {
        const consumables = {
            element,
            name: true,
            attributes: [],
            classes: [],
            styles: []
        };
        const attributes = element.getAttributeKeys();
        for (const attribute of attributes) {
            // Skip classes and styles - will be added separately.
            if (attribute == 'style' || attribute == 'class') {
                continue;
            }
            consumables.attributes.push(attribute);
        }
        const classes = element.getClassNames();
        for (const className of classes) {
            consumables.classes.push(className);
        }
        const styles = element.getStyleNames();
        for (const style of styles) {
            consumables.styles.push(style);
        }
        return consumables;
    }
    /**
     * Creates {@link module:engine/conversion/viewconsumable~ViewConsumable ViewConsumable} instance from
     * {@link module:engine/view/node~Node node} or {@link module:engine/view/documentfragment~DocumentFragment document fragment}.
     * Instance will contain all elements, child nodes, attributes, styles and classes added for consumption.
     *
     * @static
     * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} from View node or document fragment
     * from which `ViewConsumable` will be created.
     * @param {module:engine/conversion/viewconsumable~ViewConsumable} [instance] If provided, given `ViewConsumable` instance will be used
     * to add all consumables. It will be returned instead of a new instance.
     */
    static createFrom(from, instance) {
        if (!instance) {
            instance = new ViewConsumable();
        }
        if (from.is('$text')) {
            instance.add(from);
            return instance;
        }
        // Add `from` itself, if it is an element.
        if (from.is('element')) {
            instance.add(from, ViewConsumable.consumablesFromElement(from));
        }
        if (from.is('documentFragment')) {
            instance.add(from);
        }
        for (const child of from.getChildren()) {
            instance = ViewConsumable.createFrom(child, instance);
        }
        return instance;
    }
}
const CONSUMABLE_TYPES = ['attributes', 'classes', 'styles'];
/**
 * This is a private helper-class for {@link module:engine/conversion/viewconsumable~ViewConsumable}.
 * It represents and manipulates consumable parts of a single {@link module:engine/view/element~Element}.
 *
 * @private
 */
class ViewElementConsumables {
    /**
     * Creates ViewElementConsumables instance.
     *
     * @param {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment} from View node or document fragment
     * from which `ViewElementConsumables` is being created.
     */
    constructor(from) {
        /**
         * @readonly
         * @member {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment}
         */
        this.element = from;
        /**
         * Flag indicating if name of the element can be consumed.
         *
         * @private
         * @member {Boolean}
         */
        this._canConsumeName = null;
        /**
         * Contains maps of element's consumables: attributes, classes and styles.
         *
         * @private
         * @member {Object}
         */
        this._consumables = {
            attributes: new Map(),
            styles: new Map(),
            classes: new Map()
        };
    }
    /**
     * Adds consumable parts of the {@link module:engine/view/element~Element view element}.
     * Element's name itself can be marked to be consumed (when element's name is consumed its attributes, classes and
     * styles still could be consumed):
     *
     *		consumables.add( { name: true } );
     *
     * Attributes classes and styles:
     *
     *		consumables.add( { attributes: 'title', classes: 'foo', styles: 'color' } );
     *		consumables.add( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `viewconsumable-invalid-attribute` when `class` or `style`
     * attribute is provided - it should be handled separately by providing `style` and `class` in consumables object.
     *
     * @param {Object} consumables Object describing which parts of the element can be consumed.
     * @param {Boolean} consumables.name If set to `true` element's name will be added as consumable.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names to add as consumable.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names to add as consumable.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names to add as consumable.
     */
    add(consumables) {
        if (consumables.name) {
            this._canConsumeName = true;
        }
        for (const type of CONSUMABLE_TYPES) {
            if (type in consumables) {
                this._add(type, consumables[type]);
            }
        }
    }
    /**
     * Tests if parts of the {@link module:engine/view/node~Node view node} can be consumed.
     *
     * Element's name can be tested:
     *
     *		consumables.test( { name: true } );
     *
     * Attributes classes and styles:
     *
     *		consumables.test( { attributes: 'title', classes: 'foo', styles: 'color' } );
     *		consumables.test( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );
     *
     * @param {Object} consumables Object describing which parts of the element should be tested.
     * @param {Boolean} consumables.name If set to `true` element's name will be tested.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names to test.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names to test.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names to test.
     * @returns {Boolean|null} `true` when all tested items can be consumed, `null` when even one of the items
     * was never marked for consumption and `false` when even one of the items was already consumed.
     */
    test(consumables) {
        // Check if name can be consumed.
        if (consumables.name && !this._canConsumeName) {
            return this._canConsumeName;
        }
        for (const type of CONSUMABLE_TYPES) {
            if (type in consumables) {
                const value = this._test(type, consumables[type]);
                if (value !== true) {
                    return value;
                }
            }
        }
        // Return true only if all can be consumed.
        return true;
    }
    /**
     * Consumes parts of {@link module:engine/view/element~Element view element}. This function does not check if consumable item
     * is already consumed - it consumes all consumable items provided.
     * Element's name can be consumed:
     *
     *		consumables.consume( { name: true } );
     *
     * Attributes classes and styles:
     *
     *		consumables.consume( { attributes: 'title', classes: 'foo', styles: 'color' } );
     *		consumables.consume( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );
     *
     * @param {Object} consumables Object describing which parts of the element should be consumed.
     * @param {Boolean} consumables.name If set to `true` element's name will be consumed.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names to consume.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names to consume.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names to consume.
     */
    consume(consumables) {
        if (consumables.name) {
            this._canConsumeName = false;
        }
        for (const type of CONSUMABLE_TYPES) {
            if (type in consumables) {
                this._consume(type, consumables[type]);
            }
        }
    }
    /**
     * Revert already consumed parts of {@link module:engine/view/element~Element view Element}, so they can be consumed once again.
     * Element's name can be reverted:
     *
     *		consumables.revert( { name: true } );
     *
     * Attributes classes and styles:
     *
     *		consumables.revert( { attributes: 'title', classes: 'foo', styles: 'color' } );
     *		consumables.revert( { attributes: [ 'title', 'name' ], classes: [ 'foo', 'bar' ] );
     *
     * @param {Object} consumables Object describing which parts of the element should be reverted.
     * @param {Boolean} consumables.name If set to `true` element's name will be reverted.
     * @param {String|Array.<String>} consumables.attributes Attribute name or array of attribute names to revert.
     * @param {String|Array.<String>} consumables.classes Class name or array of class names to revert.
     * @param {String|Array.<String>} consumables.styles Style name or array of style names to revert.
     */
    revert(consumables) {
        if (consumables.name) {
            this._canConsumeName = true;
        }
        for (const type of CONSUMABLE_TYPES) {
            if (type in consumables) {
                this._revert(type, consumables[type]);
            }
        }
    }
    /**
     * Helper method that adds consumables of a given type: attribute, class or style.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `viewconsumable-invalid-attribute` when `class` or `style`
     * type is provided - it should be handled separately by providing actual style/class type.
     *
     * @private
     * @param {String} type Type of the consumable item: `attributes`, `classes` or `styles`.
     * @param {String|Array.<String>} item Consumable item or array of items.
     */
    _add(type, item) {
        const items = lodash_es_isArray(item) ? item : [item];
        const consumables = this._consumables[type];
        for (const name of items) {
            if (type === 'attributes' && (name === 'class' || name === 'style')) {
                /**
                 * Class and style attributes should be handled separately in
                 * {@link module:engine/conversion/viewconsumable~ViewConsumable#add `ViewConsumable#add()`}.
                 *
                 * What you have done is trying to use:
                 *
                 *		consumables.add( { attributes: [ 'class', 'style' ] } );
                 *
                 * While each class and style should be registered separately:
                 *
                 *		consumables.add( { classes: 'some-class', styles: 'font-weight' } );
                 *
                 * @error viewconsumable-invalid-attribute
                 */
                throw new CKEditorError('viewconsumable-invalid-attribute', this);
            }
            consumables.set(name, true);
            if (type === 'styles') {
                for (const alsoName of this.element.document.stylesProcessor.getRelatedStyles(name)) {
                    consumables.set(alsoName, true);
                }
            }
        }
    }
    /**
     * Helper method that tests consumables of a given type: attribute, class or style.
     *
     * @private
     * @param {String} type Type of the consumable item: `attributes`, `classes` or `styles`.
     * @param {String|Array.<String>} item Consumable item or array of items.
     * @returns {Boolean|null} Returns `true` if all items can be consumed, `null` when one of the items cannot be
     * consumed and `false` when one of the items is already consumed.
     */
    _test(type, item) {
        const items = lodash_es_isArray(item) ? item : [item];
        const consumables = this._consumables[type];
        for (const name of items) {
            if (type === 'attributes' && (name === 'class' || name === 'style')) {
                const consumableName = name == 'class' ? 'classes' : 'styles';
                // Check all classes/styles if class/style attribute is tested.
                const value = this._test(consumableName, [...this._consumables[consumableName].keys()]);
                if (value !== true) {
                    return value;
                }
            }
            else {
                const value = consumables.get(name);
                // Return null if attribute is not found.
                if (value === undefined) {
                    return null;
                }
                if (!value) {
                    return false;
                }
            }
        }
        return true;
    }
    /**
     * Helper method that consumes items of a given type: attribute, class or style.
     *
     * @private
     * @param {String} type Type of the consumable item: `attributes`, `classes` or `styles`.
     * @param {String|Array.<String>} item Consumable item or array of items.
     */
    _consume(type, item) {
        const items = lodash_es_isArray(item) ? item : [item];
        const consumables = this._consumables[type];
        for (const name of items) {
            if (type === 'attributes' && (name === 'class' || name === 'style')) {
                const consumableName = name == 'class' ? 'classes' : 'styles';
                // If class or style is provided for consumption - consume them all.
                this._consume(consumableName, [...this._consumables[consumableName].keys()]);
            }
            else {
                consumables.set(name, false);
                if (type == 'styles') {
                    for (const toConsume of this.element.document.stylesProcessor.getRelatedStyles(name)) {
                        consumables.set(toConsume, false);
                    }
                }
            }
        }
    }
    /**
     * Helper method that reverts items of a given type: attribute, class or style.
     *
     * @private
     * @param {String} type Type of the consumable item: `attributes`, `classes` or , `styles`.
     * @param {String|Array.<String>} item Consumable item or array of items.
     */
    _revert(type, item) {
        const items = lodash_es_isArray(item) ? item : [item];
        const consumables = this._consumables[type];
        for (const name of items) {
            if (type === 'attributes' && (name === 'class' || name === 'style')) {
                const consumableName = name == 'class' ? 'classes' : 'styles';
                // If class or style is provided for reverting - revert them all.
                this._revert(consumableName, [...this._consumables[consumableName].keys()]);
            }
            else {
                const value = consumables.get(name);
                if (value === false) {
                    consumables.set(name, true);
                }
            }
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/schema.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/schema
 */







/**
 * The model's schema. It defines the allowed and disallowed structures of nodes as well as nodes' attributes.
 * The schema is usually defined by the features and based on them, the editing framework and features
 * make decisions on how to change and process the model.
 *
 * The instance of schema is available in {@link module:engine/model/model~Model#schema `editor.model.schema`}.
 *
 * Read more about the schema in:
 *
 * * The {@glink framework/guides/architecture/editing-engine#schema schema section} of the
 * {@glink framework/guides/architecture/editing-engine Introduction to the Editing engine architecture} guide.
 * * The {@glink framework/guides/deep-dive/schema Schema deep dive} guide.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class Schema extends Observable {
    /**
     * Creates a schema instance.
     */
    constructor() {
        super();
        this._sourceDefinitions = {};
        /**
         * A dictionary containing attribute properties.
         *
         * @private
         * @member {Object.<String,AttributeProperties>}
         */
        this._attributeProperties = {};
        this.decorate('checkChild');
        this.decorate('checkAttribute');
        this.on('checkAttribute', (evt, args) => {
            args[0] = new SchemaContext(args[0]);
        }, { priority: 'highest' });
        this.on('checkChild', (evt, args) => {
            args[0] = new SchemaContext(args[0]);
            args[1] = this.getDefinition(args[1]);
        }, { priority: 'highest' });
    }
    /**
     * Registers a schema item. Can only be called once for every item name.
     *
     *		schema.register( 'paragraph', {
     *			inheritAllFrom: '$block'
     *		} );
     *
     * @param {String} itemName
     * @param {module:engine/model/schema~SchemaItemDefinition} definition
     */
    register(itemName, definition) {
        if (this._sourceDefinitions[itemName]) {
            /**
             * A single item cannot be registered twice in the schema.
             *
             * This situation may happen when:
             *
             * * Two or more plugins called {@link #register `register()`} with the same name. This will usually mean that
             * there is a collision between plugins which try to use the same element in the model. Unfortunately,
             * the only way to solve this is by modifying one of these plugins to use a unique model element name.
             * * A single plugin was loaded twice. This happens when it is installed by npm/yarn in two versions
             * and usually means one or more of the following issues:
             *     * a version mismatch (two of your dependencies require two different versions of this plugin),
             *     * incorrect imports (this plugin is somehow imported twice in a way which confuses webpack),
             *     * mess in `node_modules/` (`rm -rf node_modules/` may help).
             *
             * **Note:** Check the logged `itemName` to better understand which plugin was duplicated/conflicting.
             *
             * @param itemName The name of the model element that is being registered twice.
             * @error schema-cannot-register-item-twice
             */
            throw new CKEditorError('schema-cannot-register-item-twice', this, {
                itemName
            });
        }
        this._sourceDefinitions[itemName] = [
            Object.assign({}, definition)
        ];
        this._clearCache();
    }
    /**
     * Extends a {@link #register registered} item's definition.
     *
     * Extending properties such as `allowIn` will add more items to the existing properties,
     * while redefining properties such as `isBlock` will override the previously defined ones.
     *
     *		schema.register( 'foo', {
     *			allowIn: '$root',
     *			isBlock: true;
     *		} );
     *		schema.extend( 'foo', {
     *			allowIn: 'blockQuote',
     *			isBlock: false
     *		} );
     *
     *		schema.getDefinition( 'foo' );
     *		//	{
     *		//		allowIn: [ '$root', 'blockQuote' ],
     *		// 		isBlock: false
     *		//	}
     *
     * @param {String} itemName
     * @param {module:engine/model/schema~SchemaItemDefinition} definition
     */
    extend(itemName, definition) {
        if (!this._sourceDefinitions[itemName]) {
            /**
             * Cannot extend an item which was not registered yet.
             *
             * This error happens when a plugin tries to extend the schema definition of an item which was not
             * {@link #register registered} yet.
             *
             * @param itemName The name of the model element which is being extended.
             * @error schema-cannot-extend-missing-item
             */
            throw new CKEditorError('schema-cannot-extend-missing-item', this, {
                itemName
            });
        }
        this._sourceDefinitions[itemName].push(Object.assign({}, definition));
        this._clearCache();
    }
    /**
     * Returns data of all registered items.
     *
     * This method should normally be used for reflection purposes (e.g. defining a clone of a certain element,
     * checking a list of all block elements, etc).
     * Use specific methods (such as {@link #checkChild `checkChild()`} or {@link #isLimit `isLimit()`})
     * in other cases.
     *
     * @returns {Object.<String,module:engine/model/schema~SchemaCompiledItemDefinition>}
     */
    getDefinitions() {
        if (!this._compiledDefinitions) {
            this._compile();
        }
        return this._compiledDefinitions;
    }
    /**
     * Returns a definition of the given item or `undefined` if an item is not registered.
     *
     * This method should normally be used for reflection purposes (e.g. defining a clone of a certain element,
     * checking a list of all block elements, etc).
     * Use specific methods (such as {@link #checkChild `checkChild()`} or {@link #isLimit `isLimit()`})
     * in other cases.
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {module:engine/model/schema~SchemaCompiledItemDefinition}
     */
    getDefinition(item) {
        let itemName;
        if (typeof item == 'string') {
            itemName = item;
        }
        else if ('is' in item && (item.is('$text') || item.is('$textProxy'))) {
            itemName = '$text';
        }
        // Element or module:engine/model/schema~SchemaContextItem.
        else {
            itemName = item.name;
        }
        return this.getDefinitions()[itemName];
    }
    /**
     * Returns `true` if the given item is registered in the schema.
     *
     *		schema.isRegistered( 'paragraph' ); // -> true
     *		schema.isRegistered( editor.model.document.getRoot() ); // -> true
     *		schema.isRegistered( 'foo' ); // -> false
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {Boolean}
     */
    isRegistered(item) {
        return !!this.getDefinition(item);
    }
    /**
     * Returns `true` if the given item is defined to be
     * a block by the {@link module:engine/model/schema~SchemaItemDefinition}'s `isBlock` property.
     *
     *		schema.isBlock( 'paragraph' ); // -> true
     *		schema.isBlock( '$root' ); // -> false
     *
     *		const paragraphElement = writer.createElement( 'paragraph' );
     *		schema.isBlock( paragraphElement ); // -> true
     *
     * See the {@glink framework/guides/deep-dive/schema#block-elements Block elements} section of
     * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {Boolean}
     */
    isBlock(item) {
        const def = this.getDefinition(item);
        return !!(def && def.isBlock);
    }
    /**
     * Returns `true` if the given item should be treated as a limit element.
     *
     * It considers an item to be a limit element if its
     * {@link module:engine/model/schema~SchemaItemDefinition}'s
     * {@link module:engine/model/schema~SchemaItemDefinition#isLimit `isLimit`} or
     * {@link module:engine/model/schema~SchemaItemDefinition#isObject `isObject`} property
     * was set to `true`.
     *
     *		schema.isLimit( 'paragraph' ); // -> false
     *		schema.isLimit( '$root' ); // -> true
     *		schema.isLimit( editor.model.document.getRoot() ); // -> true
     *		schema.isLimit( 'imageBlock' ); // -> true
     *
     * See the {@glink framework/guides/deep-dive/schema#limit-elements Limit elements} section of
     * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {Boolean}
     */
    isLimit(item) {
        const def = this.getDefinition(item);
        if (!def) {
            return false;
        }
        return !!(def.isLimit || def.isObject);
    }
    /**
     * Returns `true` if the given item should be treated as an object element.
     *
     * It considers an item to be an object element if its
     * {@link module:engine/model/schema~SchemaItemDefinition}'s
     * {@link module:engine/model/schema~SchemaItemDefinition#isObject `isObject`} property
     * was set to `true`.
     *
     *		schema.isObject( 'paragraph' ); // -> false
     *		schema.isObject( 'imageBlock' ); // -> true
     *
     *		const imageElement = writer.createElement( 'imageBlock' );
     *		schema.isObject( imageElement ); // -> true
     *
     * See the {@glink framework/guides/deep-dive/schema#object-elements Object elements} section of
     * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {Boolean}
     */
    isObject(item) {
        const def = this.getDefinition(item);
        if (!def) {
            return false;
        }
        // Note: Check out the implementation of #isLimit(), #isSelectable(), and #isContent()
        // to understand why these three constitute an object.
        return !!(def.isObject || (def.isLimit && def.isSelectable && def.isContent));
    }
    /**
     * Returns `true` if the given item is defined to be
     * an inline element by the {@link module:engine/model/schema~SchemaItemDefinition}'s `isInline` property.
     *
     *		schema.isInline( 'paragraph' ); // -> false
     *		schema.isInline( 'softBreak' ); // -> true
     *
     *		const text = writer.createText( 'foo' );
     *		schema.isInline( text ); // -> true
     *
     * See the {@glink framework/guides/deep-dive/schema#inline-elements Inline elements} section of
     * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {Boolean}
     */
    isInline(item) {
        const def = this.getDefinition(item);
        return !!(def && def.isInline);
    }
    /**
     * Returns `true` if the given item is defined to be
     * a selectable element by the {@link module:engine/model/schema~SchemaItemDefinition}'s `isSelectable` property.
     *
     *		schema.isSelectable( 'paragraph' ); // -> false
     *		schema.isSelectable( 'heading1' ); // -> false
     *		schema.isSelectable( 'imageBlock' ); // -> true
     *		schema.isSelectable( 'tableCell' ); // -> true
     *
     *		const text = writer.createText( 'foo' );
     *		schema.isSelectable( text ); // -> false
     *
     * See the {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements section} of
     * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {Boolean}
     */
    isSelectable(item) {
        const def = this.getDefinition(item);
        if (!def) {
            return false;
        }
        return !!(def.isSelectable || def.isObject);
    }
    /**
     * Returns `true` if the given item is defined to be
     * a content by the {@link module:engine/model/schema~SchemaItemDefinition}'s `isContent` property.
     *
     *		schema.isContent( 'paragraph' ); // -> false
     *		schema.isContent( 'heading1' ); // -> false
     *		schema.isContent( 'imageBlock' ); // -> true
     *		schema.isContent( 'horizontalLine' ); // -> true
     *
     *		const text = writer.createText( 'foo' );
     *		schema.isContent( text ); // -> true
     *
     * See the {@glink framework/guides/deep-dive/schema#content-elements Content elements section} of
     * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
     *
     * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
     * @returns {Boolean}
     */
    isContent(item) {
        const def = this.getDefinition(item);
        if (!def) {
            return false;
        }
        return !!(def.isContent || def.isObject);
    }
    /**
     * Checks whether the given node (`child`) can be a child of the given context.
     *
     *		schema.checkChild( model.document.getRoot(), paragraph ); // -> false
     *
     *		schema.register( 'paragraph', {
     *			allowIn: '$root'
     *		} );
     *		schema.checkChild( model.document.getRoot(), paragraph ); // -> true
     *
     * Note: When verifying whether the given node can be a child of the given context, the
     * schema also verifies the entire context &mdash; from its root to its last element. Therefore, it is possible
     * for `checkChild()` to return `false` even though the context's last element can contain the checked child.
     * It happens if one of the context's elements does not allow its child.
     *
     * @fires checkChild
     * @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the child will be checked.
     * @param {module:engine/model/node~Node|String} def The child to check.
     * @returns {Boolean}
     */
    checkChild(context, def) {
        // Note: context and child are already normalized here to a SchemaContext and SchemaCompiledItemDefinition.
        if (!def) {
            return false;
        }
        return this._checkContextMatch(def, context);
    }
    /**
     * Checks whether the given attribute can be applied in the given context (on the last
     * item of the context).
     *
     *		schema.checkAttribute( textNode, 'bold' ); // -> false
     *
     *		schema.extend( '$text', {
     *			allowAttributes: 'bold'
     *		} );
     *		schema.checkAttribute( textNode, 'bold' ); // -> true
     *
     * @fires checkAttribute
     * @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the attribute will be checked.
     * @param {String} attributeName
     * @returns {Boolean}
     */
    checkAttribute(context, attributeName) {
        const def = this.getDefinition(context.last);
        if (!def) {
            return false;
        }
        return def.allowAttributes.includes(attributeName);
    }
    /**
     * Checks whether the given element (`elementToMerge`) can be merged with the specified base element (`positionOrBaseElement`).
     *
     * In other words &mdash; whether `elementToMerge`'s children {@link #checkChild are allowed} in the `positionOrBaseElement`.
     *
     * This check ensures that elements merged with {@link module:engine/model/writer~Writer#merge `Writer#merge()`}
     * will be valid.
     *
     * Instead of elements, you can pass the instance of the {@link module:engine/model/position~Position} class as the
     * `positionOrBaseElement`. It means that the elements before and after the position will be checked whether they can be merged.
     *
     * @param {module:engine/model/position~Position|module:engine/model/element~Element} positionOrBaseElement The position or base
     * element to which the `elementToMerge` will be merged.
     * @param {module:engine/model/element~Element} elementToMerge The element to merge. Required if `positionOrBaseElement` is an element.
     * @returns {Boolean}
     */
    checkMerge(positionOrBaseElement, elementToMerge) {
        if (positionOrBaseElement instanceof position_Position) {
            const nodeBefore = positionOrBaseElement.nodeBefore;
            const nodeAfter = positionOrBaseElement.nodeAfter;
            if (!(nodeBefore instanceof element_Element)) {
                /**
                 * The node before the merge position must be an element.
                 *
                 * @error schema-check-merge-no-element-before
                 */
                throw new CKEditorError('schema-check-merge-no-element-before', this);
            }
            if (!(nodeAfter instanceof element_Element)) {
                /**
                 * The node after the merge position must be an element.
                 *
                 * @error schema-check-merge-no-element-after
                 */
                throw new CKEditorError('schema-check-merge-no-element-after', this);
            }
            return this.checkMerge(nodeBefore, nodeAfter);
        }
        for (const child of elementToMerge.getChildren()) {
            if (!this.checkChild(positionOrBaseElement, child)) {
                return false;
            }
        }
        return true;
    }
    /**
     * Allows registering a callback to the {@link #checkChild} method calls.
     *
     * Callbacks allow you to implement rules which are not otherwise possible to achieve
     * by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
     * For example, by using this method you can disallow elements in specific contexts.
     *
     * This method is a shorthand for using the {@link #event:checkChild} event. For even better control,
     * you can use that event instead.
     *
     * Example:
     *
     *		// Disallow heading1 directly inside a blockQuote.
     *		schema.addChildCheck( ( context, childDefinition ) => {
     *			if ( context.endsWith( 'blockQuote' ) && childDefinition.name == 'heading1' ) {
     *				return false;
     *			}
     *		} );
     *
     * Which translates to:
     *
     *		schema.on( 'checkChild', ( evt, args ) => {
     *			const context = args[ 0 ];
     *			const childDefinition = args[ 1 ];
     *
     *			if ( context.endsWith( 'blockQuote' ) && childDefinition && childDefinition.name == 'heading1' ) {
     *				// Prevent next listeners from being called.
     *				evt.stop();
     *				// Set the checkChild()'s return value.
     *				evt.return = false;
     *			}
     *		}, { priority: 'high' } );
     *
     * @param {Function} callback The callback to be called. It is called with two parameters:
     * {@link module:engine/model/schema~SchemaContext} (context) instance and
     * {@link module:engine/model/schema~SchemaCompiledItemDefinition} (child-to-check definition).
     * The callback may return `true/false` to override `checkChild()`'s return value. If it does not return
     * a boolean value, the default algorithm (or other callbacks) will define `checkChild()`'s return value.
     */
    addChildCheck(callback) {
        this.on('checkChild', (evt, [ctx, childDef]) => {
            // checkChild() was called with a non-registered child.
            // In 99% cases such check should return false, so not to overcomplicate all callbacks
            // don't even execute them.
            if (!childDef) {
                return;
            }
            const retValue = callback(ctx, childDef);
            if (typeof retValue == 'boolean') {
                evt.stop();
                evt.return = retValue;
            }
        }, { priority: 'high' });
    }
    /**
     * Allows registering a callback to the {@link #checkAttribute} method calls.
     *
     * Callbacks allow you to implement rules which are not otherwise possible to achieve
     * by using the declarative API of {@link module:engine/model/schema~SchemaItemDefinition}.
     * For example, by using this method you can disallow attribute if node to which it is applied
     * is contained within some other element (e.g. you want to disallow `bold` on `$text` within `heading1`).
     *
     * This method is a shorthand for using the {@link #event:checkAttribute} event. For even better control,
     * you can use that event instead.
     *
     * Example:
     *
     *		// Disallow bold on $text inside heading1.
     *		schema.addAttributeCheck( ( context, attributeName ) => {
     *			if ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {
     *				return false;
     *			}
     *		} );
     *
     * Which translates to:
     *
     *		schema.on( 'checkAttribute', ( evt, args ) => {
     *			const context = args[ 0 ];
     *			const attributeName = args[ 1 ];
     *
     *			if ( context.endsWith( 'heading1 $text' ) && attributeName == 'bold' ) {
     *				// Prevent next listeners from being called.
     *				evt.stop();
     *				// Set the checkAttribute()'s return value.
     *				evt.return = false;
     *			}
     *		}, { priority: 'high' } );
     *
     * @param {Function} callback The callback to be called. It is called with two parameters:
     * {@link module:engine/model/schema~SchemaContext} (context) instance and attribute name.
     * The callback may return `true/false` to override `checkAttribute()`'s return value. If it does not return
     * a boolean value, the default algorithm (or other callbacks) will define `checkAttribute()`'s return value.
     */
    addAttributeCheck(callback) {
        this.on('checkAttribute', (evt, [ctx, attributeName]) => {
            const retValue = callback(ctx, attributeName);
            if (typeof retValue == 'boolean') {
                evt.stop();
                evt.return = retValue;
            }
        }, { priority: 'high' });
    }
    /**
     * This method allows assigning additional metadata to the model attributes. For example,
     * {@link module:engine/model/schema~AttributeProperties `AttributeProperties#isFormatting` property} is
     * used to mark formatting attributes (like `bold` or `italic`).
     *
     *		// Mark bold as a formatting attribute.
     *		schema.setAttributeProperties( 'bold', {
     *			isFormatting: true
     *		} );
     *
     *		// Override code not to be considered a formatting markup.
     *		schema.setAttributeProperties( 'code', {
     *			isFormatting: false
     *		} );
     *
     * Properties are not limited to members defined in the
     * {@link module:engine/model/schema~AttributeProperties `AttributeProperties` type} and you can also use custom properties:
     *
     *		schema.setAttributeProperties( 'blockQuote', {
     *			customProperty: 'value'
     *		} );
     *
     * Subsequent calls with the same attribute will extend its custom properties:
     *
     *		schema.setAttributeProperties( 'blockQuote', {
     *			one: 1
     *		} );
     *
     *		schema.setAttributeProperties( 'blockQuote', {
     *			two: 2
     *		} );
     *
     *		console.log( schema.getAttributeProperties( 'blockQuote' ) );
     *		// Logs: { one: 1, two: 2 }
     *
     * @param {String} attributeName A name of the attribute to receive the properties.
     * @param {module:engine/model/schema~AttributeProperties} properties A dictionary of properties.
     */
    setAttributeProperties(attributeName, properties) {
        this._attributeProperties[attributeName] = Object.assign(this.getAttributeProperties(attributeName), properties);
    }
    /**
     * Returns properties associated with a given model attribute. See {@link #setAttributeProperties `setAttributeProperties()`}.
     *
     * @param {String} attributeName A name of the attribute.
     * @returns {module:engine/model/schema~AttributeProperties}
     */
    getAttributeProperties(attributeName) {
        return this._attributeProperties[attributeName] || {};
    }
    /**
     * Returns the lowest {@link module:engine/model/schema~Schema#isLimit limit element} containing the entire
     * selection/range/position or the root otherwise.
     *
     * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection|
     * module:engine/model/range~Range|module:engine/model/position~Position} selectionOrRangeOrPosition
     * The selection/range/position to check.
     * @returns {module:engine/model/element~Element} The lowest limit element containing
     * the entire `selectionOrRangeOrPosition`.
     */
    getLimitElement(selectionOrRangeOrPosition) {
        let element;
        if (selectionOrRangeOrPosition instanceof position_Position) {
            element = selectionOrRangeOrPosition.parent;
        }
        else {
            const ranges = selectionOrRangeOrPosition instanceof range_Range ?
                [selectionOrRangeOrPosition] :
                Array.from(selectionOrRangeOrPosition.getRanges());
            // Find the common ancestor for all selection's ranges.
            element = ranges
                .reduce((element, range) => {
                const rangeCommonAncestor = range.getCommonAncestor();
                if (!element) {
                    return rangeCommonAncestor;
                }
                return element.getCommonAncestor(rangeCommonAncestor, { includeSelf: true });
            }, null);
        }
        while (!this.isLimit(element)) {
            if (element.parent) {
                element = element.parent;
            }
            else {
                break;
            }
        }
        return element;
    }
    /**
     * Checks whether the attribute is allowed in selection:
     *
     * * if the selection is not collapsed, then checks if the attribute is allowed on any of nodes in that range,
     * * if the selection is collapsed, then checks if on the selection position there's a text with the
     * specified attribute allowed.
     *
     * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
     * Selection which will be checked.
     * @param {String} attribute The name of the attribute to check.
     * @returns {Boolean}
     */
    checkAttributeInSelection(selection, attribute) {
        if (selection.isCollapsed) {
            const firstPosition = selection.getFirstPosition();
            const context = [
                ...firstPosition.getAncestors(),
                new model_text_Text('', selection.getAttributes())
            ];
            // Check whether schema allows for a text with the attribute in the selection.
            return this.checkAttribute(context, attribute);
        }
        else {
            const ranges = selection.getRanges();
            // For all ranges, check nodes in them until you find a node that is allowed to have the attribute.
            for (const range of ranges) {
                for (const value of range) {
                    if (this.checkAttribute(value.item, attribute)) {
                        // If we found a node that is allowed to have the attribute, return true.
                        return true;
                    }
                }
            }
        }
        // If we haven't found such node, return false.
        return false;
    }
    /**
     * Transforms the given set of ranges into a set of ranges where the given attribute is allowed (and can be applied).
     *
     * @param {Iterable.<module:engine/model/range~Range>} ranges Ranges to be validated.
     * @param {String} attribute The name of the attribute to check.
     * @returns {Iterable.<module:engine/model/range~Range>} Ranges in which the attribute is allowed.
     */
    *getValidRanges(ranges, attribute) {
        ranges = convertToMinimalFlatRanges(ranges);
        for (const range of ranges) {
            yield* this._getValidRangesForRange(range, attribute);
        }
    }
    /**
     * Basing on given `position`, finds and returns a {@link module:engine/model/range~Range range} which is
     * nearest to that `position` and is a correct range for selection.
     *
     * The correct selection range might be collapsed when it is located in a position where the text node can be placed.
     * Non-collapsed range is returned when selection can be placed around element marked as an "object" in
     * the {@link module:engine/model/schema~Schema schema}.
     *
     * Direction of searching for the nearest correct selection range can be specified as:
     *
     * * `both` - searching will be performed in both ways,
     * * `forward` - searching will be performed only forward,
     * * `backward` - searching will be performed only backward.
     *
     * When valid selection range cannot be found, `null` is returned.
     *
     * @param {module:engine/model/position~Position} position Reference position where new selection range should be looked for.
     * @param {'both'|'forward'|'backward'} [direction='both'] Search direction.
     * @returns {module:engine/model/range~Range|null} Nearest selection range or `null` if one cannot be found.
     */
    getNearestSelectionRange(position, direction = 'both') {
        // Return collapsed range if provided position is valid.
        if (this.checkChild(position, '$text')) {
            return new range_Range(position);
        }
        let backwardWalker, forwardWalker;
        // Never leave a limit element.
        const limitElement = position.getAncestors().reverse().find(item => this.isLimit(item)) ||
            position.root;
        if (direction == 'both' || direction == 'backward') {
            backwardWalker = new model_treewalker_TreeWalker({
                boundaries: range_Range._createIn(limitElement),
                startPosition: position,
                direction: 'backward'
            });
        }
        if (direction == 'both' || direction == 'forward') {
            forwardWalker = new model_treewalker_TreeWalker({
                boundaries: range_Range._createIn(limitElement),
                startPosition: position
            });
        }
        for (const data of combineWalkers(backwardWalker, forwardWalker)) {
            const type = (data.walker == backwardWalker ? 'elementEnd' : 'elementStart');
            const value = data.value;
            if (value.type == type && this.isObject(value.item)) {
                return range_Range._createOn(value.item);
            }
            if (this.checkChild(value.nextPosition, '$text')) {
                return new range_Range(value.nextPosition);
            }
        }
        return null;
    }
    /**
     * Tries to find position ancestors that allow to insert a given node.
     * It starts searching from the given position and goes node by node to the top of the model tree
     * as long as a {@link module:engine/model/schema~Schema#isLimit limit element}, an
     * {@link module:engine/model/schema~Schema#isObject object element} or a topmost ancestor is not reached.
     *
     * @param {module:engine/model/position~Position} position The position that the search will start from.
     * @param {module:engine/model/node~Node|String} node The node for which an allowed parent should be found or its name.
     * @returns {module:engine/model/element~Element|null} element Allowed parent or null if nothing was found.
     */
    findAllowedParent(position, node) {
        let parent = position.parent;
        while (parent) {
            if (this.checkChild(parent, node)) {
                return parent;
            }
            // Do not split limit elements.
            if (this.isLimit(parent)) {
                return null;
            }
            parent = parent.parent;
        }
        return null;
    }
    /**
     * Sets attributes allowed by the schema on a given node.
     *
     * @param {module:engine/model/node~Node} node A node to set attributes on.
     * @param {Object} attributes Attributes keys and values.
     * @param {module:engine/model/writer~Writer} writer An instance of the model writer.
     */
    setAllowedAttributes(node, attributes, writer) {
        const model = writer.model;
        for (const [attributeName, attributeValue] of Object.entries(attributes)) {
            if (model.schema.checkAttribute(node, attributeName)) {
                writer.setAttribute(attributeName, attributeValue, node);
            }
        }
    }
    /**
     * Removes attributes disallowed by the schema.
     *
     * @param {Iterable.<module:engine/model/node~Node>} nodes Nodes that will be filtered.
     * @param {module:engine/model/writer~Writer} writer
     */
    removeDisallowedAttributes(nodes, writer) {
        for (const node of nodes) {
            // When node is a `Text` it has no children, so just filter it out.
            if (node.is('$text')) {
                removeDisallowedAttributeFromNode(this, node, writer);
            }
            // In a case of `Element` iterates through positions between nodes inside this element
            // and filter out node before the current position, or position parent when position
            // is at start of an element. Using positions prevent from omitting merged nodes
            // see https://github.com/ckeditor/ckeditor5-engine/issues/1789.
            else {
                const rangeInNode = range_Range._createIn(node);
                const positionsInRange = rangeInNode.getPositions();
                for (const position of positionsInRange) {
                    const item = position.nodeBefore || position.parent;
                    removeDisallowedAttributeFromNode(this, item, writer);
                }
            }
        }
    }
    /**
     * Gets attributes of a node that have a given property.
     *
     * @param {module:engine/model/node~Node} node Node to get attributes from.
     * @param {String} propertyName Name of the property that attribute must have to return it.
     * @param {Boolean|Symbol|String|Number|Object|null|undefined} propertyValue Desired value of the property that we want to check.
     * When `undefined` attributes will be returned if they have set a given property no matter what the value is. If specified it will
     * return attributes which given property's value is equal to this parameter.
     * @returns {Object} Object with attributes' names as key and attributes' values as value.
     */
    getAttributesWithProperty(node, propertyName, propertyValue) {
        const attributes = {};
        for (const [attributeName, attributeValue] of node.getAttributes()) {
            const attributeProperties = this.getAttributeProperties(attributeName);
            if (attributeProperties[propertyName] === undefined) {
                continue;
            }
            if (propertyValue === undefined || propertyValue === attributeProperties[propertyName]) {
                attributes[attributeName] = attributeValue;
            }
        }
        return attributes;
    }
    /**
     * Creates an instance of the schema context.
     *
     * @param {module:engine/model/schema~SchemaContextDefinition} context
     * @returns {module:engine/model/schema~SchemaContext}
     */
    createContext(context) {
        return new SchemaContext(context);
    }
    /**
     * @private
     */
    _clearCache() {
        this._compiledDefinitions = null;
    }
    /**
     * @private
     */
    _compile() {
        const compiledDefinitions = {};
        const sourceRules = this._sourceDefinitions;
        const itemNames = Object.keys(sourceRules);
        for (const itemName of itemNames) {
            compiledDefinitions[itemName] = compileBaseItemRule(sourceRules[itemName], itemName);
        }
        for (const itemName of itemNames) {
            compileAllowChildren(compiledDefinitions, itemName);
        }
        for (const itemName of itemNames) {
            compileAllowContentOf(compiledDefinitions, itemName);
        }
        for (const itemName of itemNames) {
            compileAllowWhere(compiledDefinitions, itemName);
        }
        for (const itemName of itemNames) {
            compileAllowAttributesOf(compiledDefinitions, itemName);
            compileInheritPropertiesFrom(compiledDefinitions, itemName);
        }
        for (const itemName of itemNames) {
            cleanUpAllowIn(compiledDefinitions, itemName);
            setupAllowChildren(compiledDefinitions, itemName);
            cleanUpAllowAttributes(compiledDefinitions, itemName);
        }
        this._compiledDefinitions = compiledDefinitions;
    }
    /**
     * @private
     * @param {module:engine/model/schema~SchemaCompiledItemDefinition} def
     * @param {module:engine/model/schema~SchemaContext} context
     * @param {Number} contextItemIndex
     */
    _checkContextMatch(def, context, contextItemIndex = context.length - 1) {
        const contextItem = context.getItem(contextItemIndex);
        if (def.allowIn.includes(contextItem.name)) {
            if (contextItemIndex == 0) {
                return true;
            }
            else {
                const parentRule = this.getDefinition(contextItem);
                return this._checkContextMatch(parentRule, context, contextItemIndex - 1);
            }
        }
        else {
            return false;
        }
    }
    /**
     * Takes a flat range and an attribute name. Traverses the range recursively and deeply to find and return all ranges
     * inside the given range on which the attribute can be applied.
     *
     * This is a helper function for {@link ~Schema#getValidRanges}.
     *
     * @private
     * @param {module:engine/model/range~Range} range The range to process.
     * @param {String} attribute The name of the attribute to check.
     * @returns {Iterable.<module:engine/model/range~Range>} Ranges in which the attribute is allowed.
     */
    *_getValidRangesForRange(range, attribute) {
        let start = range.start;
        let end = range.start;
        for (const item of range.getItems({ shallow: true })) {
            if (item.is('element')) {
                yield* this._getValidRangesForRange(range_Range._createIn(item), attribute);
            }
            if (!this.checkAttribute(item, attribute)) {
                if (!start.isEqual(end)) {
                    yield new range_Range(start, end);
                }
                start = position_Position._createAfter(item);
            }
            end = position_Position._createAfter(item);
        }
        if (!start.isEqual(end)) {
            yield new range_Range(start, end);
        }
    }
}
/**
 * A schema context &mdash; a list of ancestors of a given position in the document.
 *
 * Considering such position:
 *
 *		<$root>
 *			<blockQuote>
 *				<paragraph>
 *					^
 *				</paragraph>
 *			</blockQuote>
 *		</$root>
 *
 * The context of this position is its {@link module:engine/model/position~Position#getAncestors lists of ancestors}:
 *
 *		[ rootElement, blockQuoteElement, paragraphElement ]
 *
 * Contexts are used in the {@link module:engine/model/schema~Schema#event:checkChild `Schema#checkChild`} and
 * {@link module:engine/model/schema~Schema#event:checkAttribute `Schema#checkAttribute`} events as a definition
 * of a place in the document where the check occurs. The context instances are created based on the first arguments
 * of the {@link module:engine/model/schema~Schema#checkChild `Schema#checkChild()`} and
 * {@link module:engine/model/schema~Schema#checkAttribute `Schema#checkAttribute()`} methods so when
 * using these methods you need to use {@link module:engine/model/schema~SchemaContextDefinition}s.
 */
class SchemaContext {
    /**
     * Creates an instance of the context.
     *
     * @param {module:engine/model/schema~SchemaContextDefinition} context
     */
    constructor(context) {
        if (context instanceof SchemaContext) {
            return context;
        }
        let items;
        if (typeof context == 'string') {
            items = [context];
        }
        else if (!Array.isArray(context)) {
            // `context` is item or position.
            // Position#getAncestors() doesn't accept any parameters but it works just fine here.
            items = context.getAncestors({ includeSelf: true });
        }
        else {
            items = context;
        }
        this._items = items.map(mapContextItem);
    }
    /**
     * The number of items.
     *
     * @type {Number}
     */
    get length() {
        return this._items.length;
    }
    /**
     * The last item (the lowest node).
     *
     * @type {module:engine/model/schema~SchemaContextItem}
     */
    get last() {
        return this._items[this._items.length - 1];
    }
    /**
     * Iterable interface.
     *
     * Iterates over all context items.
     *
     * @returns {Iterable.<module:engine/model/schema~SchemaContextItem>}
     */
    [Symbol.iterator]() {
        return this._items[Symbol.iterator]();
    }
    /**
     * Returns a new schema context instance with an additional item.
     *
     * Item can be added as:
     *
     * 		const context = new SchemaContext( [ '$root' ] );
     *
     * 		// An element.
     * 		const fooElement = writer.createElement( 'fooElement' );
     * 		const newContext = context.push( fooElement ); // [ '$root', 'fooElement' ]
     *
     * 		// A text node.
     * 		const text = writer.createText( 'foobar' );
     * 		const newContext = context.push( text ); // [ '$root', '$text' ]
     *
     * 		// A string (element name).
     * 		const newContext = context.push( 'barElement' ); // [ '$root', 'barElement' ]
     *
     * **Note** {@link module:engine/model/node~Node} that is already in the model tree will be added as the only item
     * (without ancestors).
     *
     * @param {String|module:engine/model/node~Node} item An item that will be added
     * to the current context.
     * @returns {module:engine/model/schema~SchemaContext} A new schema context instance with an additional item.
     */
    push(item) {
        const ctx = new SchemaContext([item]);
        ctx._items = [...this._items, ...ctx._items];
        return ctx;
    }
    /**
     * Gets an item on the given index.
     *
     * @returns {module:engine/model/schema~SchemaContextItem}
     */
    getItem(index) {
        return this._items[index];
    }
    /**
     * Returns the names of items.
     *
     * @returns {Iterable.<String>}
     */
    *getNames() {
        yield* this._items.map(item => item.name);
    }
    /**
     * Checks whether the context ends with the given nodes.
     *
     *		const ctx = new SchemaContext( [ rootElement, paragraphElement, textNode ] );
     *
     *		ctx.endsWith( '$text' ); // -> true
     *		ctx.endsWith( 'paragraph $text' ); // -> true
     *		ctx.endsWith( '$root' ); // -> false
     *		ctx.endsWith( 'paragraph' ); // -> false
     *
     * @param {String} query
     * @returns {Boolean}
     */
    endsWith(query) {
        return Array.from(this.getNames()).join(' ').endsWith(query);
    }
    /**
     * Checks whether the context starts with the given nodes.
     *
     *		const ctx = new SchemaContext( [ rootElement, paragraphElement, textNode ] );
     *
     *		ctx.endsWith( '$root' ); // -> true
     *		ctx.endsWith( '$root paragraph' ); // -> true
     *		ctx.endsWith( '$text' ); // -> false
     *		ctx.endsWith( 'paragraph' ); // -> false
     *
     * @param {String} query
     * @returns {Boolean}
     */
    startsWith(query) {
        return Array.from(this.getNames()).join(' ').startsWith(query);
    }
}
function compileBaseItemRule(sourceItemRules, itemName) {
    const itemRule = {
        name: itemName,
        allowIn: [],
        allowContentOf: [],
        allowWhere: [],
        allowAttributes: [],
        allowAttributesOf: [],
        allowChildren: [],
        inheritTypesFrom: []
    };
    copyTypes(sourceItemRules, itemRule);
    copyProperty(sourceItemRules, itemRule, 'allowIn');
    copyProperty(sourceItemRules, itemRule, 'allowContentOf');
    copyProperty(sourceItemRules, itemRule, 'allowWhere');
    copyProperty(sourceItemRules, itemRule, 'allowAttributes');
    copyProperty(sourceItemRules, itemRule, 'allowAttributesOf');
    copyProperty(sourceItemRules, itemRule, 'allowChildren');
    copyProperty(sourceItemRules, itemRule, 'inheritTypesFrom');
    makeInheritAllWork(sourceItemRules, itemRule);
    return itemRule;
}
function compileAllowChildren(compiledDefinitions, itemName) {
    const item = compiledDefinitions[itemName];
    for (const allowChildrenItem of item.allowChildren) {
        const allowedChildren = compiledDefinitions[allowChildrenItem];
        // The allowChildren property may point to an unregistered element.
        if (!allowedChildren) {
            continue;
        }
        allowedChildren.allowIn.push(itemName);
    }
    // The allowIn property already includes correct items, reset the allowChildren property
    // to avoid duplicates later when setting up compilation results.
    item.allowChildren.length = 0;
}
function compileAllowContentOf(compiledDefinitions, itemName) {
    for (const allowContentOfItemName of compiledDefinitions[itemName].allowContentOf) {
        // The allowContentOf property may point to an unregistered element.
        if (compiledDefinitions[allowContentOfItemName]) {
            const allowedChildren = getAllowedChildren(compiledDefinitions, allowContentOfItemName);
            allowedChildren.forEach(allowedItem => {
                allowedItem.allowIn.push(itemName);
            });
        }
    }
    delete compiledDefinitions[itemName].allowContentOf;
}
function compileAllowWhere(compiledDefinitions, itemName) {
    for (const allowWhereItemName of compiledDefinitions[itemName].allowWhere) {
        const inheritFrom = compiledDefinitions[allowWhereItemName];
        // The allowWhere property may point to an unregistered element.
        if (inheritFrom) {
            const allowedIn = inheritFrom.allowIn;
            compiledDefinitions[itemName].allowIn.push(...allowedIn);
        }
    }
    delete compiledDefinitions[itemName].allowWhere;
}
function compileAllowAttributesOf(compiledDefinitions, itemName) {
    for (const allowAttributeOfItem of compiledDefinitions[itemName].allowAttributesOf) {
        const inheritFrom = compiledDefinitions[allowAttributeOfItem];
        if (inheritFrom) {
            const inheritAttributes = inheritFrom.allowAttributes;
            compiledDefinitions[itemName].allowAttributes.push(...inheritAttributes);
        }
    }
    delete compiledDefinitions[itemName].allowAttributesOf;
}
function compileInheritPropertiesFrom(compiledDefinitions, itemName) {
    const item = compiledDefinitions[itemName];
    for (const inheritPropertiesOfItem of item.inheritTypesFrom) {
        const inheritFrom = compiledDefinitions[inheritPropertiesOfItem];
        if (inheritFrom) {
            const typeNames = Object.keys(inheritFrom).filter(name => name.startsWith('is'));
            for (const name of typeNames) {
                if (!(name in item)) {
                    item[name] = inheritFrom[name];
                }
            }
        }
    }
    delete item.inheritTypesFrom;
}
// Remove items which weren't registered (because it may break some checks or we'd need to complicate them).
// Make sure allowIn doesn't contain repeated values.
function cleanUpAllowIn(compiledDefinitions, itemName) {
    const itemRule = compiledDefinitions[itemName];
    const existingItems = itemRule.allowIn.filter(itemToCheck => compiledDefinitions[itemToCheck]);
    itemRule.allowIn = Array.from(new Set(existingItems));
}
// Setup allowChildren items based on allowIn.
function setupAllowChildren(compiledDefinitions, itemName) {
    const itemRule = compiledDefinitions[itemName];
    for (const allowedParentItemName of itemRule.allowIn) {
        const allowedParentItem = compiledDefinitions[allowedParentItemName];
        allowedParentItem.allowChildren.push(itemName);
    }
}
function cleanUpAllowAttributes(compiledDefinitions, itemName) {
    const itemRule = compiledDefinitions[itemName];
    itemRule.allowAttributes = Array.from(new Set(itemRule.allowAttributes));
}
function copyTypes(sourceItemRules, itemRule) {
    for (const sourceItemRule of sourceItemRules) {
        const typeNames = Object.keys(sourceItemRule).filter(name => name.startsWith('is'));
        for (const name of typeNames) {
            itemRule[name] = !!sourceItemRule[name];
        }
    }
}
function copyProperty(sourceItemRules, itemRule, propertyName) {
    for (const sourceItemRule of sourceItemRules) {
        const value = sourceItemRule[propertyName];
        if (typeof value == 'string') {
            itemRule[propertyName].push(value);
        }
        else if (Array.isArray(value)) {
            itemRule[propertyName].push(...value);
        }
    }
}
function makeInheritAllWork(sourceItemRules, itemRule) {
    for (const sourceItemRule of sourceItemRules) {
        const inheritFrom = sourceItemRule.inheritAllFrom;
        if (inheritFrom) {
            itemRule.allowContentOf.push(inheritFrom);
            itemRule.allowWhere.push(inheritFrom);
            itemRule.allowAttributesOf.push(inheritFrom);
            itemRule.inheritTypesFrom.push(inheritFrom);
        }
    }
}
function getAllowedChildren(compiledDefinitions, itemName) {
    const itemRule = compiledDefinitions[itemName];
    return getValues(compiledDefinitions).filter(def => def.allowIn.includes(itemRule.name));
}
function getValues(obj) {
    return Object.keys(obj).map(key => obj[key]);
}
function mapContextItem(ctxItem) {
    if (typeof ctxItem == 'string' || ctxItem.is('documentFragment')) {
        return {
            name: typeof ctxItem == 'string' ? ctxItem : '$documentFragment',
            *getAttributeKeys() { },
            getAttribute() { }
        };
    }
    else {
        return {
            // '$text' means text nodes and text proxies.
            name: ctxItem.is('element') ? ctxItem.name : '$text',
            *getAttributeKeys() {
                yield* ctxItem.getAttributeKeys();
            },
            getAttribute(key) {
                return ctxItem.getAttribute(key);
            }
        };
    }
}
// Generator function returning values from provided walkers, switching between them at each iteration. If only one walker
// is provided it will return data only from that walker.
//
// @param {module:engine/module/treewalker~TreeWalker} [backward] Walker iterating in backward direction.
// @param {module:engine/module/treewalker~TreeWalker} [forward] Walker iterating in forward direction.
// @returns {Iterable.<Object>} Object returned at each iteration contains `value` and `walker` (informing which walker returned
// given value) fields.
function* combineWalkers(backward, forward) {
    let done = false;
    while (!done) {
        done = true;
        if (backward) {
            const step = backward.next();
            if (!step.done) {
                done = false;
                yield {
                    walker: backward,
                    value: step.value
                };
            }
        }
        if (forward) {
            const step = forward.next();
            if (!step.done) {
                done = false;
                yield {
                    walker: forward,
                    value: step.value
                };
            }
        }
    }
}
// Takes an array of non-intersecting ranges. For each of them gets minimal flat ranges covering that range and returns
// all those minimal flat ranges.
//
// @param {Array.<module:engine/model/range~Range>} ranges Ranges to process.
// @returns {Iterable.<module:engine/model/range~Range>} Minimal flat ranges of given `ranges`.
function* convertToMinimalFlatRanges(ranges) {
    for (const range of ranges) {
        yield* range.getMinimalFlatRanges();
    }
}
function removeDisallowedAttributeFromNode(schema, node, writer) {
    for (const attribute of node.getAttributeKeys()) {
        if (!schema.checkAttribute(node, attribute)) {
            writer.removeAttribute(attribute, node);
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/upcastdispatcher.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/conversion/upcastdispatcher
 */



 // eslint-disable-line no-duplicate-imports



/**
 * Upcast dispatcher is a central point of the view-to-model conversion, which is a process of
 * converting a given {@link module:engine/view/documentfragment~DocumentFragment view document fragment} or
 * {@link module:engine/view/element~Element view element} into a correct model structure.
 *
 * During the conversion process, the dispatcher fires events for all {@link module:engine/view/node~Node view nodes}
 * from the converted view document fragment.
 * Special callbacks called "converters" should listen to these events in order to convert the view nodes.
 *
 * The second parameter of the callback is the `data` object with the following properties:
 *
 * * `data.viewItem` contains a {@link module:engine/view/node~Node view node} or a
 * {@link module:engine/view/documentfragment~DocumentFragment view document fragment}
 * that is converted at the moment and might be handled by the callback.
 * * `data.modelRange` is used to point to the result
 * of the current conversion (e.g. the element that is being inserted)
 * and is always a {@link module:engine/model/range~Range} when the conversion succeeds.
 * * `data.modelCursor` is a {@link module:engine/model/position~Position position} on which the converter should insert
 * the newly created items.
 *
 * The third parameter of the callback is an instance of {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi}
 * which provides additional tools for converters.
 *
 * You can read more about conversion in the {@glink framework/guides/deep-dive/conversion/upcast Upcast conversion} guide.
 *
 * Examples of event-based converters:
 *
 *		// A converter for links (<a>).
 *		editor.data.upcastDispatcher.on( 'element:a', ( evt, data, conversionApi ) => {
 *			if ( conversionApi.consumable.consume( data.viewItem, { name: true, attributes: [ 'href' ] } ) ) {
 *				// The <a> element is inline and is represented by an attribute in the model.
 *				// This is why you need to convert only children.
 *				const { modelRange } = conversionApi.convertChildren( data.viewItem, data.modelCursor );
 *
 *				for ( let item of modelRange.getItems() ) {
 *					if ( conversionApi.schema.checkAttribute( item, 'linkHref' ) ) {
 *						conversionApi.writer.setAttribute( 'linkHref', data.viewItem.getAttribute( 'href' ), item );
 *					}
 *				}
 *			}
 *		} );
 *
 *		// Convert <p> element's font-size style.
 *		// Note: You should use a low-priority observer in order to ensure that
 *		// it is executed after the element-to-element converter.
 *		editor.data.upcastDispatcher.on( 'element:p', ( evt, data, conversionApi ) => {
 *			const { consumable, schema, writer } = conversionApi;
 *
 *			if ( !consumable.consume( data.viewItem, { style: 'font-size' } ) ) {
 *				return;
 *			}
 *
 *			const fontSize = data.viewItem.getStyle( 'font-size' );
 *
 *			// Do not go for the model element after data.modelCursor because it might happen
 *			// that a single view element was converted to multiple model elements. Get all of them.
 *			for ( const item of data.modelRange.getItems( { shallow: true } ) ) {
 *				if ( schema.checkAttribute( item, 'fontSize' ) ) {
 *					writer.setAttribute( 'fontSize', fontSize, item );
 *				}
 *			}
 *		}, { priority: 'low' } );
 *
 *		// Convert all elements which have no custom converter into a paragraph (autoparagraphing).
 *		editor.data.upcastDispatcher.on( 'element', ( evt, data, conversionApi ) => {
 *			// Check if an element can be converted.
 *			if ( !conversionApi.consumable.test( data.viewItem, { name: data.viewItem.name } ) ) {
 *				// When an element is already consumed by higher priority converters, do nothing.
 *				return;
 *			}
 *
 *			const paragraph = conversionApi.writer.createElement( 'paragraph' );
 *
 *			// Try to safely insert a paragraph at the model cursor - it will find an allowed parent for the current element.
 *			if ( !conversionApi.safeInsert( paragraph, data.modelCursor ) ) {
 *				// When an element was not inserted, it means that you cannot insert a paragraph at this position.
 *				return;
 *			}
 *
 *			// Consume the inserted element.
 *			conversionApi.consumable.consume( data.viewItem, { name: data.viewItem.name } ) );
 *
 *			// Convert the children to a paragraph.
 *			const { modelRange } = conversionApi.convertChildren( data.viewItem,  paragraph ) );
 *
 *			// Update `modelRange` and `modelCursor` in the `data` as a conversion result.
 *			conversionApi.updateConversionResult( paragraph, data );
 *		}, { priority: 'low' } );
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 * @fires viewCleanup
 * @fires element
 * @fires text
 * @fires documentFragment
 */
class UpcastDispatcher extends Emitter {
    /**
     * Creates an upcast dispatcher that operates using the passed API.
     *
     * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi
     * @param {Object} [conversionApi] Additional properties for an interface that will be passed to events fired
     * by the upcast dispatcher.
     */
    constructor(conversionApi) {
        super();
        /**
         * The list of elements that were created during splitting.
         *
         * After the conversion process, the list is cleared.
         *
         * @private
         * @type {Map.<module:engine/model/element~Element,Array.<module:engine/model/element~Element>>}
         */
        this._splitParts = new Map();
        /**
         * The list of cursor parent elements that were created during splitting.
         *
         * After the conversion process the list is cleared.
         *
         * @private
         * @type {Map.<module:engine/model/element~Element,Array.<module:engine/model/element~Element>>}
         */
        this._cursorParents = new Map();
        /**
         * The position in the temporary structure where the converted content is inserted. The structure reflects the context of
         * the target position where the content will be inserted. This property is built based on the context parameter of the
         * convert method.
         *
         * @private
         * @type {module:engine/model/position~Position|null}
         */
        this._modelCursor = null;
        /**
         * The list of elements that were created during the splitting but should not get removed on conversion end even if they are empty.
         *
         * The list is cleared after the conversion process.
         *
         * @private
         * @type {Set.<module:engine/model/element~Element>}
         */
        this._emptyElementsToKeep = new Set();
        /**
         * An interface passed by the dispatcher to the event callbacks.
         *
         * @member {module:engine/conversion/upcastdispatcher~UpcastConversionApi}
         */
        this.conversionApi = {
            ...conversionApi,
            consumable: null,
            writer: null,
            store: null,
            convertItem: (viewItem, modelCursor) => this._convertItem(viewItem, modelCursor),
            convertChildren: (viewElement, positionOrElement) => this._convertChildren(viewElement, positionOrElement),
            safeInsert: (modelElement, position) => this._safeInsert(modelElement, position),
            updateConversionResult: (modelElement, data) => this._updateConversionResult(modelElement, data),
            // Advanced API - use only if custom position handling is needed.
            splitToAllowedParent: (modelElement, modelCursor) => this._splitToAllowedParent(modelElement, modelCursor),
            getSplitParts: modelElement => this._getSplitParts(modelElement),
            keepEmptyElement: modelElement => this._keepEmptyElement(modelElement)
        };
    }
    /**
     * Starts the conversion process. The entry point for the conversion.
     *
     * @fires element
     * @fires text
     * @fires documentFragment
     * @param {module:engine/view/documentfragment~DocumentFragment|module:engine/view/element~Element} viewElement
     * The part of the view to be converted.
     * @param {module:engine/model/writer~Writer} writer An instance of the model writer.
     * @param {module:engine/model/schema~SchemaContextDefinition} [context=['$root']] Elements will be converted according to this context.
     * @returns {module:engine/model/documentfragment~DocumentFragment} Model data that is the result of the conversion process
     * wrapped in `DocumentFragment`. Converted marker elements will be set as the document fragment's
     * {@link module:engine/model/documentfragment~DocumentFragment#markers static markers map}.
     */
    convert(viewElement, writer, context = ['$root']) {
        this.fire('viewCleanup', viewElement);
        // Create context tree and set position in the top element.
        // Items will be converted according to this position.
        this._modelCursor = createContextTree(context, writer);
        // Store writer in conversion as a conversion API
        // to be sure that conversion process will use the same batch.
        this.conversionApi.writer = writer;
        // Create consumable values list for conversion process.
        this.conversionApi.consumable = ViewConsumable.createFrom(viewElement);
        // Custom data stored by converter for conversion process.
        this.conversionApi.store = {};
        // Do the conversion.
        const { modelRange } = this._convertItem(viewElement, this._modelCursor);
        // Conversion result is always a document fragment so let's create it.
        const documentFragment = writer.createDocumentFragment();
        // When there is a conversion result.
        if (modelRange) {
            // Remove all empty elements that were create while splitting.
            this._removeEmptyElements();
            // Move all items that were converted in context tree to the document fragment.
            for (const item of Array.from(this._modelCursor.parent.getChildren())) {
                writer.append(item, documentFragment);
            }
            // Extract temporary markers elements from model and set as static markers collection.
            documentFragment.markers = extractMarkersFromModelFragment(documentFragment, writer);
        }
        // Clear context position.
        this._modelCursor = null;
        // Clear split elements & parents lists.
        this._splitParts.clear();
        this._cursorParents.clear();
        this._emptyElementsToKeep.clear();
        // Clear conversion API.
        this.conversionApi.writer = null;
        this.conversionApi.store = null;
        // Return fragment as conversion result.
        return documentFragment;
    }
    /**
     * @private
     * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#convertItem
     */
    _convertItem(viewItem, modelCursor) {
        const data = { viewItem, modelCursor, modelRange: null };
        if (viewItem.is('element')) {
            this.fire(`element:${viewItem.name}`, data, this.conversionApi);
        }
        else if (viewItem.is('$text')) {
            this.fire('text', data, this.conversionApi);
        }
        else {
            this.fire('documentFragment', data, this.conversionApi);
        }
        // Handle incorrect conversion result.
        if (data.modelRange && !(data.modelRange instanceof range_Range)) {
            /**
             * Incorrect conversion result was dropped.
             *
             * {@link module:engine/model/range~Range Model range} should be a conversion result.
             *
             * @error view-conversion-dispatcher-incorrect-result
             */
            throw new CKEditorError('view-conversion-dispatcher-incorrect-result', this);
        }
        return { modelRange: data.modelRange, modelCursor: data.modelCursor };
    }
    /**
     * @private
     * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#convertChildren
     */
    _convertChildren(viewItem, elementOrModelCursor) {
        let nextModelCursor = elementOrModelCursor.is('position') ?
            elementOrModelCursor : position_Position._createAt(elementOrModelCursor, 0);
        const modelRange = new range_Range(nextModelCursor);
        for (const viewChild of Array.from(viewItem.getChildren())) {
            const result = this._convertItem(viewChild, nextModelCursor);
            if (result.modelRange instanceof range_Range) {
                modelRange.end = result.modelRange.end;
                nextModelCursor = result.modelCursor;
            }
        }
        return { modelRange, modelCursor: nextModelCursor };
    }
    /**
     * @private
     * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#safeInsert
     */
    _safeInsert(modelElement, position) {
        // Find allowed parent for element that we are going to insert.
        // If current parent does not allow to insert element but one of the ancestors does
        // then split nodes to allowed parent.
        const splitResult = this._splitToAllowedParent(modelElement, position);
        // When there is no split result it means that we can't insert element to model tree, so let's skip it.
        if (!splitResult) {
            return false;
        }
        // Insert element on allowed position.
        this.conversionApi.writer.insert(modelElement, splitResult.position);
        return true;
    }
    /**
     * @private
     * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#updateConversionResult
     */
    _updateConversionResult(modelElement, data) {
        const parts = this._getSplitParts(modelElement);
        const writer = this.conversionApi.writer;
        // Set conversion result range - only if not set already.
        if (!data.modelRange) {
            data.modelRange = writer.createRange(writer.createPositionBefore(modelElement), writer.createPositionAfter(parts[parts.length - 1]));
        }
        const savedCursorParent = this._cursorParents.get(modelElement);
        // Now we need to check where the `modelCursor` should be.
        if (savedCursorParent) {
            // If we split parent to insert our element then we want to continue conversion in the new part of the split parent.
            //
            // before: <allowed><notAllowed>foo[]</notAllowed></allowed>
            // after:  <allowed><notAllowed>foo</notAllowed> <converted></converted> <notAllowed>[]</notAllowed></allowed>
            data.modelCursor = writer.createPositionAt(savedCursorParent, 0);
        }
        else {
            // Otherwise just continue after inserted element.
            data.modelCursor = data.modelRange.end;
        }
    }
    /**
     * @private
     * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#splitToAllowedParent
     */
    _splitToAllowedParent(node, modelCursor) {
        const { schema, writer } = this.conversionApi;
        // Try to find allowed parent.
        let allowedParent = schema.findAllowedParent(modelCursor, node);
        if (allowedParent) {
            // When current position parent allows to insert node then return this position.
            if (allowedParent === modelCursor.parent) {
                return { position: modelCursor };
            }
            // When allowed parent is in context tree (it's outside the converted tree).
            if (this._modelCursor.parent.getAncestors().includes(allowedParent)) {
                allowedParent = null;
            }
        }
        if (!allowedParent) {
            // Check if the node wrapped with a paragraph would be accepted by the schema.
            if (!isParagraphable(modelCursor, node, schema)) {
                return null;
            }
            return {
                position: wrapInParagraph(modelCursor, writer)
            };
        }
        // Split element to allowed parent.
        const splitResult = this.conversionApi.writer.split(modelCursor, allowedParent);
        // Using the range returned by `model.Writer#split`, we will pair original elements with their split parts.
        //
        // The range returned from the writer spans "over the split" or, precisely saying, from the end of the original element (the one
        // that got split) to the beginning of the other part of that element:
        //
        // <limit><a><b><c>X[]Y</c></b><a></limit> ->
        // <limit><a><b><c>X[</c></b></a><a><b><c>]Y</c></b></a>
        //
        // After the split there cannot be any full node between the positions in `splitRange`. The positions are touching.
        // Also, because of how splitting works, it is easy to notice, that "closing tags" are in the reverse order than "opening tags".
        // Also, since we split all those elements, each of them has to have the other part.
        //
        // With those observations in mind, we will pair the original elements with their split parts by saving "closing tags" and matching
        // them with "opening tags" in the reverse order. For that we can use a stack.
        const stack = [];
        for (const treeWalkerValue of splitResult.range.getWalker()) {
            if (treeWalkerValue.type == 'elementEnd') {
                stack.push(treeWalkerValue.item);
            }
            else {
                // There should not be any text nodes after the element is split, so the only other value is `elementStart`.
                const originalPart = stack.pop();
                const splitPart = treeWalkerValue.item;
                this._registerSplitPair(originalPart, splitPart);
            }
        }
        const cursorParent = splitResult.range.end.parent;
        this._cursorParents.set(node, cursorParent);
        return {
            position: splitResult.position,
            cursorParent
        };
    }
    /**
     * Registers that a `splitPart` element is a split part of the `originalPart` element.
     *
     * The data set by this method is used by {@link #_getSplitParts} and {@link #_removeEmptyElements}.
     *
     * @private
     * @param {module:engine/model/element~Element} originalPart
     * @param {module:engine/model/element~Element} splitPart
     */
    _registerSplitPair(originalPart, splitPart) {
        if (!this._splitParts.has(originalPart)) {
            this._splitParts.set(originalPart, [originalPart]);
        }
        const list = this._splitParts.get(originalPart);
        this._splitParts.set(splitPart, list);
        list.push(splitPart);
    }
    /**
     * @private
     * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#getSplitParts
     */
    _getSplitParts(element) {
        let parts;
        if (!this._splitParts.has(element)) {
            parts = [element];
        }
        else {
            parts = this._splitParts.get(element);
        }
        return parts;
    }
    /**
     * Mark an element that were created during the splitting to not get removed on conversion end even if it is empty.
     *
     * @private
     */
    _keepEmptyElement(element) {
        this._emptyElementsToKeep.add(element);
    }
    /**
     * Checks if there are any empty elements created while splitting and removes them.
     *
     * This method works recursively to re-check empty elements again after at least one element was removed in the initial call,
     * as some elements might have become empty after other empty elements were removed from them.
     *
     * @private
     */
    _removeEmptyElements() {
        let anyRemoved = false;
        for (const element of this._splitParts.keys()) {
            if (element.isEmpty && !this._emptyElementsToKeep.has(element)) {
                this.conversionApi.writer.remove(element);
                this._splitParts.delete(element);
                anyRemoved = true;
            }
        }
        if (anyRemoved) {
            this._removeEmptyElements();
        }
    }
}
// Traverses given model item and searches elements which marks marker range. Found element is removed from
// DocumentFragment but path of this element is stored in a Map which is then returned.
//
// @param {module:engine/view/documentfragment~DocumentFragment|module:engine/view/node~Node} modelItem Fragment of model.
// @returns {Map<String, module:engine/model/range~Range>} List of static markers.
function extractMarkersFromModelFragment(modelItem, writer) {
    const markerElements = new Set();
    const markers = new Map();
    // Create ModelTreeWalker.
    const range = range_Range._createIn(modelItem).getItems();
    // Walk through DocumentFragment and collect marker elements.
    for (const item of range) {
        // Check if current element is a marker.
        if (item.is('element', '$marker')) {
            markerElements.add(item);
        }
    }
    // Walk through collected marker elements store its path and remove its from the DocumentFragment.
    for (const markerElement of markerElements) {
        const markerName = markerElement.getAttribute('data-name');
        const currentPosition = writer.createPositionBefore(markerElement);
        // When marker of given name is not stored it means that we have found the beginning of the range.
        if (!markers.has(markerName)) {
            markers.set(markerName, new range_Range(currentPosition.clone()));
            // Otherwise is means that we have found end of the marker range.
        }
        else {
            markers.get(markerName).end = currentPosition.clone();
        }
        // Remove marker element from DocumentFragment.
        writer.remove(markerElement);
    }
    return markers;
}
// Creates model fragment according to given context and returns position in the bottom (the deepest) element.
function createContextTree(contextDefinition, writer) {
    let position;
    for (const item of new SchemaContext(contextDefinition)) {
        const attributes = {};
        for (const key of item.getAttributeKeys()) {
            attributes[key] = item.getAttribute(key);
        }
        const current = writer.createElement(item.name, attributes);
        if (position) {
            writer.insert(current, position);
        }
        position = position_Position._createAt(current, 0);
    }
    return position;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/dataprocessor/basichtmlwriter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/dataprocessor/basichtmlwriter
 */
/* globals document */
/**
 * Basic HTML writer. It uses the native `innerHTML` property for basic conversion
 * from a document fragment to an HTML string.
 *
 * @implements module:engine/dataprocessor/htmlwriter~HtmlWriter
 */
class BasicHtmlWriter {
    /**
     * Returns an HTML string created from the document fragment.
     *
     * @param {DocumentFragment} fragment
     * @returns {String}
     */
    getHtml(fragment) {
        const doc = document.implementation.createHTMLDocument('');
        const container = doc.createElement('div');
        container.appendChild(fragment);
        return container.innerHTML;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/dataprocessor/htmldataprocessor
 */
/* globals DOMParser */


/**
 * The HTML data processor class.
 * This data processor implementation uses HTML as input and output data.
 *
 * @implements module:engine/dataprocessor/dataprocessor~DataProcessor
 */
class HtmlDataProcessor {
    /**
     * Creates a new instance of the HTML data processor class.
     *
     * @param {module:engine/view/document~Document} document The view document instance.
     */
    constructor(document) {
        /**
         * A DOM parser instance used to parse an HTML string to an HTML document.
         *
         * @member {DOMParser}
         */
        this.domParser = new DOMParser();
        /**
         * A DOM converter used to convert DOM elements to view elements.
         *
         * @member {module:engine/view/domconverter~DomConverter}
         */
        this.domConverter = new DomConverter(document, { renderingMode: 'data' });
        /**
         * A basic HTML writer instance used to convert DOM elements to an HTML string.
         *
         * @member {module:engine/dataprocessor/htmlwriter~HtmlWriter}
         */
        this.htmlWriter = new BasicHtmlWriter();
    }
    /**
     * Converts a provided {@link module:engine/view/documentfragment~DocumentFragment document fragment}
     * to data format &mdash; in this case to an HTML string.
     *
     * @param {module:engine/view/documentfragment~DocumentFragment} viewFragment
     * @returns {String} HTML string.
     */
    toData(viewFragment) {
        // Convert view DocumentFragment to DOM DocumentFragment.
        const domFragment = this.domConverter.viewToDom(viewFragment);
        // Convert DOM DocumentFragment to HTML output.
        return this.htmlWriter.getHtml(domFragment);
    }
    /**
     * Converts the provided HTML string to a view tree.
     *
     * @param {String} data An HTML string.
     * @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment|null} A converted view element.
     */
    toView(data) {
        // Convert input HTML data to DOM DocumentFragment.
        const domFragment = this._toDom(data);
        // Convert DOM DocumentFragment to view DocumentFragment.
        return this.domConverter.domToView(domFragment);
    }
    /**
     * Registers a {@link module:engine/view/matcher~MatcherPattern} for view elements whose content should be treated as raw data
     * and not processed during the conversion from the DOM to the view elements.
     *
     * The raw data can be later accessed by a
     * {@link module:engine/view/element~Element#getCustomProperty custom property of a view element} called `"$rawContent"`.
     *
     * @param {module:engine/view/matcher~MatcherPattern} pattern Pattern matching all view elements whose content should
     * be treated as raw data.
     */
    registerRawContentMatcher(pattern) {
        this.domConverter.registerRawContentMatcher(pattern);
    }
    /**
     * If the processor is set to use marked fillers, it will insert `&nbsp;` fillers wrapped in `<span>` elements
     * (`<span data-cke-filler="true">&nbsp;</span>`) instead of regular `&nbsp;` characters.
     *
     * This mode allows for a more precise handling of the block fillers (so they do not leak into the editor content) but
     * bloats the editor data with additional markup.
     *
     * This mode may be required by some features and will be turned on by them automatically.
     *
     * @param {'default'|'marked'} type Whether to use the default or the marked `&nbsp;` block fillers.
     */
    useFillerType(type) {
        this.domConverter.blockFillerMode = type == 'marked' ? 'markedNbsp' : 'nbsp';
    }
    /**
     * Converts an HTML string to its DOM representation. Returns a document fragment containing nodes parsed from
     * the provided data.
     *
     * @private
     * @param {String} data
     * @returns {DocumentFragment}
     */
    _toDom(data) {
        // Wrap data with a <body> tag so leading non-layout nodes (like <script>, <style>, HTML comment)
        // will be preserved in the body collection.
        // Do it only for data that is not a full HTML document.
        if (!data.match(/<(?:html|body|head|meta)(?:\s[^>]*)?>/i)) {
            data = `<body>${data}</body>`;
        }
        const document = this.domParser.parseFromString(data, 'text/html');
        const fragment = document.createDocumentFragment();
        const bodyChildNodes = document.body.childNodes;
        while (bodyChildNodes.length > 0) {
            fragment.appendChild(bodyChildNodes[0]);
        }
        return fragment;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/controller/datacontroller.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/controller/datacontroller
 */














/**
 * Controller for the data pipeline. The data pipeline controls how data is retrieved from the document
 * and set inside it. Hence, the controller features two methods which allow to {@link ~DataController#get get}
 * and {@link ~DataController#set set} data of the {@link ~DataController#model model}
 * using the given:
 *
 * * {@link module:engine/dataprocessor/dataprocessor~DataProcessor data processor},
 * * downcast converters,
 * * upcast converters.
 *
 * An instance of the data controller is always available in the {@link module:core/editor/editor~Editor#data `editor.data`}
 * property:
 *
 *		editor.data.get( { rootName: 'customRoot' } ); // -> '<p>Hello!</p>'
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class DataController extends Emitter {
    /**
     * Creates a data controller instance.
     *
     * @param {module:engine/model/model~Model} model Data model.
     * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor The styles processor instance.
     */
    constructor(model, stylesProcessor) {
        super();
        /**
         * Data model.
         *
         * @readonly
         * @member {module:engine/model/model~Model}
         */
        this.model = model;
        /**
         * Mapper used for the conversion. It has no permanent bindings, because these are created while getting data and
         * ae cleared directly after the data are converted. However, the mapper is defined as a class property, because
         * it needs to be passed to the `DowncastDispatcher` as a conversion API.
         *
         * @readonly
         * @member {module:engine/conversion/mapper~Mapper}
         */
        this.mapper = new Mapper();
        /**
         * Downcast dispatcher used by the {@link #get get method}. Downcast converters should be attached to it.
         *
         * @readonly
         * @member {module:engine/conversion/downcastdispatcher~DowncastDispatcher}
         */
        this.downcastDispatcher = new DowncastDispatcher({
            mapper: this.mapper,
            schema: model.schema
        });
        this.downcastDispatcher.on('insert:$text', insertText(), { priority: 'lowest' });
        this.downcastDispatcher.on('insert', insertAttributesAndChildren(), { priority: 'lowest' });
        /**
         * Upcast dispatcher used by the {@link #set set method}. Upcast converters should be attached to it.
         *
         * @readonly
         * @member {module:engine/conversion/upcastdispatcher~UpcastDispatcher}
         */
        this.upcastDispatcher = new UpcastDispatcher({
            schema: model.schema
        });
        /**
         * The view document used by the data controller.
         *
         * @readonly
         * @member {module:engine/view/document~Document}
         */
        this.viewDocument = new Document(stylesProcessor);
        /**
         * Styles processor used during the conversion.
         *
         * @readonly
         * @member {module:engine/view/stylesmap~StylesProcessor}
         */
        this.stylesProcessor = stylesProcessor;
        /**
         * Data processor used specifically for HTML conversion.
         *
         * @readonly
         * @member {module:engine/dataprocessor/htmldataprocessor~HtmlDataProcessor} #htmlProcessor
         */
        this.htmlProcessor = new HtmlDataProcessor(this.viewDocument);
        /**
         * Data processor used during the conversion.
         * Same instance as {@link #htmlProcessor} by default. Can be replaced at run time to handle different format, e.g. XML or Markdown.
         *
         * @member {module:engine/dataprocessor/dataprocessor~DataProcessor} #processor
         */
        this.processor = this.htmlProcessor;
        /**
         * The view downcast writer just for data conversion purposes, i.e. to modify
         * the {@link #viewDocument}.
         *
         * @private
         * @readonly
         * @member {module:engine/view/downcastwriter~DowncastWriter}
         */
        this._viewWriter = new DowncastWriter(this.viewDocument);
        // Define default converters for text and elements.
        //
        // Note that if there is no default converter for the element it will be skipped, for instance `<b>foo</b>` will be
        // converted to nothing. We therefore add `convertToModelFragment` as a last converter so it converts children of that
        // element to the document fragment so `<b>foo</b>` will still be converted to `foo` even if there is no converter for `<b>`.
        this.upcastDispatcher.on('text', convertText(), { priority: 'lowest' });
        this.upcastDispatcher.on('element', convertToModelFragment(), { priority: 'lowest' });
        this.upcastDispatcher.on('documentFragment', convertToModelFragment(), { priority: 'lowest' });
        Observable.prototype.decorate.call(this, 'init');
        Observable.prototype.decorate.call(this, 'set');
        Observable.prototype.decorate.call(this, 'get');
        // Fire the `ready` event when the initialization has completed. Such low-level listener offers the possibility
        // to plug into the initialization pipeline without interrupting the initialization flow.
        this.on('init', () => {
            this.fire('ready');
        }, { priority: 'lowest' });
        // Fix empty roots after DataController is 'ready' (note that the init method could be decorated and stopped).
        // We need to handle this event because initial data could be empty and the post-fixer would not get triggered.
        this.on('ready', () => {
            this.model.enqueueChange({ isUndoable: false }, autoParagraphEmptyRoots);
        }, { priority: 'lowest' });
    }
    /**
     * Returns the model's data converted by downcast dispatchers attached to {@link #downcastDispatcher} and
     * formatted by the {@link #processor data processor}.
     *
     * @fires get
     * @param {Object} [options] Additional configuration for the retrieved data. `DataController` provides two optional
     * properties: `rootName` and `trim`. Other properties of this object are specified by various editor features.
     * @param {String} [options.rootName='main'] Root name.
     * @param {String} [options.trim='empty'] Whether returned data should be trimmed. This option is set to `empty` by default,
     * which means whenever editor content is considered empty, an empty string will be returned. To turn off trimming completely
     * use `'none'`. In such cases the exact content will be returned (for example a `<p>&nbsp;</p>` for an empty editor).
     * @returns {String} Output data.
     */
    get(options = {}) {
        const { rootName = 'main', trim = 'empty' } = options;
        if (!this._checkIfRootsExists([rootName])) {
            /**
             * Cannot get data from a non-existing root. This error is thrown when {@link #get DataController#get() method}
             * is called with a non-existent root name. For example, if there is an editor instance with only `main` root,
             * calling {@link #get} like:
             *
             *		data.get( { rootName: 'root2' } );
             *
             * will throw this error.
             *
             * @error datacontroller-get-non-existent-root
             */
            throw new CKEditorError('datacontroller-get-non-existent-root', this);
        }
        const root = this.model.document.getRoot(rootName);
        if (trim === 'empty' && !this.model.hasContent(root, { ignoreWhitespaces: true })) {
            return '';
        }
        return this.stringify(root, options);
    }
    /**
     * Returns the content of the given {@link module:engine/model/element~Element model's element} or
     * {@link module:engine/model/documentfragment~DocumentFragment model document fragment} converted by the downcast converters
     * attached to the {@link #downcastDispatcher} and formatted by the {@link #processor data processor}.
     *
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
     * The element whose content will be stringified.
     * @param {Object} [options] Additional configuration passed to the conversion process.
     * @returns {String} Output data.
     */
    stringify(modelElementOrFragment, options = {}) {
        // Model -> view.
        const viewDocumentFragment = this.toView(modelElementOrFragment, options);
        // View -> data.
        return this.processor.toData(viewDocumentFragment);
    }
    /**
     * Returns the content of the given {@link module:engine/model/element~Element model element} or
     * {@link module:engine/model/documentfragment~DocumentFragment model document fragment} converted by the downcast
     * converters attached to {@link #downcastDispatcher} into a
     * {@link module:engine/view/documentfragment~DocumentFragment view document fragment}.
     *
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
     * Element or document fragment whose content will be converted.
     * @param {Object} [options={}] Additional configuration that will be available through the
     * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi#options} during the conversion process.
     * @returns {module:engine/view/documentfragment~DocumentFragment} Output view DocumentFragment.
     */
    toView(modelElementOrFragment, options = {}) {
        const viewDocument = this.viewDocument;
        const viewWriter = this._viewWriter;
        // Clear bindings so the call to this method returns correct results.
        this.mapper.clearBindings();
        // First, convert elements.
        const modelRange = range_Range._createIn(modelElementOrFragment);
        const viewDocumentFragment = new DocumentFragment(viewDocument);
        this.mapper.bindElements(modelElementOrFragment, viewDocumentFragment);
        // Prepare list of markers.
        // For document fragment, simply take the markers assigned to this document fragment.
        // For model root, all markers in that root will be taken.
        // For model element, we need to check which markers are intersecting with this element and relatively modify the markers' ranges.
        // Collapsed markers at element boundary, although considered as not intersecting with the element, will also be returned.
        const markers = modelElementOrFragment.is('documentFragment') ?
            modelElementOrFragment.markers :
            _getMarkersRelativeToElement(modelElementOrFragment);
        this.downcastDispatcher.convert(modelRange, markers, viewWriter, options);
        return viewDocumentFragment;
    }
    /**
     * Sets the initial input data parsed by the {@link #processor data processor} and
     * converted by the {@link #upcastDispatcher view-to-model converters}.
     * Initial data can be only set to a document whose {@link module:engine/model/document~Document#version} is equal 0.
     *
     * **Note** This method is {@link module:utils/observablemixin~ObservableMixin#decorate decorated} which is
     * used by e.g. collaborative editing plugin that syncs remote data on init.
     *
     * When data is passed as a string, it is initialized on the default `main` root:
     *
     *		dataController.init( '<p>Foo</p>' ); // Initializes data on the `main` root only, as no other is specified.
     *
     * To initialize data on a different root or multiple roots at once, an object containing `rootName` - `data` pairs should be passed:
     *
     *		dataController.init( { main: '<p>Foo</p>', title: '<h1>Bar</h1>' } ); // Initializes data on both the `main` and `title` roots.
     *
     * @fires init
     * @param {String|Object.<String,String>} data Input data as a string or an object containing the `rootName` - `data`
     * pairs to initialize data on multiple roots at once.
     * @returns {Promise} Promise that is resolved after the data is set on the editor.
     */
    init(data) {
        if (this.model.document.version) {
            /**
             * Cannot set initial data to a non-empty {@link module:engine/model/document~Document}.
             * Initial data should be set once, during the {@link module:core/editor/editor~Editor} initialization,
             * when the {@link module:engine/model/document~Document#version} is equal 0.
             *
             * @error datacontroller-init-document-not-empty
             */
            throw new CKEditorError('datacontroller-init-document-not-empty', this);
        }
        let initialData = {};
        if (typeof data === 'string') {
            initialData.main = data; // Default root is 'main'. To initiate data on a different root, object should be passed.
        }
        else {
            initialData = data;
        }
        if (!this._checkIfRootsExists(Object.keys(initialData))) {
            /**
             * Cannot init data on a non-existent root. This error is thrown when {@link #init DataController#init() method}
             * is called with non-existent root name. For example, if there is an editor instance with only `main` root,
             * calling {@link #init} like:
             *
             * 		data.init( { main: '<p>Foo</p>', root2: '<p>Bar</p>' } );
             *
             * will throw this error.
             *
             * @error datacontroller-init-non-existent-root
             */
            throw new CKEditorError('datacontroller-init-non-existent-root', this);
        }
        this.model.enqueueChange({ isUndoable: false }, writer => {
            for (const rootName of Object.keys(initialData)) {
                const modelRoot = this.model.document.getRoot(rootName);
                writer.insert(this.parse(initialData[rootName], modelRoot), modelRoot, 0);
            }
        });
        return Promise.resolve();
    }
    /**
     * Sets the input data parsed by the {@link #processor data processor} and
     * converted by the {@link #upcastDispatcher view-to-model converters}.
     * This method can be used any time to replace existing editor data with the new one without clearing the
     * {@link module:engine/model/document~Document#history document history}.
     *
     * This method also creates a batch with all the changes applied. If all you need is to parse data, use
     * the {@link #parse} method.
     *
     * When data is passed as a string it is set on the default `main` root:
     *
     *		dataController.set( '<p>Foo</p>' ); // Sets data on the `main` root, as no other is specified.
     *
     * To set data on a different root or multiple roots at once, an object containing `rootName` - `data` pairs should be passed:
     *
     *		dataController.set( { main: '<p>Foo</p>', title: '<h1>Bar</h1>' } ); // Sets data on the `main` and `title` roots as specified.
     *
     * To set the data with a preserved undo stack and add the change to the undo stack, set `{ isUndoable: true }` as a `batchType` option.
     *
     *		dataController.set( '<p>Foo</p>', { batchType: { isUndoable: true } } );
     *
     * @fires set
     * @param {String|Object.<String,String>} data Input data as a string or an object containing the `rootName` - `data`
     * pairs to set data on multiple roots at once.
     * @param {Object} [options={}] Options for setting data.
     * @param {Object} [options.batchType] The batch type that will be used to create a batch for the changes applied by this method.
     * By default, the batch will be set as {@link module:engine/model/batch~Batch#isUndoable not undoable} and the undo stack will be
     * cleared after the new data is applied (all undo steps will be removed). If the batch type `isUndoable` flag is be set to `true`,
     * the undo stack will be preserved instead and not cleared when new data is applied.
     */
    set(data, options = {}) {
        let newData = {};
        if (typeof data === 'string') {
            newData.main = data; // The default root is 'main'. To set data on a different root, an object should be passed.
        }
        else {
            newData = data;
        }
        if (!this._checkIfRootsExists(Object.keys(newData))) {
            /**
             * Cannot set data on a non-existent root. This error is thrown when the {@link #set DataController#set() method}
             * is called with non-existent root name. For example, if there is an editor instance with only the default `main` root,
             * calling {@link #set} like:
             *
             * 		data.set( { main: '<p>Foo</p>', root2: '<p>Bar</p>' } );
             *
             * will throw this error.
             *
             * @error datacontroller-set-non-existent-root
             */
            throw new CKEditorError('datacontroller-set-non-existent-root', this);
        }
        this.model.enqueueChange(options.batchType || {}, writer => {
            writer.setSelection(null);
            writer.removeSelectionAttribute(this.model.document.selection.getAttributeKeys());
            for (const rootName of Object.keys(newData)) {
                // Save to model.
                const modelRoot = this.model.document.getRoot(rootName);
                writer.remove(writer.createRangeIn(modelRoot));
                writer.insert(this.parse(newData[rootName], modelRoot), modelRoot, 0);
            }
        });
    }
    /**
     * Returns the data parsed by the {@link #processor data processor} and then converted by upcast converters
     * attached to the {@link #upcastDispatcher}.
     *
     * @see #set
     * @param {String} data Data to parse.
     * @param {module:engine/model/schema~SchemaContextDefinition} [context='$root'] Base context in which the view will
     * be converted to the model. See: {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#convert}.
     * @returns {module:engine/model/documentfragment~DocumentFragment} Parsed data.
     */
    parse(data, context = '$root') {
        // data -> view
        const viewDocumentFragment = this.processor.toView(data);
        // view -> model
        return this.toModel(viewDocumentFragment, context);
    }
    /**
     * Returns the result of the given {@link module:engine/view/element~Element view element} or
     * {@link module:engine/view/documentfragment~DocumentFragment view document fragment} converted by the
     * {@link #upcastDispatcher view-to-model converters}, wrapped by {@link module:engine/model/documentfragment~DocumentFragment}.
     *
     * When marker elements were converted during the conversion process, it will be set as a document fragment's
     * {@link module:engine/model/documentfragment~DocumentFragment#markers static markers map}.
     *
     * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} viewElementOrFragment
     * The element or document fragment whose content will be converted.
     * @param {module:engine/model/schema~SchemaContextDefinition} [context='$root'] Base context in which the view will
     * be converted to the model. See: {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#convert}.
     * @returns {module:engine/model/documentfragment~DocumentFragment} Output document fragment.
     */
    toModel(viewElementOrFragment, context = '$root') {
        return this.model.change(writer => {
            return this.upcastDispatcher.convert(viewElementOrFragment, writer, context);
        });
    }
    /**
     * Adds the style processor normalization rules.
     *
     * You can implement your own rules as well as use one of the available processor rules:
     *
     * * background: {@link module:engine/view/styles/background~addBackgroundRules}
     * * border: {@link module:engine/view/styles/border~addBorderRules}
     * * margin: {@link module:engine/view/styles/margin~addMarginRules}
     * * padding: {@link module:engine/view/styles/padding~addPaddingRules}
     *
     * @param {Function} callback
     */
    addStyleProcessorRules(callback) {
        callback(this.stylesProcessor);
    }
    /**
     * Registers a {@link module:engine/view/matcher~MatcherPattern} on an {@link #htmlProcessor htmlProcessor}
     * and a {@link #processor processor} for view elements whose content should be treated as raw data
     * and not processed during the conversion from DOM to view elements.
     *
     * The raw data can be later accessed by the {@link module:engine/view/element~Element#getCustomProperty view element custom property}
     * `"$rawContent"`.
     *
     * @param {module:engine/view/matcher~MatcherPattern} pattern Pattern matching all view elements whose content should
     * be treated as a raw data.
     */
    registerRawContentMatcher(pattern) {
        // No need to register the pattern if both the `htmlProcessor` and `processor` are the same instances.
        if (this.processor && this.processor !== this.htmlProcessor) {
            this.processor.registerRawContentMatcher(pattern);
        }
        this.htmlProcessor.registerRawContentMatcher(pattern);
    }
    /**
     * Removes all event listeners set by the DataController.
     */
    destroy() {
        this.stopListening();
    }
    /**
     * Checks whether all provided root names are actually existing editor roots.
     *
     * @private
     * @param {Array.<String>} rootNames Root names to check.
     * @returns {Boolean} Whether all provided root names are existing editor roots.
     */
    _checkIfRootsExists(rootNames) {
        for (const rootName of rootNames) {
            if (!this.model.document.getRootNames().includes(rootName)) {
                return false;
            }
        }
        return true;
    }
}
// Helper function for downcast conversion.
//
// Takes a document element (element that is added to a model document) and checks which markers are inside it. If the marker is collapsed
// at element boundary, it is considered as contained inside the element and marker range is returned. Otherwise, if the marker is
// intersecting with the element, the intersection is returned.
function _getMarkersRelativeToElement(element) {
    const result = [];
    const doc = element.root.document;
    if (!doc) {
        return new Map();
    }
    const elementRange = range_Range._createIn(element);
    for (const marker of doc.model.markers) {
        const markerRange = marker.getRange();
        const isMarkerCollapsed = markerRange.isCollapsed;
        const isMarkerAtElementBoundary = markerRange.start.isEqual(elementRange.start) || markerRange.end.isEqual(elementRange.end);
        if (isMarkerCollapsed && isMarkerAtElementBoundary) {
            result.push([marker.name, markerRange]);
        }
        else {
            const updatedMarkerRange = elementRange.getIntersection(markerRange);
            if (updatedMarkerRange) {
                result.push([marker.name, updatedMarkerRange]);
            }
        }
    }
    // Sort the markers in a stable fashion to ensure that the order in which they are
    // added to the model's marker collection does not affect how they are
    // downcast. One particular use case that we are targeting here, is one where
    // two markers are adjacent but not overlapping, such as an insertion/deletion
    // suggestion pair representing the replacement of a range of text. In this
    // case, putting the markers in DOM order causes the first marker's end to be
    // serialized right after the second marker's start, while putting the markers
    // in reverse DOM order causes it to be right before the second marker's
    // start. So, we sort these in a way that ensures non-intersecting ranges are in
    // reverse DOM order, and intersecting ranges are in something approximating
    // reverse DOM order (since reverse DOM order doesn't have a precise meaning
    // when working with intersecting ranges).
    result.sort(([n1, r1], [n2, r2]) => {
        if (r1.end.compareWith(r2.start) !== 'after') {
            // m1.end <= m2.start -- m1 is entirely <= m2
            return 1;
        }
        else if (r1.start.compareWith(r2.end) !== 'before') {
            // m1.start >= m2.end -- m1 is entirely >= m2
            return -1;
        }
        else {
            // they overlap, so use their start positions as the primary sort key and
            // end positions as the secondary sort key
            switch (r1.start.compareWith(r2.start)) {
                case 'before':
                    return 1;
                case 'after':
                    return -1;
                default:
                    switch (r1.end.compareWith(r2.end)) {
                        case 'before':
                            return 1;
                        case 'after':
                            return -1;
                        default:
                            return n2.localeCompare(n1);
                    }
            }
        }
    });
    return new Map(result);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/conversion/conversion.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/conversion/conversion
 */




/**
 * A utility class that helps add converters to upcast and downcast dispatchers.
 *
 * We recommend reading the {@glink framework/guides/deep-dive/conversion/intro editor conversion} guide first to
 * understand the core concepts of the conversion mechanisms.
 *
 * An instance of the conversion manager is available in the
 * {@link module:core/editor/editor~Editor#conversion `editor.conversion`} property
 * and by default has the following groups of dispatchers (i.e. directions of conversion):
 *
 * * `downcast` (editing and data downcasts)
 * * `editingDowncast`
 * * `dataDowncast`
 * * `upcast`
 *
 * # One-way converters
 *
 * To add a converter to a specific group, use the {@link module:engine/conversion/conversion~Conversion#for `for()`}
 * method:
 *
 *		// Add a converter to editing downcast and data downcast.
 *		editor.conversion.for( 'downcast' ).elementToElement( config ) );
 *
 *		// Add a converter to the data pipepline only:
 *		editor.conversion.for( 'dataDowncast' ).elementToElement( dataConversionConfig ) );
 *
 *		// And a slightly different one for the editing pipeline:
 *		editor.conversion.for( 'editingDowncast' ).elementToElement( editingConversionConfig ) );
 *
 * See {@link module:engine/conversion/conversion~Conversion#for `for()`} method documentation to learn more about
 * available conversion helpers and how to use your custom ones.
 *
 * # Two-way converters
 *
 * Besides using one-way converters via the `for()` method, you can also use other methods available in this
 * class to add two-way converters (upcast and downcast):
 *
 * * {@link module:engine/conversion/conversion~Conversion#elementToElement `elementToElement()`} &ndash;
 * Model element to view element and vice versa.
 * * {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement()`} &ndash;
 * Model attribute to view element and vice versa.
 * * {@link module:engine/conversion/conversion~Conversion#attributeToAttribute `attributeToAttribute()`} &ndash;
 * Model attribute to view attribute and vice versa.
 */
class Conversion {
    /**
     * Creates a new conversion instance.
     *
     * @param {module:engine/conversion/downcastdispatcher~DowncastDispatcher|
     * Array.<module:engine/conversion/downcastdispatcher~DowncastDispatcher>} downcastDispatchers
     * @param {module:engine/conversion/upcastdispatcher~UpcastDispatcher|
     * Array.<module:engine/conversion/upcastdispatcher~UpcastDispatcher>} upcastDispatchers
     */
    constructor(downcastDispatchers, upcastDispatchers) {
        /**
         * Maps dispatchers group name to ConversionHelpers instances.
         *
         * @private
         * @member {Map.<String,module:engine/conversion/conversionhelpers~ConversionHelpers>}
         */
        this._helpers = new Map();
        // Define default 'downcast' & 'upcast' dispatchers groups. Those groups are always available as two-way converters needs them.
        this._downcast = toArray(downcastDispatchers);
        this._createConversionHelpers({ name: 'downcast', dispatchers: this._downcast, isDowncast: true });
        this._upcast = toArray(upcastDispatchers);
        this._createConversionHelpers({ name: 'upcast', dispatchers: this._upcast, isDowncast: false });
    }
    /**
     * Define an alias for registered dispatcher.
     *
     *		const conversion = new Conversion(
     *			[ dataDowncastDispatcher, editingDowncastDispatcher ],
     *			upcastDispatcher
     *		);
     *
     *		conversion.addAlias( 'dataDowncast', dataDowncastDispatcher );
     *
     * @param {String} alias An alias of a dispatcher.
     * @param {module:engine/conversion/downcastdispatcher~DowncastDispatcher|
     * module:engine/conversion/upcastdispatcher~UpcastDispatcher} dispatcher Dispatcher which should have an alias.
     */
    addAlias(alias, dispatcher) {
        const isDowncast = this._downcast.includes(dispatcher);
        const isUpcast = this._upcast.includes(dispatcher);
        if (!isUpcast && !isDowncast) {
            /**
             * Trying to register an alias for a dispatcher that nas not been registered.
             *
             * @error conversion-add-alias-dispatcher-not-registered
             */
            throw new CKEditorError('conversion-add-alias-dispatcher-not-registered', this);
        }
        this._createConversionHelpers({ name: alias, dispatchers: [dispatcher], isDowncast });
    }
    /**
     * Provides a chainable API to assign converters to a conversion dispatchers group.
     *
     * If the given group name has not been registered, the
     * {@link module:utils/ckeditorerror~CKEditorError `conversion-for-unknown-group` error} is thrown.
     *
     * You can use conversion helpers available directly in the `for()` chain or your custom ones via
     * the {@link module:engine/conversion/conversionhelpers~ConversionHelpers#add `add()`} method.
     *
     * # Using built-in conversion helpers
     *
     * The `for()` chain comes with a set of conversion helpers which you can use like this:
     *
     *		editor.conversion.for( 'downcast' )
     *			.elementToElement( config1 )        // Adds an element-to-element downcast converter.
     *			.attributeToElement( config2 );     // Adds an attribute-to-element downcast converter.
     *
     *		editor.conversion.for( 'upcast' )
     *			.elementToAttribute( config3 );     // Adds an element-to-attribute upcast converter.
     *
     * Refer to the documentation of built-in conversion helpers to learn about their configuration options.
     *
     * * downcast (model-to-view) conversion helpers:
     *
     *	* {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement `elementToElement()`},
     *	* {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement `attributeToElement()`},
     *	* {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToAttribute `attributeToAttribute()`}.
     *	* {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToElement `markerToElement()`}.
     *	* {@link module:engine/conversion/downcasthelpers~DowncastHelpers#markerToHighlight `markerToHighlight()`}.
     *
     * * upcast (view-to-model) conversion helpers:
     *
     *	* {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToElement `elementToElement()`},
     *	* {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToAttribute `elementToAttribute()`},
     *	* {@link module:engine/conversion/upcasthelpers~UpcastHelpers#attributeToAttribute `attributeToAttribute()`}.
     *	* {@link module:engine/conversion/upcasthelpers~UpcastHelpers#elementToMarker `elementToMarker()`}.
     *
     * # Using custom conversion helpers
     *
     * If you need to implement an atypical converter, you can do so by calling:
     *
     *		editor.conversion.for( direction ).add( customHelper );
     *
     * The `.add()` method takes exactly one parameter, which is a function. This function should accept one parameter that
     * is a dispatcher instance. The function should add an actual converter to the passed dispatcher instance.
     *
     * Example:
     *
     *		editor.conversion.for( 'upcast' ).add( dispatcher => {
     *			dispatcher.on( 'element:a',  ( evt, data, conversionApi ) => {
     *				// Do something with a view <a> element.
     *			} );
     *		} );
     *
     * Refer to the documentation of {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher}
     * and {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} to learn how to write
     * custom converters.
     *
     * @param {String} groupName The name of dispatchers group to add the converters to.
     * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers|module:engine/conversion/upcasthelpers~UpcastHelpers}
     */
    for(groupName) {
        if (!this._helpers.has(groupName)) {
            /**
             * Trying to add a converter to an unknown dispatchers group.
             *
             * @error conversion-for-unknown-group
             */
            throw new CKEditorError('conversion-for-unknown-group', this);
        }
        return this._helpers.get(groupName);
    }
    /**
     * Sets up converters between the model and the view that convert a model element to a view element (and vice versa).
     * For example, the model `<paragraph>Foo</paragraph>` is turned into `<p>Foo</p>` in the view.
     *
     *		// A simple conversion from the `paragraph` model element to the `<p>` view element (and vice versa).
     *		editor.conversion.elementToElement( { model: 'paragraph', view: 'p' } );
     *
     *		// Override other converters by specifying a converter definition with a higher priority.
     *		editor.conversion.elementToElement( { model: 'paragraph', view: 'div', converterPriority: 'high' } );
     *
     *		// View specified as an object instead of a string.
     *		editor.conversion.elementToElement( {
     *			model: 'fancyParagraph',
     *			view: {
     *				name: 'p',
     *				classes: 'fancy'
     *			}
     *		} );
     *
     *		// Use `upcastAlso` to define other view elements that should also be converted to a `paragraph` element.
     *		editor.conversion.elementToElement( {
     *			model: 'paragraph',
     *			view: 'p',
     *			upcastAlso: [
     *				'div',
     *				{
     *					// Any element with the `display: block` style.
     *					styles: {
     *						display: 'block'
     *					}
     *				}
     *			]
     *		} );
     *
     *		// `upcastAlso` set as callback enables a conversion of a wide range of different view elements.
     *		editor.conversion.elementToElement( {
     *			model: 'heading',
     *			view: 'h2',
     *			// Convert "heading-like" paragraphs to headings.
     *			upcastAlso: viewElement => {
     *				const fontSize = viewElement.getStyle( 'font-size' );
     *
     *				if ( !fontSize ) {
     *					return null;
     *				}
     *
     *				const match = fontSize.match( /(\d+)\s*px/ );
     *
     *				if ( !match ) {
     *					return null;
     *				}
     *
     *				const size = Number( match[ 1 ] );
     *
     *				if ( size > 26 ) {
     *					// Returned value can be an object with the matched properties.
     *					// These properties will be "consumed" during the conversion.
     *					// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.
     *
     *					return { name: true, styles: [ 'font-size' ] };
     *				}
     *
     *				return null;
     *			}
     *		} );
     *
     * `definition.model` is a `String` with a model element name to convert from or to.
     * See {@link module:engine/conversion/conversion~ConverterDefinition} to learn about other parameters.
     *
     * @param {module:engine/conversion/conversion~ConverterDefinition} definition The converter definition.
     */
    elementToElement(definition) {
        // Set up downcast converter.
        this.for('downcast').elementToElement(definition);
        // Set up upcast converter.
        for (const { model, view } of _getAllUpcastDefinitions(definition)) {
            this.for('upcast')
                .elementToElement({
                model,
                view,
                converterPriority: definition.converterPriority
            });
        }
    }
    /**
     * Sets up converters between the model and the view that convert a model attribute to a view element (and vice versa).
     * For example, a model text node with `"Foo"` as data and the `bold` attribute will be turned to `<strong>Foo</strong>` in the view.
     *
     *		// A simple conversion from the `bold=true` attribute to the `<strong>` view element (and vice versa).
     *		editor.conversion.attributeToElement( { model: 'bold', view: 'strong' } );
     *
     *		// Override other converters by specifying a converter definition with a higher priority.
     *		editor.conversion.attributeToElement( { model: 'bold', view: 'b', converterPriority: 'high' } );
     *
     *		// View specified as an object instead of a string.
     *		editor.conversion.attributeToElement( {
     *			model: 'bold',
     *			view: {
     *				name: 'span',
     *				classes: 'bold'
     *			}
     *		} );
     *
     *		// Use `config.model.name` to define the conversion only from a given node type, `$text` in this case.
     *		// The same attribute on different elements may then be handled by a different converter.
     *		editor.conversion.attributeToElement( {
     *			model: {
     *				key: 'textDecoration',
     *				values: [ 'underline', 'lineThrough' ],
     *				name: '$text'
     *			},
     *			view: {
     *				underline: {
     *					name: 'span',
     *					styles: {
     *						'text-decoration': 'underline'
     *					}
     *				},
     *				lineThrough: {
     *					name: 'span',
     *					styles: {
     *						'text-decoration': 'line-through'
     *					}
     *				}
     *			}
     *		} );
     *
     *		// Use `upcastAlso` to define other view elements that should also be converted to the `bold` attribute.
     *		editor.conversion.attributeToElement( {
     *			model: 'bold',
     *			view: 'strong',
     *			upcastAlso: [
     *				'b',
     *				{
     *					name: 'span',
     *					classes: 'bold'
     *				},
     *				{
     *					name: 'span',
     *					styles: {
     *						'font-weight': 'bold'
     *					}
     *				},
     *				viewElement => {
     *					const fontWeight = viewElement.getStyle( 'font-weight' );
     *
     *					if ( viewElement.is( 'element', 'span' ) && fontWeight && /\d+/.test() && Number( fontWeight ) > 500 ) {
     *						// Returned value can be an object with the matched properties.
     *						// These properties will be "consumed" during the conversion.
     *						// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.
     *
     *						return {
     *							name: true,
     *							styles: [ 'font-weight' ]
     *						};
     *					}
     *				}
     *			]
     *		} );
     *
     *		// Conversion from and to a model attribute key whose value is an enum (`fontSize=big|small`).
     *		// `upcastAlso` set as callback enables a conversion of a wide range of different view elements.
     *		editor.conversion.attributeToElement( {
     *			model: {
     *				key: 'fontSize',
     *				values: [ 'big', 'small' ]
     *			},
     *			view: {
     *				big: {
     *					name: 'span',
     *					styles: {
     *						'font-size': '1.2em'
     *					}
     *				},
     *				small: {
     *					name: 'span',
     *					styles: {
     *						'font-size': '0.8em'
     *					}
     *				}
     *			},
     *			upcastAlso: {
     *				big: viewElement => {
     *					const fontSize = viewElement.getStyle( 'font-size' );
     *
     *					if ( !fontSize ) {
     *						return null;
     *					}
     *
     *					const match = fontSize.match( /(\d+)\s*px/ );
     *
     *					if ( !match ) {
     *						return null;
     *					}
     *
     *					const size = Number( match[ 1 ] );
     *
     *					if ( viewElement.is( 'element', 'span' ) && size > 10 ) {
     *						// Returned value can be an object with the matched properties.
     *						// These properties will be "consumed" during the conversion.
     *						// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.
     *
     *						return { name: true, styles: [ 'font-size' ] };
     *					}
     *
     *					return null;
     *				},
     *				small: viewElement => {
     *					const fontSize = viewElement.getStyle( 'font-size' );
     *
     *					if ( !fontSize ) {
     *						return null;
     *					}
     *
     *					const match = fontSize.match( /(\d+)\s*px/ );
     *
     *					if ( !match ) {
     *						return null;
     *					}
     *
     *					const size = Number( match[ 1 ] );
     *
     *					if ( viewElement.is( 'element', 'span' ) && size < 10 ) {
     *						// Returned value can be an object with the matched properties.
     *						// These properties will be "consumed" during the conversion.
     *						// See `engine.view.Matcher~MatcherPattern` and `engine.view.Matcher#match` for more details.
     *
     *						return { name: true, styles: [ 'font-size' ] };
     *					}
     *
     *					return null;
     *				}
     *			}
     *		} );
     *
     * The `definition.model` parameter specifies which model attribute should be converted from or to. It can be a `{ key, value }` object
     * describing the attribute key and value to convert or a `String` specifying just the attribute key (in such a case
     * `value` is set to `true`).
     * See {@link module:engine/conversion/conversion~ConverterDefinition} to learn about other parameters.
     *
     * @param {module:engine/conversion/conversion~ConverterDefinition} definition The converter definition.
     */
    attributeToElement(definition) {
        // Set up downcast converter.
        this.for('downcast').attributeToElement(definition);
        // Set up upcast converter.
        for (const { model, view } of _getAllUpcastDefinitions(definition)) {
            this.for('upcast')
                .elementToAttribute({
                view,
                model,
                converterPriority: definition.converterPriority
            });
        }
    }
    /**
     * Sets up converters between the model and the view that convert a model attribute to a view attribute (and vice versa). For example,
     * `<imageBlock src='foo.jpg'></imageBlock>` is converted to `<img src='foo.jpg'></img>` (the same attribute key and value).
     * This type of converters is intended to be used with {@link module:engine/model/element~Element model element} nodes.
     * To convert the text attributes,
     * the {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement converter`}should be set up.
     *
     *		// A simple conversion from the `source` model attribute to the `src` view attribute (and vice versa).
     *		editor.conversion.attributeToAttribute( { model: 'source', view: 'src' } );
     *
     *		// Attribute values are strictly specified.
     *		editor.conversion.attributeToAttribute( {
     *			model: {
     *				name: 'imageInline',
     *				key: 'aside',
     *				values: [ 'aside' ]
     *			},
     *			view: {
     *				aside: {
     *					name: 'img',
     *					key: 'class',
     *					value: [ 'aside', 'half-size' ]
     *				}
     *			}
     *		} );
     *
     *		// Set the style attribute.
     *		editor.conversion.attributeToAttribute( {
     *			model: {
     *				name: 'imageInline',
     *				key: 'aside',
     *				values: [ 'aside' ]
     *			},
     *			view: {
     *				aside: {
     *					name: 'img',
     *					key: 'style',
     *					value: {
     *						float: 'right',
     *						width: '50%',
     *						margin: '5px'
     *					}
     *				}
     *			}
     *		} );
     *
     *		// Conversion from and to a model attribute key whose value is an enum (`align=right|center`).
     *		// Use `upcastAlso` to define other view elements that should also be converted to the `align=right` attribute.
     *		editor.conversion.attributeToAttribute( {
     *			model: {
     *				key: 'align',
     *				values: [ 'right', 'center' ]
     *			},
     *			view: {
     *				right: {
     *					key: 'class',
     *					value: 'align-right'
     *				},
     *				center: {
     *					key: 'class',
     *					value: 'align-center'
     *				}
     *			},
     *			upcastAlso: {
     *				right: {
     *					styles: {
     *						'text-align': 'right'
     *					}
     *				},
     *				center: {
     *					styles: {
     *						'text-align': 'center'
     *					}
     *				}
     *			}
     *		} );
     *
     * The `definition.model` parameter specifies which model attribute should be converted from and to.
     * It can be a `{ key, [ values ], [ name ] }` object or a `String`, which will be treated like `{ key: definition.model }`.
     * The `key` property is the model attribute key to convert from and to.
     * The `values` are the possible model attribute values. If the `values` parameter is not set, the model attribute value
     * will be the same as the view attribute value.
     * If `name` is set, the conversion will be set up only for model elements with the given name.
     *
     * The `definition.view` parameter specifies which view attribute should be converted from and to.
     * It can be a `{ key, value, [ name ] }` object or a `String`, which will be treated like `{ key: definition.view }`.
     * The `key` property is the view attribute key to convert from and to.
     * The `value` is the view attribute value to convert from and to. If `definition.value` is not set, the view attribute value will be
     * the same as the model attribute value.
     * If `key` is `'class'`, `value` can be a `String` or an array of `String`s.
     * If `key` is `'style'`, `value` is an object with key-value pairs.
     * In other cases, `value` is a `String`.
     * If `name` is set, the conversion will be set up only for model elements with the given name.
     * If `definition.model.values` is set, `definition.view` is an object that assigns values from `definition.model.values`
     * to `{ key, value, [ name ] }` objects.
     *
     * `definition.upcastAlso` specifies which other matching view elements should also be upcast to the given model configuration.
     * If `definition.model.values` is set, `definition.upcastAlso` should be an object assigning values from `definition.model.values`
     * to {@link module:engine/view/matcher~MatcherPattern}s or arrays of {@link module:engine/view/matcher~MatcherPattern}s.
     *
     * **Note:** `definition.model` and `definition.view` form should be mirrored, so the same types of parameters should
     * be given in both parameters.
     *
     * @param {Object} definition The converter definition.
     * @param {String|Object} definition.model The model attribute to convert from and to.
     * @param {String|Object} definition.view The view attribute to convert from and to.
     * @param {module:engine/view/matcher~MatcherPattern|Array.<module:engine/view/matcher~MatcherPattern>} [definition.upcastAlso]
     * Any view element matching `definition.upcastAlso` will also be converted to the given model attribute. `definition.upcastAlso`
     * is used only if `config.model.values` is specified.
     */
    attributeToAttribute(definition) {
        // Set up downcast converter.
        this.for('downcast').attributeToAttribute(definition);
        // Set up upcast converter.
        for (const { model, view } of _getAllUpcastDefinitions(definition)) {
            this.for('upcast')
                .attributeToAttribute({
                view,
                model
            });
        }
    }
    /**
     * Creates and caches conversion helpers for given dispatchers group.
     *
     * @private
     * @param {Object} options
     * @param {String} options.name Group name.
     * @param {Array.<module:engine/conversion/downcastdispatcher~DowncastDispatcher|
     * module:engine/conversion/upcastdispatcher~UpcastDispatcher>} options.dispatchers
     * @param {Boolean} options.isDowncast
     */
    _createConversionHelpers({ name, dispatchers, isDowncast }) {
        if (this._helpers.has(name)) {
            /**
             * Trying to register a group name that has already been registered.
             *
             * @error conversion-group-exists
             */
            throw new CKEditorError('conversion-group-exists', this);
        }
        const helpers = isDowncast ?
            new DowncastHelpers(dispatchers) :
            new UpcastHelpers(dispatchers);
        this._helpers.set(name, helpers);
    }
}
/**
 * Defines how the model should be converted from and to the view.
 *
 * @typedef {Object} module:engine/conversion/conversion~ConverterDefinition
 *
 * @property {*} [model] The model conversion definition. Describes the model element or model attribute to convert. This parameter differs
 * for different functions that accept `ConverterDefinition`. See the description of the function to learn how to set it.
 * @property {module:engine/view/elementdefinition~ElementDefinition|Object} view The definition of the view element to convert from and
 * to. If `model` describes multiple values, `view` is an object that assigns these values (`view` object keys) to view element definitions
 * (`view` object values).
 * @property {module:engine/view/matcher~MatcherPattern|Array.<module:engine/view/matcher~MatcherPattern>} [upcastAlso]
 * Any view element matching `upcastAlso` will also be converted to the model. If `model` describes multiple values, `upcastAlso`
 * is an object that assigns these values (`upcastAlso` object keys) to {@link module:engine/view/matcher~MatcherPattern}s
 * (`upcastAlso` object values).
 * @property {module:utils/priorities~PriorityString} [converterPriority] The converter priority.
 */
// Helper function that creates a joint array out of an item passed in `definition.view` and items passed in
// `definition.upcastAlso`.
//
// @param {module:engine/conversion/conversion~ConverterDefinition} definition
// @returns {Array} Array containing view definitions.
function* _getAllUpcastDefinitions(definition) {
    if (definition.model.values) {
        for (const value of definition.model.values) {
            const model = { key: definition.model.key, value };
            const view = definition.view[value];
            const upcastAlso = definition.upcastAlso ? definition.upcastAlso[value] : undefined;
            yield* _getUpcastDefinition(model, view, upcastAlso);
        }
    }
    else {
        yield* _getUpcastDefinition(definition.model, definition.view, definition.upcastAlso);
    }
}
function* _getUpcastDefinition(model, view, upcastAlso) {
    yield { model, view };
    if (upcastAlso) {
        for (const upcastAlsoItem of toArray(upcastAlso)) {
            yield { model, view: upcastAlsoItem };
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/batch.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/batch
 */

/**
 * A batch instance groups model changes ({@link module:engine/model/operation/operation~Operation operations}). All operations
 * grouped in a single batch can be reverted together, so you can also think about a batch as of a single undo step. If you want
 * to extend a given undo step, you can add more changes to the batch using {@link module:engine/model/model~Model#enqueueChange}:
 *
 *		model.enqueueChange( batch, writer => {
 *			writer.insertText( 'foo', paragraph, 'end' );
 *		} );
 *
 * @see module:engine/model/model~Model#enqueueChange
 * @see module:engine/model/model~Model#change
 */
class Batch {
    /**
     * Creates a batch instance.
     *
     * @see module:engine/model/model~Model#enqueueChange
     * @see module:engine/model/model~Model#change
     * @param {Object} [type] A set of flags that specify the type of the batch. Batch type can alter how some of the features work
     * when encountering a given `Batch` instance (for example, when a feature listens to applied operations).
     * @param {Boolean} [type.isUndoable=true] Whether a batch can be undone through undo feature.
     * @param {Boolean} [type.isLocal=true] Whether a batch includes operations created locally (`true`) or operations created on
     * other, remote editors (`false`).
     * @param {Boolean} [type.isUndo=false] Whether a batch was created by the undo feature and undoes other operations.
     * @param {Boolean} [type.isTyping=false] Whether a batch includes operations connected with a typing action.
     */
    constructor(type = {}) {
        if (typeof type === 'string') {
            type = type === 'transparent' ? { isUndoable: false } : {};
            /**
             * The string value for a `type` property of the `Batch` constructor has been deprecated and will be removed in the near future.
             * Please refer to the {@link module:engine/model/batch~Batch#constructor `Batch` constructor API documentation} for more
             * information.
             *
             * @error batch-constructor-deprecated-string-type
             */
            logWarning('batch-constructor-deprecated-string-type');
        }
        const { isUndoable = true, isLocal = true, isUndo = false, isTyping = false } = type;
        /**
         * An array of operations that compose this batch.
         *
         * @readonly
         * @type {Array.<module:engine/model/operation/operation~Operation>}
         */
        this.operations = [];
        /**
         * Whether the batch can be undone through the undo feature.
         *
         * @readonly
         * @type {Boolean}
         */
        this.isUndoable = isUndoable;
        /**
         * Whether the batch includes operations created locally (`true`) or operations created on other, remote editors (`false`).
         *
         * @readonly
         * @type {Boolean}
         */
        this.isLocal = isLocal;
        /**
         * Whether the batch was created by the undo feature and undoes other operations.
         *
         * @readonly
         * @type {Boolean}
         */
        this.isUndo = isUndo;
        /**
         * Whether the batch includes operations connected with typing.
         *
         * @readonly
         * @type {Boolean}
         */
        this.isTyping = isTyping;
    }
    /**
     * The type of the batch.
     *
     * **This property has been deprecated and is always set to the `'default'` value.**
     *
     * It can be one of the following values:
     * * `'default'` &ndash; All "normal" batches. This is the most commonly used type.
     * * `'transparent'` &ndash; A batch that should be ignored by other features, i.e. an initial batch or collaborative editing
     * changes.
     *
     * @deprecated
     * @type {'default'}
     */
    get type() {
        /**
         * The {@link module:engine/model/batch~Batch#type `Batch#type` } property has been deprecated and will be removed in the near
         * future. Use `Batch#isLocal`, `Batch#isUndoable`, `Batch#isUndo` and `Batch#isTyping` instead.
         *
         * @error batch-type-deprecated
         */
        logWarning('batch-type-deprecated');
        return 'default';
    }
    /**
     * Returns the base version of this batch, which is equal to the base version of the first operation in the batch.
     * If there are no operations in the batch or neither operation has the base version set, it returns `null`.
     *
     * @readonly
     * @type {Number|null}
     */
    get baseVersion() {
        for (const op of this.operations) {
            if (op.baseVersion !== null) {
                return op.baseVersion;
            }
        }
        return null;
    }
    /**
     * Adds an operation to the batch instance.
     *
     * @param {module:engine/model/operation/operation~Operation} operation An operation to add.
     * @returns {module:engine/model/operation/operation~Operation} The added operation.
     */
    addOperation(operation) {
        operation.batch = this;
        this.operations.push(operation);
        return operation;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/differ.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/differ
 */


/**
 * Calculates the difference between two model states.
 *
 * Receives operations that are to be applied on the model document. Marks parts of the model document tree which
 * are changed and saves the state of these elements before the change. Then, it compares saved elements with the
 * changed elements, after all changes are applied on the model document. Calculates the diff between saved
 * elements and new ones and returns a change set.
 */
class Differ {
    /**
     * Creates a `Differ` instance.
     *
     * @param {module:engine/model/markercollection~MarkerCollection} markerCollection Model's marker collection.
     */
    constructor(markerCollection) {
        /**
         * Reference to the model's marker collection.
         *
         * @private
         * @type {module:engine/model/markercollection~MarkerCollection}
         */
        this._markerCollection = markerCollection;
        /**
         * A map that stores changes that happened in a given element.
         *
         * The keys of the map are references to the model elements.
         * The values of the map are arrays with changes that were done on this element.
         *
         * @private
         * @type {Map}
         */
        this._changesInElement = new Map();
        /**
         * A map that stores "element's children snapshots". A snapshot is representing children of a given element before
         * the first change was applied on that element. Snapshot items are objects with two properties: `name`,
         * containing the element name (or `'$text'` for a text node) and `attributes` which is a map of the node's attributes.
         *
         * @private
         * @type {Map}
         */
        this._elementSnapshots = new Map();
        /**
         * A map that stores all changed markers.
         *
         * The keys of the map are marker names.
         * The values of the map are objects with the following properties:
         * - `oldMarkerData`,
         * - `newMarkerData`.
         *
         * @private
         * @type {Map.<String, Object>}
         */
        this._changedMarkers = new Map();
        /**
         * Stores the number of changes that were processed. Used to order the changes chronologically. It is important
         * when changes are sorted.
         *
         * @private
         * @type {Number}
         */
        this._changeCount = 0;
        /**
         * For efficiency purposes, `Differ` stores the change set returned by the differ after {@link #getChanges} call.
         * Cache is reset each time a new operation is buffered. If the cache has not been reset, {@link #getChanges} will
         * return the cached value instead of calculating it again.
         *
         * This property stores those changes that did not take place in graveyard root.
         *
         * @private
         * @type {Array.<Object>|null}
         */
        this._cachedChanges = null;
        /**
         * For efficiency purposes, `Differ` stores the change set returned by the differ after the {@link #getChanges} call.
         * The cache is reset each time a new operation is buffered. If the cache has not been reset, {@link #getChanges} will
         * return the cached value instead of calculating it again.
         *
         * This property stores all changes evaluated by `Differ`, including those that took place in the graveyard.
         *
         * @private
         * @type {Array.<Object>|null}
         */
        this._cachedChangesWithGraveyard = null;
        /**
         * Set of model items that were marked to get refreshed in {@link #_refreshItem}.
         *
         * @private
         * @type {Set.<module:engine/model/item~Item>}
         */
        this._refreshedItems = new Set();
    }
    /**
     * Informs whether there are any changes buffered in `Differ`.
     *
     * @readonly
     * @type {Boolean}
     */
    get isEmpty() {
        return this._changesInElement.size == 0 && this._changedMarkers.size == 0;
    }
    /**
     * Buffers the given operation. An operation has to be buffered before it is executed.
     *
     * Operation type is checked and it is checked which nodes it will affect. These nodes are then stored in `Differ`
     * in the state before the operation is executed.
     *
     * @param {module:engine/model/operation/operation~Operation} operationToBuffer An operation to buffer.
     */
    bufferOperation(operationToBuffer) {
        // Below we take an operation, check its type, then use its parameters in marking (private) methods.
        // The general rule is to not mark elements inside inserted element. All inserted elements are re-rendered.
        // Marking changes in them would cause a "double" changing then.
        //
        const operation = operationToBuffer;
        switch (operation.type) {
            case 'insert': {
                if (this._isInInsertedElement(operation.position.parent)) {
                    return;
                }
                this._markInsert(operation.position.parent, operation.position.offset, operation.nodes.maxOffset);
                break;
            }
            case 'addAttribute':
            case 'removeAttribute':
            case 'changeAttribute': {
                for (const item of operation.range.getItems({ shallow: true })) {
                    if (this._isInInsertedElement(item.parent)) {
                        continue;
                    }
                    this._markAttribute(item);
                }
                break;
            }
            case 'remove':
            case 'move':
            case 'reinsert': {
                // When range is moved to the same position then not mark it as a change.
                // See: https://github.com/ckeditor/ckeditor5-engine/issues/1664.
                if (operation.sourcePosition.isEqual(operation.targetPosition) ||
                    operation.sourcePosition.getShiftedBy(operation.howMany).isEqual(operation.targetPosition)) {
                    return;
                }
                const sourceParentInserted = this._isInInsertedElement(operation.sourcePosition.parent);
                const targetParentInserted = this._isInInsertedElement(operation.targetPosition.parent);
                if (!sourceParentInserted) {
                    this._markRemove(operation.sourcePosition.parent, operation.sourcePosition.offset, operation.howMany);
                }
                if (!targetParentInserted) {
                    this._markInsert(operation.targetPosition.parent, operation.getMovedRangeStart().offset, operation.howMany);
                }
                break;
            }
            case 'rename': {
                if (this._isInInsertedElement(operation.position.parent)) {
                    return;
                }
                this._markRemove(operation.position.parent, operation.position.offset, 1);
                this._markInsert(operation.position.parent, operation.position.offset, 1);
                const range = range_Range._createFromPositionAndShift(operation.position, 1);
                for (const marker of this._markerCollection.getMarkersIntersectingRange(range)) {
                    const markerData = marker.getData();
                    this.bufferMarkerChange(marker.name, markerData, markerData);
                }
                break;
            }
            case 'split': {
                const splitElement = operation.splitPosition.parent;
                // Mark that children of the split element were removed.
                if (!this._isInInsertedElement(splitElement)) {
                    this._markRemove(splitElement, operation.splitPosition.offset, operation.howMany);
                }
                // Mark that the new element (split copy) was inserted.
                if (!this._isInInsertedElement(operation.insertionPosition.parent)) {
                    this._markInsert(operation.insertionPosition.parent, operation.insertionPosition.offset, 1);
                }
                // If the split took the element from the graveyard, mark that the element from the graveyard was removed.
                if (operation.graveyardPosition) {
                    this._markRemove(operation.graveyardPosition.parent, operation.graveyardPosition.offset, 1);
                }
                break;
            }
            case 'merge': {
                // Mark that the merged element was removed.
                const mergedElement = operation.sourcePosition.parent;
                if (!this._isInInsertedElement(mergedElement.parent)) {
                    this._markRemove(mergedElement.parent, mergedElement.startOffset, 1);
                }
                // Mark that the merged element was inserted into graveyard.
                const graveyardParent = operation.graveyardPosition.parent;
                this._markInsert(graveyardParent, operation.graveyardPosition.offset, 1);
                // Mark that children of merged element were inserted at new parent.
                const mergedIntoElement = operation.targetPosition.parent;
                if (!this._isInInsertedElement(mergedIntoElement)) {
                    this._markInsert(mergedIntoElement, operation.targetPosition.offset, mergedElement.maxOffset);
                }
                break;
            }
        }
        // Clear cache after each buffered operation as it is no longer valid.
        this._cachedChanges = null;
    }
    /**
     * Buffers a marker change.
     *
     * @param {String} markerName The name of the marker that changed.
     * @param {module:engine/model/markercollection~MarkerData} oldMarkerData Marker data before the change.
     * @param {module:engine/model/markercollection~MarkerData} newMarkerData Marker data after the change.
     */
    bufferMarkerChange(markerName, oldMarkerData, newMarkerData) {
        const buffered = this._changedMarkers.get(markerName);
        if (!buffered) {
            this._changedMarkers.set(markerName, {
                newMarkerData,
                oldMarkerData
            });
        }
        else {
            buffered.newMarkerData = newMarkerData;
            if (buffered.oldMarkerData.range == null && newMarkerData.range == null) {
                // The marker is going to be removed (`newMarkerData.range == null`) but it did not exist before the first buffered change
                // (`buffered.oldMarkerData.range == null`). In this case, do not keep the marker in buffer at all.
                this._changedMarkers.delete(markerName);
            }
        }
    }
    /**
     * Returns all markers that should be removed as a result of buffered changes.
     *
     * @returns {Array.<Object>} Markers to remove. Each array item is an object containing the `name` and `range` properties.
     */
    getMarkersToRemove() {
        const result = [];
        for (const [name, change] of this._changedMarkers) {
            if (change.oldMarkerData.range != null) {
                result.push({ name, range: change.oldMarkerData.range });
            }
        }
        return result;
    }
    /**
     * Returns all markers which should be added as a result of buffered changes.
     *
     * @returns {Array.<Object>} Markers to add. Each array item is an object containing the `name` and `range` properties.
     */
    getMarkersToAdd() {
        const result = [];
        for (const [name, change] of this._changedMarkers) {
            if (change.newMarkerData.range != null) {
                result.push({ name, range: change.newMarkerData.range });
            }
        }
        return result;
    }
    /**
     * Returns all markers which changed.
     *
     * @returns {Array.<Object>}
     */
    getChangedMarkers() {
        return Array.from(this._changedMarkers).map(([name, change]) => ({
            name,
            data: {
                oldRange: change.oldMarkerData.range,
                newRange: change.newMarkerData.range
            }
        }));
    }
    /**
     * Checks whether some of the buffered changes affect the editor data.
     *
     * Types of changes which affect the editor data:
     *
     * * model structure changes,
     * * attribute changes,
     * * changes of markers which were defined as `affectsData`,
     * * changes of markers' `affectsData` property.
     *
     * @returns {Boolean}
     */
    hasDataChanges() {
        if (this._changesInElement.size > 0) {
            return true;
        }
        for (const { newMarkerData, oldMarkerData } of this._changedMarkers.values()) {
            if (newMarkerData.affectsData !== oldMarkerData.affectsData) {
                return true;
            }
            if (newMarkerData.affectsData) {
                const markerAdded = newMarkerData.range && !oldMarkerData.range;
                const markerRemoved = !newMarkerData.range && oldMarkerData.range;
                const markerChanged = newMarkerData.range && oldMarkerData.range && !newMarkerData.range.isEqual(oldMarkerData.range);
                if (markerAdded || markerRemoved || markerChanged) {
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * Calculates the diff between the old model tree state (the state before the first buffered operations since the last {@link #reset}
     * call) and the new model tree state (actual one). It should be called after all buffered operations are executed.
     *
     * The diff set is returned as an array of {@link module:engine/model/differ~DiffItem diff items}, each describing a change done
     * on the model. The items are sorted by the position on which the change happened. If a position
     * {@link module:engine/model/position~Position#isBefore is before} another one, it will be on an earlier index in the diff set.
     *
     * **Note**: Elements inside inserted element will not have a separate diff item, only the top most element change will be reported.
     *
     * Because calculating the diff is a costly operation, the result is cached. If no new operation was buffered since the
     * previous {@link #getChanges} call, the next call will return the cached value.
     *
     * @param {Object} options Additional options.
     * @param {Boolean} [options.includeChangesInGraveyard=false] If set to `true`, also changes that happened
     * in the graveyard root will be returned. By default, changes in the graveyard root are not returned.
     * @returns {Array.<module:engine/model/differ~DiffItem>} Diff between the old and the new model tree state.
     */
    getChanges(options = {}) {
        // If there are cached changes, just return them instead of calculating changes again.
        if (this._cachedChanges) {
            if (options.includeChangesInGraveyard) {
                return this._cachedChangesWithGraveyard.slice();
            }
            else {
                return this._cachedChanges.slice();
            }
        }
        // Will contain returned results.
        let diffSet = [];
        // Check all changed elements.
        for (const element of this._changesInElement.keys()) {
            // Get changes for this element and sort them.
            const changes = this._changesInElement.get(element).sort((a, b) => {
                if (a.offset === b.offset) {
                    if (a.type != b.type) {
                        // If there are multiple changes at the same position, "remove" change should be first.
                        // If the order is different, for example, we would first add some nodes and then removed them
                        // (instead of the nodes that we should remove).
                        return a.type == 'remove' ? -1 : 1;
                    }
                    return 0;
                }
                return a.offset < b.offset ? -1 : 1;
            });
            // Get children of this element before any change was applied on it.
            const snapshotChildren = this._elementSnapshots.get(element);
            // Get snapshot of current element's children.
            const elementChildren = _getChildrenSnapshot(element.getChildren());
            // Generate actions basing on changes done on element.
            const actions = _generateActionsFromChanges(snapshotChildren.length, changes);
            let i = 0; // Iterator in `elementChildren` array -- iterates through current children of element.
            let j = 0; // Iterator in `snapshotChildren` array -- iterates through old children of element.
            // Process every action.
            for (const action of actions) {
                if (action === 'i') {
                    // Generate diff item for this element and insert it into the diff set.
                    diffSet.push(this._getInsertDiff(element, i, elementChildren[i]));
                    i++;
                }
                else if (action === 'r') {
                    // Generate diff item for this element and insert it into the diff set.
                    diffSet.push(this._getRemoveDiff(element, i, snapshotChildren[j]));
                    j++;
                }
                else if (action === 'a') {
                    // Take attributes from saved and current children.
                    const elementAttributes = elementChildren[i].attributes;
                    const snapshotAttributes = snapshotChildren[j].attributes;
                    let range;
                    if (elementChildren[i].name == '$text') {
                        range = new range_Range(position_Position._createAt(element, i), position_Position._createAt(element, i + 1));
                    }
                    else {
                        const index = element.offsetToIndex(i);
                        range = new range_Range(position_Position._createAt(element, i), position_Position._createAt(element.getChild(index), 0));
                    }
                    // Generate diff items for this change (there might be multiple attributes changed and
                    // there is a single diff for each of them) and insert them into the diff set.
                    diffSet.push(...this._getAttributesDiff(range, snapshotAttributes, elementAttributes));
                    i++;
                    j++;
                }
                else {
                    // `action` is 'equal'. Child not changed.
                    i++;
                    j++;
                }
            }
        }
        // Then, sort the changes by the position (change at position before other changes is first).
        diffSet.sort((a, b) => {
            // If the change is in different root, we don't care much, but we'd like to have all changes in given
            // root "together" in the array. So let's just sort them by the root name. It does not matter which root
            // will be processed first.
            if (a.position.root != b.position.root) {
                return a.position.root.rootName < b.position.root.rootName ? -1 : 1;
            }
            // If change happens at the same position...
            if (a.position.isEqual(b.position)) {
                // Keep chronological order of operations.
                return a.changeCount - b.changeCount;
            }
            // If positions differ, position "on the left" should be earlier in the result.
            return a.position.isBefore(b.position) ? -1 : 1;
        });
        // Glue together multiple changes (mostly on text nodes).
        for (let i = 1, prevIndex = 0; i < diffSet.length; i++) {
            const prevDiff = diffSet[prevIndex];
            const thisDiff = diffSet[i];
            // Glue remove changes if they happen on text on same position.
            const isConsecutiveTextRemove = prevDiff.type == 'remove' && thisDiff.type == 'remove' &&
                prevDiff.name == '$text' && thisDiff.name == '$text' &&
                prevDiff.position.isEqual(thisDiff.position);
            // Glue insert changes if they happen on text on consecutive fragments.
            const isConsecutiveTextAdd = prevDiff.type == 'insert' && thisDiff.type == 'insert' &&
                prevDiff.name == '$text' && thisDiff.name == '$text' &&
                prevDiff.position.parent == thisDiff.position.parent &&
                prevDiff.position.offset + prevDiff.length == thisDiff.position.offset;
            // Glue attribute changes if they happen on consecutive fragments and have same key, old value and new value.
            const isConsecutiveAttributeChange = prevDiff.type == 'attribute' && thisDiff.type == 'attribute' &&
                prevDiff.position.parent == thisDiff.position.parent &&
                prevDiff.range.isFlat && thisDiff.range.isFlat &&
                (prevDiff.position.offset + prevDiff.length) == thisDiff.position.offset &&
                prevDiff.attributeKey == thisDiff.attributeKey &&
                prevDiff.attributeOldValue == thisDiff.attributeOldValue &&
                prevDiff.attributeNewValue == thisDiff.attributeNewValue;
            if (isConsecutiveTextRemove || isConsecutiveTextAdd || isConsecutiveAttributeChange) {
                prevDiff.length++;
                if (isConsecutiveAttributeChange) {
                    prevDiff.range.end = prevDiff.range.end.getShiftedBy(1);
                }
                diffSet[i] = null;
            }
            else {
                prevIndex = i;
            }
        }
        diffSet = diffSet.filter(v => v);
        // Remove `changeCount` property from diff items. It is used only for sorting and is internal thing.
        for (const item of diffSet) {
            delete item.changeCount;
            if (item.type == 'attribute') {
                delete item.position;
                delete item.length;
            }
        }
        this._changeCount = 0;
        // Cache changes.
        this._cachedChangesWithGraveyard = diffSet;
        this._cachedChanges = diffSet.filter(_changesInGraveyardFilter);
        if (options.includeChangesInGraveyard) {
            return this._cachedChangesWithGraveyard.slice();
        }
        else {
            return this._cachedChanges.slice();
        }
    }
    /**
     * Returns a set of model items that were marked to get refreshed.
     *
     * @return {Set.<module:engine/model/item~Item>}
     */
    getRefreshedItems() {
        return new Set(this._refreshedItems);
    }
    /**
     * Resets `Differ`. Removes all buffered changes.
     */
    reset() {
        this._changesInElement.clear();
        this._elementSnapshots.clear();
        this._changedMarkers.clear();
        this._refreshedItems = new Set();
        this._cachedChanges = null;
    }
    /**
     * Marks the given `item` in differ to be "refreshed". It means that the item will be marked as removed and inserted
     * in the differ changes set, so it will be effectively re-converted when the differ changes are handled by a dispatcher.
     *
     * @protected
     * @internal
     * @param {module:engine/model/item~Item} item Item to refresh.
     */
    _refreshItem(item) {
        if (this._isInInsertedElement(item.parent)) {
            return;
        }
        this._markRemove(item.parent, item.startOffset, item.offsetSize);
        this._markInsert(item.parent, item.startOffset, item.offsetSize);
        this._refreshedItems.add(item);
        const range = range_Range._createOn(item);
        for (const marker of this._markerCollection.getMarkersIntersectingRange(range)) {
            const markerData = marker.getData();
            this.bufferMarkerChange(marker.name, markerData, markerData);
        }
        // Clear cache after each buffered operation as it is no longer valid.
        this._cachedChanges = null;
    }
    /**
     * Saves and handles an insert change.
     *
     * @private
     * @param {module:engine/model/element~Element} parent
     * @param {Number} offset
     * @param {Number} howMany
     */
    _markInsert(parent, offset, howMany) {
        const changeItem = { type: 'insert', offset, howMany, count: this._changeCount++ };
        this._markChange(parent, changeItem);
    }
    /**
     * Saves and handles a remove change.
     *
     * @private
     * @param {module:engine/model/element~Element} parent
     * @param {Number} offset
     * @param {Number} howMany
     */
    _markRemove(parent, offset, howMany) {
        const changeItem = { type: 'remove', offset, howMany, count: this._changeCount++ };
        this._markChange(parent, changeItem);
        this._removeAllNestedChanges(parent, offset, howMany);
    }
    /**
     * Saves and handles an attribute change.
     *
     * @private
     * @param {module:engine/model/item~Item} item
     */
    _markAttribute(item) {
        const changeItem = { type: 'attribute', offset: item.startOffset, howMany: item.offsetSize, count: this._changeCount++ };
        this._markChange(item.parent, changeItem);
    }
    /**
     * Saves and handles a model change.
     *
     * @private
     * @param {module:engine/model/element~Element} parent
     * @param {Object} changeItem
     */
    _markChange(parent, changeItem) {
        // First, make a snapshot of this parent's children (it will be made only if it was not made before).
        this._makeSnapshot(parent);
        // Then, get all changes that already were done on the element (empty array if this is the first change).
        const changes = this._getChangesForElement(parent);
        // Then, look through all the changes, and transform them or the new change.
        this._handleChange(changeItem, changes);
        // Add the new change.
        changes.push(changeItem);
        // Remove incorrect changes. During transformation some change might be, for example, included in another.
        // In that case, the change will have `howMany` property set to `0` or less. We need to remove those changes.
        for (let i = 0; i < changes.length; i++) {
            if (changes[i].howMany < 1) {
                changes.splice(i, 1);
                i--;
            }
        }
    }
    /**
     * Gets an array of changes that have already been saved for a given element.
     *
     * @private
     * @param {module:engine/model/element~Element} element
     * @returns {Array.<Object>}
     */
    _getChangesForElement(element) {
        let changes;
        if (this._changesInElement.has(element)) {
            changes = this._changesInElement.get(element);
        }
        else {
            changes = [];
            this._changesInElement.set(element, changes);
        }
        return changes;
    }
    /**
     * Saves a children snapshot for a given element.
     *
     * @private
     * @param {module:engine/model/element~Element} element
     */
    _makeSnapshot(element) {
        if (!this._elementSnapshots.has(element)) {
            this._elementSnapshots.set(element, _getChildrenSnapshot(element.getChildren()));
        }
    }
    /**
     * For a given newly saved change, compares it with a change already done on the element and modifies the incoming
     * change and/or the old change.
     *
     * @private
     * @param {Object} inc Incoming (new) change.
     * @param {Array.<Object>} changes An array containing all the changes done on that element.
     */
    _handleChange(inc, changes) {
        // We need a helper variable that will store how many nodes are to be still handled for this change item.
        // `nodesToHandle` (how many nodes still need to be handled) and `howMany` (how many nodes were affected)
        // needs to be differentiated.
        //
        // This comes up when there are multiple changes that are affected by `inc` change item.
        //
        // For example: assume two insert changes: `{ offset: 2, howMany: 1 }` and `{ offset: 5, howMany: 1 }`.
        // Assume that `inc` change is remove `{ offset: 2, howMany: 2, nodesToHandle: 2 }`.
        //
        // Then, we:
        // - "forget" about first insert change (it is "eaten" by remove),
        // - because of that, at the end we will want to remove only one node (`nodesToHandle = 1`),
        // - but still we have to change offset of the second insert change from `5` to `3`!
        //
        // So, `howMany` does not change throughout items transformation and keeps information about how many nodes were affected,
        // while `nodesToHandle` means how many nodes need to be handled after the change item is transformed by other changes.
        inc.nodesToHandle = inc.howMany;
        for (const old of changes) {
            const incEnd = inc.offset + inc.howMany;
            const oldEnd = old.offset + old.howMany;
            if (inc.type == 'insert') {
                if (old.type == 'insert') {
                    if (inc.offset <= old.offset) {
                        old.offset += inc.howMany;
                    }
                    else if (inc.offset < oldEnd) {
                        old.howMany += inc.nodesToHandle;
                        inc.nodesToHandle = 0;
                    }
                }
                if (old.type == 'remove') {
                    if (inc.offset < old.offset) {
                        old.offset += inc.howMany;
                    }
                }
                if (old.type == 'attribute') {
                    if (inc.offset <= old.offset) {
                        old.offset += inc.howMany;
                    }
                    else if (inc.offset < oldEnd) {
                        // This case is more complicated, because attribute change has to be split into two.
                        // Example (assume that uppercase and lowercase letters mean different attributes):
                        //
                        // initial state:		abcxyz
                        // attribute change:	aBCXYz
                        // incoming insert:		aBCfooXYz
                        //
                        // Change ranges cannot intersect because each item has to be described exactly (it was either
                        // not changed, inserted, removed, or its attribute was changed). That's why old attribute
                        // change has to be split and both parts has to be handled separately from now on.
                        const howMany = old.howMany;
                        old.howMany = inc.offset - old.offset;
                        // Add the second part of attribute change to the beginning of processed array so it won't
                        // be processed again in this loop.
                        changes.unshift({
                            type: 'attribute',
                            offset: incEnd,
                            howMany: howMany - old.howMany,
                            count: this._changeCount++
                        });
                    }
                }
            }
            if (inc.type == 'remove') {
                if (old.type == 'insert') {
                    if (incEnd <= old.offset) {
                        old.offset -= inc.howMany;
                    }
                    else if (incEnd <= oldEnd) {
                        if (inc.offset < old.offset) {
                            const intersectionLength = incEnd - old.offset;
                            old.offset = inc.offset;
                            old.howMany -= intersectionLength;
                            inc.nodesToHandle -= intersectionLength;
                        }
                        else {
                            old.howMany -= inc.nodesToHandle;
                            inc.nodesToHandle = 0;
                        }
                    }
                    else {
                        if (inc.offset <= old.offset) {
                            inc.nodesToHandle -= old.howMany;
                            old.howMany = 0;
                        }
                        else if (inc.offset < oldEnd) {
                            const intersectionLength = oldEnd - inc.offset;
                            old.howMany -= intersectionLength;
                            inc.nodesToHandle -= intersectionLength;
                        }
                    }
                }
                if (old.type == 'remove') {
                    if (incEnd <= old.offset) {
                        old.offset -= inc.howMany;
                    }
                    else if (inc.offset < old.offset) {
                        inc.nodesToHandle += old.howMany;
                        old.howMany = 0;
                    }
                }
                if (old.type == 'attribute') {
                    if (incEnd <= old.offset) {
                        old.offset -= inc.howMany;
                    }
                    else if (inc.offset < old.offset) {
                        const intersectionLength = incEnd - old.offset;
                        old.offset = inc.offset;
                        old.howMany -= intersectionLength;
                    }
                    else if (inc.offset < oldEnd) {
                        if (incEnd <= oldEnd) {
                            // On first sight in this case we don't need to split attribute operation into two.
                            // However the changes set is later converted to actions (see `_generateActionsFromChanges`).
                            // For that reason, no two changes may intersect.
                            // So we cannot have an attribute change that "contains" remove change.
                            // Attribute change needs to be split.
                            const howMany = old.howMany;
                            old.howMany = inc.offset - old.offset;
                            const howManyAfter = howMany - old.howMany - inc.nodesToHandle;
                            // Add the second part of attribute change to the beginning of processed array so it won't
                            // be processed again in this loop.
                            changes.unshift({
                                type: 'attribute',
                                offset: inc.offset,
                                howMany: howManyAfter,
                                count: this._changeCount++
                            });
                        }
                        else {
                            old.howMany -= oldEnd - inc.offset;
                        }
                    }
                }
            }
            if (inc.type == 'attribute') {
                // In case of attribute change, `howMany` should be kept same as `nodesToHandle`. It's not an error.
                if (old.type == 'insert') {
                    if (inc.offset < old.offset && incEnd > old.offset) {
                        if (incEnd > oldEnd) {
                            // This case is similar to a case described when incoming change was insert and old change was attribute.
                            // See comment above.
                            //
                            // This time incoming change is attribute. We need to split incoming change in this case too.
                            // However this time, the second part of the attribute change needs to be processed further
                            // because there might be other changes that it collides with.
                            const attributePart = {
                                type: 'attribute',
                                offset: oldEnd,
                                howMany: incEnd - oldEnd,
                                count: this._changeCount++
                            };
                            this._handleChange(attributePart, changes);
                            changes.push(attributePart);
                        }
                        inc.nodesToHandle = old.offset - inc.offset;
                        inc.howMany = inc.nodesToHandle;
                    }
                    else if (inc.offset >= old.offset && inc.offset < oldEnd) {
                        if (incEnd > oldEnd) {
                            inc.nodesToHandle = incEnd - oldEnd;
                            inc.offset = oldEnd;
                        }
                        else {
                            inc.nodesToHandle = 0;
                        }
                    }
                }
                if (old.type == 'remove') {
                    // This is a case when attribute change "contains" remove change.
                    // The attribute change needs to be split into two because changes cannot intersect.
                    if (inc.offset < old.offset && incEnd > old.offset) {
                        const attributePart = {
                            type: 'attribute',
                            offset: old.offset,
                            howMany: incEnd - old.offset,
                            count: this._changeCount++
                        };
                        this._handleChange(attributePart, changes);
                        changes.push(attributePart);
                        inc.nodesToHandle = old.offset - inc.offset;
                        inc.howMany = inc.nodesToHandle;
                    }
                }
                if (old.type == 'attribute') {
                    // There are only two conflicting scenarios possible here:
                    if (inc.offset >= old.offset && incEnd <= oldEnd) {
                        // `old` change includes `inc` change, or they are the same.
                        inc.nodesToHandle = 0;
                        inc.howMany = 0;
                        inc.offset = 0;
                    }
                    else if (inc.offset <= old.offset && incEnd >= oldEnd) {
                        // `inc` change includes `old` change.
                        old.howMany = 0;
                    }
                }
            }
        }
        inc.howMany = inc.nodesToHandle;
        delete inc.nodesToHandle;
    }
    /**
     * Returns an object with a single insert change description.
     *
     * @private
     * @param {module:engine/model/element~Element} parent The element in which the change happened.
     * @param {Number} offset The offset at which change happened.
     * @param {Object} elementSnapshot The snapshot of the removed element a character.
     * @returns {Object} The diff item.
     */
    _getInsertDiff(parent, offset, elementSnapshot) {
        return {
            type: 'insert',
            position: position_Position._createAt(parent, offset),
            name: elementSnapshot.name,
            attributes: new Map(elementSnapshot.attributes),
            length: 1,
            changeCount: this._changeCount++
        };
    }
    /**
     * Returns an object with a single remove change description.
     *
     * @private
     * @param {module:engine/model/element~Element} parent The element in which change happened.
     * @param {Number} offset The offset at which change happened.
     * @param {Object} elementSnapshot The snapshot of the removed element a character.
     * @returns {Object} The diff item.
     */
    _getRemoveDiff(parent, offset, elementSnapshot) {
        return {
            type: 'remove',
            position: position_Position._createAt(parent, offset),
            name: elementSnapshot.name,
            attributes: new Map(elementSnapshot.attributes),
            length: 1,
            changeCount: this._changeCount++
        };
    }
    /**
     * Returns an array of objects where each one is a single attribute change description.
     *
     * @private
     * @param {module:engine/model/range~Range} range The range where the change happened.
     * @param {Map} oldAttributes A map, map iterator or compatible object that contains attributes before the change.
     * @param {Map} newAttributes A map, map iterator or compatible object that contains attributes after the change.
     * @returns {Array.<Object>} An array containing one or more diff items.
     */
    _getAttributesDiff(range, oldAttributes, newAttributes) {
        // Results holder.
        const diffs = [];
        // Clone new attributes as we will be performing changes on this object.
        newAttributes = new Map(newAttributes);
        // Look through old attributes.
        for (const [key, oldValue] of oldAttributes) {
            // Check what is the new value of the attribute (or if it was removed).
            const newValue = newAttributes.has(key) ? newAttributes.get(key) : null;
            // If values are different (or attribute was removed)...
            if (newValue !== oldValue) {
                // Add diff item.
                diffs.push({
                    type: 'attribute',
                    position: range.start,
                    range: range.clone(),
                    length: 1,
                    attributeKey: key,
                    attributeOldValue: oldValue,
                    attributeNewValue: newValue,
                    changeCount: this._changeCount++
                });
            }
            // Prevent returning two diff items for the same change.
            newAttributes.delete(key);
        }
        // Look through new attributes that weren't handled above.
        for (const [key, newValue] of newAttributes) {
            // Each of them is a new attribute. Add diff item.
            diffs.push({
                type: 'attribute',
                position: range.start,
                range: range.clone(),
                length: 1,
                attributeKey: key,
                attributeOldValue: null,
                attributeNewValue: newValue,
                changeCount: this._changeCount++
            });
        }
        return diffs;
    }
    /**
     * Checks whether given element or any of its parents is an element that is buffered as an inserted element.
     *
     * @private
     * @param {module:engine/model/element~Element} element Element to check.
     * @returns {Boolean}
     */
    _isInInsertedElement(element) {
        const parent = element.parent;
        if (!parent) {
            return false;
        }
        const changes = this._changesInElement.get(parent);
        const offset = element.startOffset;
        if (changes) {
            for (const change of changes) {
                if (change.type == 'insert' && offset >= change.offset && offset < change.offset + change.howMany) {
                    return true;
                }
            }
        }
        return this._isInInsertedElement(parent);
    }
    /**
     * Removes deeply all buffered changes that are registered in elements from range specified by `parent`, `offset`
     * and `howMany`.
     *
     * @private
     * @param {module:engine/model/element~Element} parent
     * @param {Number} offset
     * @param {Number} howMany
     */
    _removeAllNestedChanges(parent, offset, howMany) {
        const range = new range_Range(position_Position._createAt(parent, offset), position_Position._createAt(parent, offset + howMany));
        for (const item of range.getItems({ shallow: true })) {
            if (item.is('element')) {
                this._elementSnapshots.delete(item);
                this._changesInElement.delete(item);
                this._removeAllNestedChanges(item, 0, item.maxOffset);
            }
        }
    }
}
// Returns an array that is a copy of passed child list with the exception that text nodes are split to one or more
// objects, each representing one character and attributes set on that character.
function _getChildrenSnapshot(children) {
    const snapshot = [];
    for (const child of children) {
        if (child.is('$text')) {
            for (let i = 0; i < child.data.length; i++) {
                snapshot.push({
                    name: '$text',
                    attributes: new Map(child.getAttributes())
                });
            }
        }
        else {
            snapshot.push({
                name: child.name,
                attributes: new Map(child.getAttributes())
            });
        }
    }
    return snapshot;
}
// Generates array of actions for given changes set.
// It simulates what `diff` function does.
// Generated actions are:
// - 'e' for 'equal' - when item at that position did not change,
// - 'i' for 'insert' - when item at that position was inserted,
// - 'r' for 'remove' - when item at that position was removed,
// - 'a' for 'attribute' - when item at that position has it attributes changed.
//
// Example (assume that uppercase letters have bold attribute, compare with function code):
//
// children before:	fooBAR
// children after:	foxybAR
//
// changes: type: remove, offset: 1, howMany: 1
//			type: insert, offset: 2, howMany: 2
//			type: attribute, offset: 4, howMany: 1
//
// expected actions: equal (f), remove (o), equal (o), insert (x), insert (y), attribute (b), equal (A), equal (R)
//
// steps taken by th script:
//
// 1. change = "type: remove, offset: 1, howMany: 1"; offset = 0; oldChildrenHandled = 0
//    1.1 between this change and the beginning is one not-changed node, fill with one equal action, one old child has been handled
//    1.2 this change removes one node, add one remove action
//    1.3 change last visited `offset` to 1
//    1.4 since an old child has been removed, one more old child has been handled
//    1.5 actions at this point are: equal, remove
//
// 2. change = "type: insert, offset: 2, howMany: 2"; offset = 1; oldChildrenHandled = 2
//    2.1 between this change and previous change is one not-changed node, add equal action, another one old children has been handled
//    2.2 this change inserts two nodes, add two insert actions
//    2.3 change last visited offset to the end of the inserted range, that is 4
//    2.4 actions at this point are: equal, remove, equal, insert, insert
//
// 3. change = "type: attribute, offset: 4, howMany: 1"; offset = 4, oldChildrenHandled = 3
//    3.1 between this change and previous change are no not-changed nodes
//    3.2 this change changes one node, add one attribute action
//    3.3 change last visited `offset` to the end of change range, that is 5
//    3.4 since an old child has been changed, one more old child has been handled
//    3.5 actions at this point are: equal, remove, equal, insert, insert, attribute
//
// 4. after loop oldChildrenHandled = 4, oldChildrenLength = 6 (fooBAR is 6 characters)
//    4.1 fill up with two equal actions
//
// The result actions are: equal, remove, equal, insert, insert, attribute, equal, equal.
function _generateActionsFromChanges(oldChildrenLength, changes) {
    const actions = [];
    let offset = 0;
    let oldChildrenHandled = 0;
    // Go through all buffered changes.
    for (const change of changes) {
        // First, fill "holes" between changes with "equal" actions.
        if (change.offset > offset) {
            for (let i = 0; i < change.offset - offset; i++) {
                actions.push('e');
            }
            oldChildrenHandled += change.offset - offset;
        }
        // Then, fill up actions accordingly to change type.
        if (change.type == 'insert') {
            for (let i = 0; i < change.howMany; i++) {
                actions.push('i');
            }
            // The last handled offset is after inserted range.
            offset = change.offset + change.howMany;
        }
        else if (change.type == 'remove') {
            for (let i = 0; i < change.howMany; i++) {
                actions.push('r');
            }
            // The last handled offset is at the position where the nodes were removed.
            offset = change.offset;
            // We removed `howMany` old nodes, update `oldChildrenHandled`.
            oldChildrenHandled += change.howMany;
        }
        else {
            actions.push(...'a'.repeat(change.howMany).split(''));
            // The last handled offset is at the position after the changed range.
            offset = change.offset + change.howMany;
            // We changed `howMany` old nodes, update `oldChildrenHandled`.
            oldChildrenHandled += change.howMany;
        }
    }
    // Fill "equal" actions at the end of actions set. Use `oldChildrenHandled` to see how many children
    // has not been changed / removed at the end of their parent.
    if (oldChildrenHandled < oldChildrenLength) {
        for (let i = 0; i < oldChildrenLength - oldChildrenHandled - offset; i++) {
            actions.push('e');
        }
    }
    return actions;
}
// Filter callback for Array.filter that filters out change entries that are in graveyard.
function _changesInGraveyardFilter(entry) {
    const posInGy = 'position' in entry && entry.position.root.rootName == '$graveyard';
    const rangeInGy = 'range' in entry && entry.range.root.rootName == '$graveyard';
    return !posInGy && !rangeInGy;
}
/**
 * The type of diff item.
 *
 * @member {'attribute'} module:engine/model/differ~DiffItemAttribute#type
 */
/**
 * The name of the changed attribute.
 *
 * @member {String} module:engine/model/differ~DiffItemAttribute#attributeKey
 */
/**
 * An attribute previous value (before change).
 *
 * @member {String} module:engine/model/differ~DiffItemAttribute#attributeOldValue
 */
/**
 * An attribute new value (after change).
 *
 * @member {String} module:engine/model/differ~DiffItemAttribute#attributeNewValue
 */
/**
 * The range where the change happened.
 *
 * @member {module:engine/model/range~Range} module:engine/model/differ~DiffItemAttribute#range
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/history.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module engine/model/history
 */
/**
 * `History` keeps the track of all the operations applied to the {@link module:engine/model/document~Document document}.
 */
class History {
    /**
     * Creates an empty History instance.
     */
    constructor() {
        /**
         * Operations added to the history.
         *
         * @private
         * @readonly
         * @type {Array.<module:engine/model/operation/operation~Operation>}
         */
        this._operations = [];
        /**
         * Holds an information which {@link module:engine/model/operation/operation~Operation operation} undoes which
         * {@link module:engine/model/operation/operation~Operation operation}.
         *
         * Keys of the map are "undoing operations", that is operations that undone some other operations. For each key, the
         * value is an operation that has been undone by the "undoing operation".
         *
         * @private
         * @member {Map} module:engine/model/history~History#_undoPairs
         */
        this._undoPairs = new Map();
        /**
         * Holds all undone operations.
         *
         * @private
         * @type {Set.<module:engine/model/operation/operation~Operation>}
         */
        this._undoneOperations = new Set();
        /**
         * A map that allows retrieving the operations fast based on the given base version.
         *
         * @private
         * @type Map.<Number,Number>
         */
        this._baseVersionToOperationIndex = new Map();
        /**
         * The history version.
         *
         * @private
         * @type {Number}
         */
        this._version = 0;
        /**
         * The gap pairs kept in the <from,to> format.
         *
         * Anytime the `history.version` is set to a version larger than `history.version + 1`,
         * a new <lastHistoryVersion, newHistoryVersion> entry is added to the map.
         *
         * @private
         * @type Map.<number,number>
         */
        this._gaps = new Map();
    }
    /**
     * The version of the last operation in the history.
     *
     * The history version is incremented automatically when a new operation is added to the history.
     * Setting the version manually should be done only in rare circumstances when a gap is planned
     * between history versions. When doing so, a gap will be created and the history will accept adding
     * an operation with base version equal to the new history version.
     *
     * @type {Number}
     */
    get version() {
        return this._version;
    }
    set version(version) {
        // Store a gap if there are some operations already in the history and the
        // new version does not increment the latest one.
        if (this._operations.length && version > this._version + 1) {
            this._gaps.set(this._version, version);
        }
        this._version = version;
    }
    /**
     * The last history operation.
     *
     * @readonly
     * @type {module:engine/model/operation/operation~Operation|undefined}
     */
    get lastOperation() {
        return this._operations[this._operations.length - 1];
    }
    /**
     * Adds an operation to the history and increments the history version.
     *
     * The operation's base version should be equal to the history version. Otherwise an error is thrown.
     *
     * @param {module:engine/model/operation/operation~Operation} operation Operation to add.
     */
    addOperation(operation) {
        if (operation.baseVersion !== this.version) {
            /**
             * Only operations with matching versions can be added to the history.
             *
             * @error model-document-history-addoperation-incorrect-version
             * @param {Object} errorData The operation and the current document history version.
             */
            throw new CKEditorError('model-document-history-addoperation-incorrect-version', this, {
                operation,
                historyVersion: this.version
            });
        }
        this._operations.push(operation);
        this._version++;
        this._baseVersionToOperationIndex.set(operation.baseVersion, this._operations.length - 1);
    }
    /**
     * Returns operations from the given range of operation base versions that were added to the history.
     *
     * Note that there may be gaps in operations base versions.
     *
     * @param {Number} [fromBaseVersion] Base version from which operations should be returned (inclusive).
     * @param {Number} [toBaseVersion] Base version up to which operations should be returned (exclusive).
     * @returns {Array.<module:engine/model/operation/operation~Operation>} History operations for the given range, in chronological order.
     */
    getOperations(fromBaseVersion, toBaseVersion = this.version) {
        // When there is no operation in the history, return an empty array.
        // After that we can be sure that `firstOperation`, `lastOperation` are not nullish.
        if (!this._operations.length) {
            return [];
        }
        const firstOperation = this._operations[0];
        if (fromBaseVersion === undefined) {
            fromBaseVersion = firstOperation.baseVersion;
        }
        // Change exclusive `toBaseVersion` to inclusive, so it will refer to the actual index.
        // Thanks to that mapping from base versions to operation indexes are possible.
        let inclusiveTo = toBaseVersion - 1;
        // Check if "from" or "to" point to a gap between versions.
        // If yes, then change the incorrect position to the proper side of the gap.
        // Thanks to it, it will be possible to get index of the operation.
        for (const [gapFrom, gapTo] of this._gaps) {
            if (fromBaseVersion > gapFrom && fromBaseVersion < gapTo) {
                fromBaseVersion = gapTo;
            }
            if (inclusiveTo > gapFrom && inclusiveTo < gapTo) {
                inclusiveTo = gapFrom - 1;
            }
        }
        // If the whole range is outside of the operation versions, then return an empty array.
        if (inclusiveTo < firstOperation.baseVersion || fromBaseVersion > this.lastOperation.baseVersion) {
            return [];
        }
        let fromIndex = this._baseVersionToOperationIndex.get(fromBaseVersion);
        // If the range starts before the first operation, then use the first operation as the range's start.
        if (fromIndex === undefined) {
            fromIndex = 0;
        }
        let toIndex = this._baseVersionToOperationIndex.get(inclusiveTo);
        // If the range ends after the last operation, then use the last operation as the range's end.
        if (toIndex === undefined) {
            toIndex = this._operations.length - 1;
        }
        // Return the part of the history operations based on the calculated start index and end index.
        return this._operations.slice(fromIndex, 
        // The `toIndex` should be included in the returned operations, so add `1`.
        toIndex + 1);
    }
    /**
     * Returns operation from the history that bases on given `baseVersion`.
     *
     * @param {Number} baseVersion Base version of the operation to get.
     * @returns {module:engine/model/operation/operation~Operation|undefined} Operation with given base version or `undefined` if
     * there is no such operation in history.
     */
    getOperation(baseVersion) {
        const operationIndex = this._baseVersionToOperationIndex.get(baseVersion);
        if (operationIndex === undefined) {
            return;
        }
        return this._operations[operationIndex];
    }
    /**
     * Marks in history that one operation is an operation that is undoing the other operation. By marking operation this way,
     * history is keeping more context information about operations, which helps in operational transformation.
     *
     * @param {module:engine/model/operation/operation~Operation} undoneOperation Operation which is undone by `undoingOperation`.
     * @param {module:engine/model/operation/operation~Operation} undoingOperation Operation which undoes `undoneOperation`.
     */
    setOperationAsUndone(undoneOperation, undoingOperation) {
        this._undoPairs.set(undoingOperation, undoneOperation);
        this._undoneOperations.add(undoneOperation);
    }
    /**
     * Checks whether given `operation` is undoing any other operation.
     *
     * @param {module:engine/model/operation/operation~Operation} operation Operation to check.
     * @returns {Boolean} `true` if given `operation` is undoing any other operation, `false` otherwise.
     */
    isUndoingOperation(operation) {
        return this._undoPairs.has(operation);
    }
    /**
     * Checks whether given `operation` has been undone by any other operation.
     *
     * @param {module:engine/model/operation/operation~Operation} operation Operation to check.
     * @returns {Boolean} `true` if given `operation` has been undone any other operation, `false` otherwise.
     */
    isUndoneOperation(operation) {
        return this._undoneOperations.has(operation);
    }
    /**
     * For given `undoingOperation`, returns the operation which has been undone by it.
     *
     * @param {module:engine/model/operation/operation~Operation} undoingOperation
     * @returns {module:engine/model/operation/operation~Operation|undefined} Operation that has been undone by given
     * `undoingOperation` or `undefined` if given `undoingOperation` is not undoing any other operation.
     */
    getUndoneOperation(undoingOperation) {
        return this._undoPairs.get(undoingOperation);
    }
    /**
     * Resets the history of operations.
     */
    reset() {
        this._version = 0;
        this._undoPairs = new Map();
        this._operations = [];
        this._undoneOperations = new Set();
        this._gaps = new Map();
        this._baseVersionToOperationIndex = new Map();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/rootelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/rootelement
 */

/**
 * Type of {@link module:engine/model/element~Element} that is a root of a model tree.
 * @extends module:engine/model/element~Element
 */
class RootElement extends element_Element {
    /**
     * Creates root element.
     *
     * @param {module:engine/model/document~Document} document Document that is an owner of this root.
     * @param {String} name Node name.
     * @param {String} [rootName='main'] Unique root name used to identify this root
     * element by {@link module:engine/model/document~Document}.
     */
    constructor(document, name, rootName = 'main') {
        super(name);
        /**
         * Document that is an owner of this root.
         *
         * @private
         * @member {module:engine/model/document~Document}
         */
        this._document = document;
        /**
         * Unique root name used to identify this root element by {@link module:engine/model/document~Document}.
         *
         * @readonly
         * @member {String}
         */
        this.rootName = rootName;
    }
    /**
     * {@link module:engine/model/document~Document Document} that owns this root element.
     *
     * @readonly
     * @type {module:engine/model/document~Document|null}
     */
    get document() {
        return this._document;
    }
    /**
     * Converts `RootElement` instance to `String` containing it's name.
     *
     * @returns {String} `RootElement` instance converted to `String`.
     */
    toJSON() {
        return this.rootName;
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		rootElement.is( 'rootElement' ); // -> true
 *		rootElement.is( 'element' ); // -> true
 *		rootElement.is( 'node' ); // -> true
 *		rootElement.is( 'model:rootElement' ); // -> true
 *		rootElement.is( 'model:element' ); // -> true
 *		rootElement.is( 'model:node' ); // -> true
 *
 *		rootElement.is( 'view:element' ); // -> false
 *		rootElement.is( 'documentFragment' ); // -> false
 *
 * Assuming that the object being checked is an element, you can also check its
 * {@link module:engine/model/element~Element#name name}:
 *
 *		rootElement.is( 'rootElement', '$root' ); // -> same as above
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type Type to check.
 * @param {String} [name] Element name.
 * @returns {Boolean}
 */
RootElement.prototype.is = function (type, name) {
    if (!name) {
        return type === 'rootElement' || type === 'model:rootElement' ||
            // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
            type === 'element' || type === 'model:element' ||
            type === 'node' || type === 'model:node';
    }
    return name === this.name && (type === 'rootElement' || type === 'model:rootElement' ||
        // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
        type === 'element' || type === 'model:element');
};

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/unicode.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Set of utils to handle unicode characters.
 *
 * @module utils/unicode
 */
/**
 * Checks whether given `character` is a combining mark.
 *
 * @param {String} character Character to check.
 * @returns {Boolean}
 */
function isCombiningMark(character) {
    // eslint-disable-next-line no-misleading-character-class
    return !!character && character.length == 1 && /[\u0300-\u036f\u1ab0-\u1aff\u1dc0-\u1dff\u20d0-\u20ff\ufe20-\ufe2f]/.test(character);
}
/**
 * Checks whether given `character` is a high half of surrogate pair.
 *
 * Using UTF-16 terminology, a surrogate pair denotes UTF-16 character using two UTF-8 characters. The surrogate pair
 * consist of high surrogate pair character followed by low surrogate pair character.
 *
 * @param {String} character Character to check.
 * @returns {Boolean}
 */
function isHighSurrogateHalf(character) {
    return !!character && character.length == 1 && /[\ud800-\udbff]/.test(character);
}
/**
 * Checks whether given `character` is a low half of surrogate pair.
 *
 * Using UTF-16 terminology, a surrogate pair denotes UTF-16 character using two UTF-8 characters. The surrogate pair
 * consist of high surrogate pair character followed by low surrogate pair character.
 *
 * @param {String} character Character to check.
 * @returns {Boolean}
 */
function isLowSurrogateHalf(character) {
    return !!character && character.length == 1 && /[\udc00-\udfff]/.test(character);
}
/**
 * Checks whether given offset in a string is inside a surrogate pair (between two surrogate halves).
 *
 * @param {String} string String to check.
 * @param {Number} offset Offset to check.
 * @returns {Boolean}
 */
function isInsideSurrogatePair(string, offset) {
    return isHighSurrogateHalf(string.charAt(offset - 1)) && isLowSurrogateHalf(string.charAt(offset));
}
/**
 * Checks whether given offset in a string is between base character and combining mark or between two combining marks.
 *
 * @param {String} string String to check.
 * @param {Number} offset Offset to check.
 * @returns {Boolean}
 */
function isInsideCombinedSymbol(string, offset) {
    return isCombiningMark(string.charAt(offset));
}
const EMOJI_PATTERN = buildEmojiRegexp();
/**
 * Checks whether given offset in a string is inside multi-character emoji sequence.
 *
 * @param {String} string String to check.
 * @param {Number} offset Offset to check.
 * @returns {Boolean}
 */
function isInsideEmojiSequence(string, offset) {
    const matches = String(string).matchAll(EMOJI_PATTERN);
    return Array.from(matches).some(match => match.index < offset && offset < match.index + match[0].length);
}
function buildEmojiRegexp() {
    const parts = [
        // Emoji Tag Sequence (ETS)
        /\p{Emoji}[\u{E0020}-\u{E007E}]+\u{E007F}/u,
        // Emoji Keycap Sequence
        /\p{Emoji}\u{FE0F}?\u{20E3}/u,
        // Emoji Presentation Sequence
        /\p{Emoji}\u{FE0F}/u,
        // Single-Character Emoji / Emoji Modifier Sequence
        /(?=\p{General_Category=Other_Symbol})\p{Emoji}\p{Emoji_Modifier}*/u
    ];
    const flagSequence = /\p{Regional_Indicator}{2}/u.source;
    const emoji = '(?:' + parts.map(part => part.source).join('|') + ')';
    const sequence = `${flagSequence}|${emoji}(?:\u{200D}${emoji})*`;
    return new RegExp(sequence, 'ug');
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/document.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/document
 */









// @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' );
const graveyardName = '$graveyard';
/**
 * Data model's document. It contains the model's structure, its selection and the history of changes.
 *
 * Read more about working with the model in
 * {@glink framework/guides/architecture/editing-engine#model introduction to the the editing engine's architecture}.
 *
 * Usually, the document contains just one {@link module:engine/model/document~Document#roots root element}, so
 * you can retrieve it by just calling {@link module:engine/model/document~Document#getRoot} without specifying its name:
 *
 *		model.document.getRoot(); // -> returns the main root
 *
 * However, the document may contain multiple roots – e.g. when the editor has multiple editable areas
 * (e.g. a title and a body of a message).
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class document_Document extends Emitter {
    /**
     * Creates an empty document instance with no {@link #roots} (other than
     * the {@link #graveyard graveyard root}).
     */
    constructor(model) {
        super();
        /**
         * The {@link module:engine/model/model~Model model} that the document is a part of.
         *
         * @readonly
         * @type {module:engine/model/model~Model}
         */
        this.model = model;
        /**
         * The document's history.
         *
         * @readonly
         * @type {module:engine/model/history~History}
         */
        this.history = new History();
        /**
         * The selection in this document.
         *
         * @readonly
         * @type {module:engine/model/documentselection~DocumentSelection}
         */
        this.selection = new documentselection_DocumentSelection(this);
        /**
         * A list of roots that are owned and managed by this document. Use {@link #createRoot} and
         * {@link #getRoot} to manipulate it.
         *
         * @readonly
         * @type {module:utils/collection~Collection}
         */
        this.roots = new Collection({ idProperty: 'rootName' });
        /**
         * The model differ object. Its role is to buffer changes done on the model document and then calculate a diff of those changes.
         *
         * @readonly
         * @type {module:engine/model/differ~Differ}
         */
        this.differ = new Differ(model.markers);
        /**
         * Post-fixer callbacks registered to the model document.
         *
         * @private
         * @type {Set.<Function>}
         */
        this._postFixers = new Set();
        /**
         * A boolean indicates whether the selection has changed until
         *
         * @private
         * @type {Boolean}
         */
        this._hasSelectionChangedFromTheLastChangeBlock = false;
        // Graveyard tree root. Document always have a graveyard root, which stores removed nodes.
        this.createRoot('$root', graveyardName);
        // Then, still before an operation is applied on model, buffer the change in differ.
        this.listenTo(model, 'applyOperation', (evt, args) => {
            const operation = args[0];
            if (operation.isDocumentOperation) {
                this.differ.bufferOperation(operation);
            }
        }, { priority: 'high' });
        // After the operation is applied, bump document's version and add the operation to the history.
        this.listenTo(model, 'applyOperation', (evt, args) => {
            const operation = args[0];
            if (operation.isDocumentOperation) {
                this.history.addOperation(operation);
            }
        }, { priority: 'low' });
        // Listen to selection changes. If selection changed, mark it.
        this.listenTo(this.selection, 'change', () => {
            this._hasSelectionChangedFromTheLastChangeBlock = true;
        });
        // Buffer marker changes.
        // This is not covered in buffering operations because markers may change outside of them (when they
        // are modified using `model.markers` collection, not through `MarkerOperation`).
        this.listenTo(model.markers, 'update', (evt, marker, oldRange, newRange, oldMarkerData) => {
            // Copy the `newRange` to the new marker data as during the marker removal the range is not updated.
            const newMarkerData = { ...marker.getData(), range: newRange };
            // Whenever marker is updated, buffer that change.
            this.differ.bufferMarkerChange(marker.name, oldMarkerData, newMarkerData);
            if (oldRange === null) {
                // If this is a new marker, add a listener that will buffer change whenever marker changes.
                marker.on('change', (evt, oldRange) => {
                    const markerData = marker.getData();
                    this.differ.bufferMarkerChange(marker.name, { ...markerData, range: oldRange }, markerData);
                });
            }
        });
    }
    /**
     * The document version. Every applied operation increases the version number. It is used to
     * ensure that operations are applied on a proper document version.
     *
     * This property is equal to {@link module:engine/model/history~History#version `model.Document#history#version`}.
     *
     * If the {@link module:engine/model/operation/operation~Operation#baseVersion base version} does not match the document version,
     * a {@link module:utils/ckeditorerror~CKEditorError model-document-applyoperation-wrong-version} error is thrown.
     *
     * @type {Number}
     */
    get version() {
        return this.history.version;
    }
    set version(version) {
        this.history.version = version;
    }
    /**
     * The graveyard tree root. A document always has a graveyard root that stores removed nodes.
     *
     * @readonly
     * @member {module:engine/model/rootelement~RootElement}
     */
    get graveyard() {
        return this.getRoot(graveyardName);
    }
    /**
     * Creates a new root.
     *
     * @param {String} [elementName='$root'] The element name. Defaults to `'$root'` which also has some basic schema defined
     * (`$block`s are allowed inside the `$root`). Make sure to define a proper schema if you use a different name.
     * @param {String} [rootName='main'] A unique root name.
     * @returns {module:engine/model/rootelement~RootElement} The created root.
     */
    createRoot(elementName = '$root', rootName = 'main') {
        if (this.roots.get(rootName)) {
            /**
             * A root with the specified name already exists.
             *
             * @error model-document-createroot-name-exists
             * @param {module:engine/model/document~Document} doc
             * @param {String} name
             */
            throw new CKEditorError('model-document-createroot-name-exists', this, { name: rootName });
        }
        const root = new RootElement(this, elementName, rootName);
        this.roots.add(root);
        return root;
    }
    /**
     * Removes all event listeners set by the document instance.
     */
    destroy() {
        this.selection.destroy();
        this.stopListening();
    }
    /**
     * Returns a root by its name.
     *
     * @param {String} [name='main'] A unique root name.
     * @returns {module:engine/model/rootelement~RootElement|null} The root registered under a given name or `null` when
     * there is no root with the given name.
     */
    getRoot(name = 'main') {
        return this.roots.get(name);
    }
    /**
     * Returns an array with names of all roots (without the {@link #graveyard}) added to the document.
     *
     * @returns {Array.<String>} Roots names.
     */
    getRootNames() {
        return Array.from(this.roots, root => root.rootName).filter(name => name != graveyardName);
    }
    /**
     * Used to register a post-fixer callback. A post-fixer mechanism guarantees that the features
     * will operate on a correct model state.
     *
     * An execution of a feature may lead to an incorrect document tree state. The callbacks are used to fix the document tree after
     * it has changed. Post-fixers are fired just after all changes from the outermost change block were applied but
     * before the {@link module:engine/model/document~Document#event:change change event} is fired. If a post-fixer callback made
     * a change, it should return `true`. When this happens, all post-fixers are fired again to check if something else should
     * not be fixed in the new document tree state.
     *
     * As a parameter, a post-fixer callback receives a {@link module:engine/model/writer~Writer writer} instance connected with the
     * executed changes block. Thanks to that, all changes done by the callback will be added to the same
     * {@link module:engine/model/batch~Batch batch} (and undo step) as the original changes. This makes post-fixer changes transparent
     * for the user.
     *
     * An example of a post-fixer is a callback that checks if all the data were removed from the editor. If so, the
     * callback should add an empty paragraph so that the editor is never empty:
     *
     *		document.registerPostFixer( writer => {
     *			const changes = document.differ.getChanges();
     *
     *			// Check if the changes lead to an empty root in the editor.
     *			for ( const entry of changes ) {
     *				if ( entry.type == 'remove' && entry.position.root.isEmpty ) {
     *					writer.insertElement( 'paragraph', entry.position.root, 0 );
     *
     *					// It is fine to return early, even if multiple roots would need to be fixed.
     *					// All post-fixers will be fired again, so if there are more empty roots, those will be fixed, too.
     *					return true;
     *				}
     *			}
     *
     *			return false;
     *		} );
     *
     * @param {Function} postFixer
     */
    registerPostFixer(postFixer) {
        this._postFixers.add(postFixer);
    }
    /**
     * A custom `toJSON()` method to solve child-parent circular dependencies.
     *
     * @returns {Object} A clone of this object with the document property changed to a string.
     */
    toJSON() {
        const json = lodash_es_clone(this);
        // Due to circular references we need to remove parent reference.
        json.selection = '[engine.model.DocumentSelection]';
        json.model = '[engine.model.Model]';
        return json;
    }
    /**
     * Check if there were any changes done on document, and if so, call post-fixers,
     * fire `change` event for features and conversion and then reset the differ.
     * Fire `change:data` event when at least one operation or buffered marker changes the data.
     *
     * @internal
     * @protected
     * @fires change
     * @fires change:data
     * @param {module:engine/model/writer~Writer} writer The writer on which post-fixers will be called.
     */
    _handleChangeBlock(writer) {
        if (this._hasDocumentChangedFromTheLastChangeBlock()) {
            this._callPostFixers(writer);
            // Refresh selection attributes according to the final position in the model after the change.
            this.selection.refresh();
            if (this.differ.hasDataChanges()) {
                this.fire('change:data', writer.batch);
            }
            else {
                this.fire('change', writer.batch);
            }
            // Theoretically, it is not necessary to refresh selection after change event because
            // post-fixers are the last who should change the model, but just in case...
            this.selection.refresh();
            this.differ.reset();
        }
        this._hasSelectionChangedFromTheLastChangeBlock = false;
    }
    /**
     * Returns whether there is a buffered change or if the selection has changed from the last
     * {@link module:engine/model/model~Model#enqueueChange `enqueueChange()` block}
     * or {@link module:engine/model/model~Model#change `change()` block}.
     *
     * @protected
     * @returns {Boolean} Returns `true` if document has changed from the last `change()` or `enqueueChange()` block.
     */
    _hasDocumentChangedFromTheLastChangeBlock() {
        return !this.differ.isEmpty || this._hasSelectionChangedFromTheLastChangeBlock;
    }
    /**
     * Returns the default root for this document which is either the first root that was added to the document using
     * {@link #createRoot} or the {@link #graveyard graveyard root} if no other roots were created.
     *
     * @protected
     * @returns {module:engine/model/rootelement~RootElement} The default root for this document.
     */
    _getDefaultRoot() {
        for (const root of this.roots) {
            if (root !== this.graveyard) {
                return root;
            }
        }
        return this.graveyard;
    }
    /**
     * Returns the default range for this selection. The default range is a collapsed range that starts and ends
     * at the beginning of this selection's document {@link #_getDefaultRoot default root}.
     *
     * @internal
     * @protected
     * @returns {module:engine/model/range~Range}
     */
    _getDefaultRange() {
        const defaultRoot = this._getDefaultRoot();
        const model = this.model;
        const schema = model.schema;
        // Find the first position where the selection can be put.
        const position = model.createPositionFromPath(defaultRoot, [0]);
        const nearestRange = schema.getNearestSelectionRange(position);
        // If valid selection range is not found - return range collapsed at the beginning of the root.
        return nearestRange || model.createRange(position);
    }
    /**
     * Checks whether a given {@link module:engine/model/range~Range range} is a valid range for
     * the {@link #selection document's selection}.
     *
     * @internal
     * @protected
     * @param {module:engine/model/range~Range} range A range to check.
     * @returns {Boolean} `true` if `range` is valid, `false` otherwise.
     */
    _validateSelectionRange(range) {
        return validateTextNodePosition(range.start) && validateTextNodePosition(range.end);
    }
    /**
     * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model.
     *
     * @private
     * @param {module:engine/model/writer~Writer} writer The writer on which post-fixer callbacks will be called.
     */
    _callPostFixers(writer) {
        let wasFixed = false;
        do {
            for (const callback of this._postFixers) {
                // Ensure selection attributes are up to date before each post-fixer.
                // https://github.com/ckeditor/ckeditor5-engine/issues/1673.
                //
                // It might be good to refresh the selection after each operation but at the moment it leads
                // to losing attributes for composition or and spell checking
                // https://github.com/ckeditor/ckeditor5-typing/issues/188
                this.selection.refresh();
                wasFixed = callback(writer);
                if (wasFixed) {
                    break;
                }
            }
        } while (wasFixed);
    }
}
// Checks whether given range boundary position is valid for document selection, meaning that is not between
// unicode surrogate pairs or base character and combining marks.
function validateTextNodePosition(rangeBoundary) {
    const textNode = rangeBoundary.textNode;
    if (textNode) {
        const data = textNode.data;
        const offset = rangeBoundary.offset - textNode.startOffset;
        return !isInsideSurrogatePair(data, offset) && !isInsideCombinedSymbol(data, offset);
    }
    return true;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/markercollection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/model/markercollection
 */




/**
 * The collection of all {@link module:engine/model/markercollection~Marker markers} attached to the document.
 * It lets you {@link module:engine/model/markercollection~MarkerCollection#get get} markers or track them using
 * {@link module:engine/model/markercollection~MarkerCollection#event:update} event.
 *
 * To create, change or remove makers use {@link module:engine/model/writer~Writer model writers'} methods:
 * {@link module:engine/model/writer~Writer#addMarker} or {@link module:engine/model/writer~Writer#removeMarker}. Since
 * the writer is the only proper way to change the data model it is not possible to change markers directly using this
 * collection. All markers created by the writer will be automatically added to this collection.
 *
 * By default there is one marker collection available as {@link module:engine/model/model~Model#markers model property}.
 *
 * @see module:engine/model/markercollection~Marker
 */
class MarkerCollection extends Emitter {
    /**
     * Creates a markers collection.
     */
    constructor() {
        super();
        /**
         * Stores {@link ~Marker markers} added to the collection.
         *
         * @private
         * @member {Map} #_markers
         */
        this._markers = new Map();
    }
    /**
     * Iterable interface.
     *
     * Iterates over all {@link ~Marker markers} added to the collection.
     *
     * @returns {Iterator}
     */
    [Symbol.iterator]() {
        return this._markers.values();
    }
    /**
     * Checks if given {@link ~Marker marker} or marker name is in the collection.
     *
     * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of marker or marker instance to check.
     * @returns {Boolean} `true` if marker is in the collection, `false` otherwise.
     */
    has(markerOrName) {
        const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
        return this._markers.has(markerName);
    }
    /**
     * Returns {@link ~Marker marker} with given `markerName`.
     *
     * @param {String} markerName Name of marker to get.
     * @returns {module:engine/model/markercollection~Marker|null} Marker with given name or `null` if such marker was
     * not added to the collection.
     */
    get(markerName) {
        return this._markers.get(markerName) || null;
    }
    /**
     * Creates and adds a {@link ~Marker marker} to the `MarkerCollection` with given name on given
     * {@link module:engine/model/range~Range range}.
     *
     * If `MarkerCollection` already had a marker with given name (or {@link ~Marker marker} was passed), the marker in
     * collection is updated and {@link module:engine/model/markercollection~MarkerCollection#event:update} event is fired
     * but only if there was a change (marker range or {@link module:engine/model/markercollection~Marker#managedUsingOperations}
     * flag has changed.
     *
     * @internal
     * @protected
     * @fires module:engine/model/markercollection~MarkerCollection#event:update
     * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of marker to set or marker instance to update.
     * @param {module:engine/model/range~Range} range Marker range.
     * @param {Boolean} [managedUsingOperations=false] Specifies whether the marker is managed using operations.
     * @param {Boolean} [affectsData=false] Specifies whether the marker affects the data produced by the data pipeline
     * (is persisted in the editor's data).
     * @returns {module:engine/model/markercollection~Marker} `Marker` instance which was added or updated.
     */
    _set(markerOrName, range, managedUsingOperations = false, affectsData = false) {
        const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
        if (markerName.includes(',')) {
            /**
             * Marker name cannot contain the "," character.
             *
             * @error markercollection-incorrect-marker-name
             */
            throw new CKEditorError('markercollection-incorrect-marker-name', this);
        }
        const oldMarker = this._markers.get(markerName);
        if (oldMarker) {
            const oldMarkerData = oldMarker.getData();
            const oldRange = oldMarker.getRange();
            let hasChanged = false;
            if (!oldRange.isEqual(range)) {
                oldMarker._attachLiveRange(LiveRange.fromRange(range));
                hasChanged = true;
            }
            if (managedUsingOperations != oldMarker.managedUsingOperations) {
                oldMarker._managedUsingOperations = managedUsingOperations;
                hasChanged = true;
            }
            if (typeof affectsData === 'boolean' && affectsData != oldMarker.affectsData) {
                oldMarker._affectsData = affectsData;
                hasChanged = true;
            }
            if (hasChanged) {
                this.fire(`update:${markerName}`, oldMarker, oldRange, range, oldMarkerData);
            }
            return oldMarker;
        }
        const liveRange = LiveRange.fromRange(range);
        const marker = new Marker(markerName, liveRange, managedUsingOperations, affectsData);
        this._markers.set(markerName, marker);
        this.fire(`update:${markerName}`, marker, null, range, { ...marker.getData(), range: null });
        return marker;
    }
    /**
     * Removes given {@link ~Marker marker} or a marker with given name from the `MarkerCollection`.
     *
     * @internal
     * @protected
     * @fires module:engine/model/markercollection~MarkerCollection#event:update
     * @param {String|module:engine/model/markercollection~Marker} markerOrName Marker or name of a marker to remove.
     * @returns {Boolean} `true` if marker was found and removed, `false` otherwise.
     */
    _remove(markerOrName) {
        const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
        const oldMarker = this._markers.get(markerName);
        if (oldMarker) {
            this._markers.delete(markerName);
            this.fire(`update:${markerName}`, oldMarker, oldMarker.getRange(), null, oldMarker.getData());
            this._destroyMarker(oldMarker);
            return true;
        }
        return false;
    }
    /**
     * Fires an {@link module:engine/model/markercollection~MarkerCollection#event:update} event for the given {@link ~Marker marker}
     * but does not change the marker. Useful to force {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast
     * conversion} for the marker.
     *
     * @internal
     * @protected
     * @fires module:engine/model/markercollection~MarkerCollection#event:update
     * @param {String|module:engine/model/markercollection~Marker} markerOrName Marker or name of a marker to refresh.
     */
    _refresh(markerOrName) {
        const markerName = markerOrName instanceof Marker ? markerOrName.name : markerOrName;
        const marker = this._markers.get(markerName);
        if (!marker) {
            /**
             * Marker with provided name does not exists.
             *
             * @error markercollection-refresh-marker-not-exists
             */
            throw new CKEditorError('markercollection-refresh-marker-not-exists', this);
        }
        const range = marker.getRange();
        this.fire(`update:${markerName}`, marker, range, range, marker.getData());
    }
    /**
     * Returns iterator that iterates over all markers, which ranges contain given {@link module:engine/model/position~Position position}.
     *
     * @param {module:engine/model/position~Position} position
     * @returns {Iterable.<module:engine/model/markercollection~Marker>}
     */
    *getMarkersAtPosition(position) {
        for (const marker of this) {
            if (marker.getRange().containsPosition(position)) {
                yield marker;
            }
        }
    }
    /**
     * Returns iterator that iterates over all markers, which intersects with given {@link module:engine/model/range~Range range}.
     *
     * @param {module:engine/model/range~Range} range
     * @returns {Iterable.<module:engine/model/markercollection~Marker>}
     */
    *getMarkersIntersectingRange(range) {
        for (const marker of this) {
            if (marker.getRange().getIntersection(range) !== null) {
                yield marker;
            }
        }
    }
    /**
     * Destroys marker collection and all markers inside it.
     */
    destroy() {
        for (const marker of this._markers.values()) {
            this._destroyMarker(marker);
        }
        this._markers = null;
        this.stopListening();
    }
    /**
     * Iterates over all markers that starts with given `prefix`.
     *
     *		const markerFooA = markersCollection.set( 'foo:a', rangeFooA );
     *		const markerFooB = markersCollection.set( 'foo:b', rangeFooB );
     *		const markerBarA = markersCollection.set( 'bar:a', rangeBarA );
     *		const markerFooBarA = markersCollection.set( 'foobar:a', rangeFooBarA );
     *		Array.from( markersCollection.getMarkersGroup( 'foo' ) ); // [ markerFooA, markerFooB ]
     *		Array.from( markersCollection.getMarkersGroup( 'a' ) ); // []
     *
     * @param prefix
     * @returns {Iterable.<module:engine/model/markercollection~Marker>}
     */
    *getMarkersGroup(prefix) {
        for (const marker of this._markers.values()) {
            if (marker.name.startsWith(prefix + ':')) {
                yield marker;
            }
        }
    }
    /**
     * Destroys the marker.
     *
     * @private
     * @param {module:engine/model/markercollection~Marker} marker Marker to destroy.
     */
    _destroyMarker(marker) {
        marker.stopListening();
        marker._detachLiveRange();
    }
}
/**
 * `Marker` is a continuous parts of model (like a range), is named and represent some kind of information about marked
 * part of model document. In contrary to {@link module:engine/model/node~Node nodes}, which are building blocks of
 * model document tree, markers are not stored directly in document tree but in
 * {@link module:engine/model/model~Model#markers model markers' collection}. Still, they are document data, by giving
 * additional meaning to the part of a model document between marker start and marker end.
 *
 * In this sense, markers are similar to adding and converting attributes on nodes. The difference is that attribute is
 * connected with a given node (e.g. a character is bold no matter if it gets moved or content around it changes).
 * Markers on the other hand are continuous ranges and are characterized by their start and end position. This means that
 * any character in the marker is marked by the marker. For example, if a character is moved outside of marker it stops being
 * "special" and the marker is shrunk. Similarly, when a character is moved into the marker from other place in document
 * model, it starts being "special" and the marker is enlarged.
 *
 * Another upside of markers is that finding marked part of document is fast and easy. Using attributes to mark some nodes
 * and then trying to find that part of document would require traversing whole document tree. Marker gives instant access
 * to the range which it is marking at the moment.
 *
 * Markers are built from a name and a range.
 *
 * Range of the marker is updated automatically when document changes, using
 * {@link module:engine/model/liverange~LiveRange live range} mechanism.
 *
 * Name is used to group and identify markers. Names have to be unique, but markers can be grouped by
 * using common prefixes, separated with `:`, for example: `user:john` or `search:3`. That's useful in term of creating
 * namespaces for custom elements (e.g. comments, highlights). You can use this prefixes in
 * {@link module:engine/model/markercollection~MarkerCollection#event:update} listeners to listen on changes in a group of markers.
 * For instance: `model.markers.on( 'update:user', callback );` will be called whenever any `user:*` markers changes.
 *
 * There are two types of markers.
 *
 * 1. Markers managed directly, without using operations. They are added directly by {@link module:engine/model/writer~Writer}
 * to the {@link module:engine/model/markercollection~MarkerCollection} without any additional mechanism. They can be used
 * as bookmarks or visual markers. They are great for showing results of the find, or select link when the focus is in the input.
 *
 * 1. Markers managed using operations. These markers are also stored in {@link module:engine/model/markercollection~MarkerCollection}
 * but changes in these markers is managed the same way all other changes in the model structure - using operations.
 * Therefore, they are handled in the undo stack and synchronized between clients if the collaboration plugin is enabled.
 * This type of markers is useful for solutions like spell checking or comments.
 *
 * Both type of them should be added / updated by {@link module:engine/model/writer~Writer#addMarker}
 * and removed by {@link module:engine/model/writer~Writer#removeMarker} methods.
 *
 *		model.change( ( writer ) => {
 * 			const marker = writer.addMarker( name, { range, usingOperation: true } );
 *
 * 			// ...
 *
 * 			writer.removeMarker( marker );
 *		} );
 *
 * See {@link module:engine/model/writer~Writer} to find more examples.
 *
 * Since markers need to track change in the document, for efficiency reasons, it is best to create and keep as little
 * markers as possible and remove them as soon as they are not needed anymore.
 *
 * Markers can be downcasted and upcasted.
 *
 * Markers downcast happens on {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker} and
 * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:removeMarker} events.
 * Use {@link module:engine/conversion/downcasthelpers downcast converters} or attach a custom converter to mentioned events.
 * For {@link module:engine/controller/datacontroller~DataController data pipeline}, marker should be downcasted to an element.
 * Then, it can be upcasted back to a marker. Again, use {@link module:engine/conversion/upcasthelpers upcast converters} or
 * attach a custom converter to {@link module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element}.
 *
 * `Marker` instances are created and destroyed only by {@link ~MarkerCollection MarkerCollection}.
 */
class Marker extends EmitterMixin(typecheckable_TypeCheckable) {
    /**
     * Creates a marker instance.
     *
     * @param {String} name Marker name.
     * @param {module:engine/model/liverange~LiveRange} liveRange Range marked by the marker.
     * @param {Boolean} managedUsingOperations Specifies whether the marker is managed using operations.
     * @param {Boolean} affectsData Specifies whether the marker affects the data produced by the data pipeline
     * (is persisted in the editor's data).
     */
    constructor(name, liveRange, managedUsingOperations, affectsData) {
        super();
        /**
         * Marker's name.
         *
         * @readonly
         * @type {String}
         */
        this.name = name;
        /**
         * Range marked by the marker.
         *
         * @protected
         * @member {module:engine/model/liverange~LiveRange}
         */
        this._liveRange = this._attachLiveRange(liveRange);
        /**
         * Flag indicates if the marker is managed using operations or not.
         *
         * @private
         * @member {Boolean}
         */
        this._managedUsingOperations = managedUsingOperations;
        /**
         * Specifies whether the marker affects the data produced by the data pipeline
         * (is persisted in the editor's data).
         *
         * @private
         * @member {Boolean}
         */
        this._affectsData = affectsData;
    }
    /**
     * A value indicating if the marker is managed using operations.
     * See {@link ~Marker marker class description} to learn more about marker types.
     * See {@link module:engine/model/writer~Writer#addMarker}.
     *
     * @returns {Boolean}
     */
    get managedUsingOperations() {
        if (!this._liveRange) {
            throw new CKEditorError('marker-destroyed', this);
        }
        return this._managedUsingOperations;
    }
    /**
     * A value indicating if the marker changes the data.
     *
     * @returns {Boolean}
     */
    get affectsData() {
        if (!this._liveRange) {
            throw new CKEditorError('marker-destroyed', this);
        }
        return this._affectsData;
    }
    /**
     * Returns the marker data (properties defining the marker).
     *
     * @returns {module:engine/model/markercollection~MarkerData}
     */
    getData() {
        return {
            range: this.getRange(),
            affectsData: this.affectsData,
            managedUsingOperations: this.managedUsingOperations
        };
    }
    /**
     * Returns current marker start position.
     *
     * @returns {module:engine/model/position~Position}
     */
    getStart() {
        if (!this._liveRange) {
            throw new CKEditorError('marker-destroyed', this);
        }
        return this._liveRange.start.clone();
    }
    /**
     * Returns current marker end position.
     *
     * @returns {module:engine/model/position~Position}
     */
    getEnd() {
        if (!this._liveRange) {
            throw new CKEditorError('marker-destroyed', this);
        }
        return this._liveRange.end.clone();
    }
    /**
     * Returns a range that represents the current state of the marker.
     *
     * Keep in mind that returned value is a {@link module:engine/model/range~Range Range}, not a
     * {@link module:engine/model/liverange~LiveRange LiveRange}. This means that it is up-to-date and relevant only
     * until next model document change. Do not store values returned by this method. Instead, store {@link ~Marker#name}
     * and get `Marker` instance from {@link module:engine/model/markercollection~MarkerCollection MarkerCollection} every
     * time there is a need to read marker properties. This will guarantee that the marker has not been removed and
     * that it's data is up-to-date.
     *
     * @returns {module:engine/model/range~Range}
     */
    getRange() {
        if (!this._liveRange) {
            throw new CKEditorError('marker-destroyed', this);
        }
        return this._liveRange.toRange();
    }
    /**
     * Binds new live range to the marker and detach the old one if is attached.
     *
     * @internal
     * @protected
     * @param {module:engine/model/liverange~LiveRange} liveRange Live range to attach
     * @returns {module:engine/model/liverange~LiveRange} Attached live range.
     */
    _attachLiveRange(liveRange) {
        if (this._liveRange) {
            this._detachLiveRange();
        }
        // Delegating does not work with namespaces. Alternatively, we could delegate all events (using `*`).
        liveRange.delegate('change:range').to(this);
        liveRange.delegate('change:content').to(this);
        this._liveRange = liveRange;
        return liveRange;
    }
    /**
     * Unbinds and destroys currently attached live range.
     *
     * @internal
     * @protected
     */
    _detachLiveRange() {
        this._liveRange.stopDelegating('change:range', this);
        this._liveRange.stopDelegating('change:content', this);
        this._liveRange.detach();
        this._liveRange = null;
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		marker.is( 'marker' ); // -> true
 *		marker.is( 'model:marker' ); // -> true
 *
 *		marker.is( 'view:element' ); // -> false
 *		marker.is( 'documentSelection' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
Marker.prototype.is = function (type) {
    return type === 'marker' || type === 'model:marker';
};
/**
 * Cannot use a {@link module:engine/model/markercollection~MarkerCollection#destroy destroyed marker} instance.
 *
 * @error marker-destroyed
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/operation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/operation
 */
/**
 * Abstract base operation class.
 *
 * @abstract
 */
class Operation {
    /**
     * Base operation constructor.
     *
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(baseVersion) {
        /**
         * {@link module:engine/model/document~Document#version} on which operation can be applied. If you try to
         * {@link module:engine/model/model~Model#applyOperation apply} operation with different base version than the
         * {@link module:engine/model/document~Document#version document version} the
         * {@link module:utils/ckeditorerror~CKEditorError model-document-applyOperation-wrong-version} error is thrown.
         *
         * @member {Number|null}
         */
        this.baseVersion = baseVersion;
        /**
         * Defines whether operation is executed on attached or detached {@link module:engine/model/item~Item items}.
         *
         * @readonly
         * @member {Boolean} #isDocumentOperation
         */
        this.isDocumentOperation = this.baseVersion !== null;
        /**
         * {@link module:engine/model/batch~Batch Batch} to which the operation is added or `null` if the operation is not
         * added to any batch yet.
         *
         * @member {module:engine/model/batch~Batch|null} #batch
         */
        this.batch = null;
        /**
         * Operation type.
         *
         * @readonly
         * @member {String} #type
         */
        /**
         * Creates and returns an operation that has the same parameters as this operation.
         *
         * @method #clone
         * @returns {module:engine/model/operation/operation~Operation} Clone of this operation.
         */
        /**
         * Creates and returns a reverse operation. Reverse operation when executed right after
         * the original operation will bring back tree model state to the point before the original
         * operation execution. In other words, it reverses changes done by the original operation.
         *
         * Keep in mind that tree model state may change since executing the original operation,
         * so reverse operation will be "outdated". In that case you will need to transform it by
         * all operations that were executed after the original operation.
         *
         * @method #getReversed
         * @returns {module:engine/model/operation/operation~Operation} Reversed operation.
         */
        /**
         * Executes the operation - modifications described by the operation properties will be applied to the model tree.
         *
         * @protected
         * @method #_execute
         */
    }
    /**
     * Checks whether the operation's parameters are correct and the operation can be correctly executed. Throws
     * an error if operation is not valid.
     *
     * @internal
     * @protected
     * @method #_validate
     */
    _validate() {
    }
    /**
     * Custom toJSON method to solve child-parent circular dependencies.
     *
     * @method #toJSON
     * @returns {Object} Clone of this object with the operation property replaced with string.
     */
    toJSON() {
        // This method creates only a shallow copy, all nested objects should be defined separately.
        // See https://github.com/ckeditor/ckeditor5-engine/issues/1477.
        const json = Object.assign({}, this);
        json.__className = this.constructor.className;
        // Remove reference to the parent `Batch` to avoid circular dependencies.
        delete json.batch;
        // Only document operations are shared with other clients so it is not necessary to keep this information.
        delete json.isDocumentOperation;
        return json;
    }
    /**
     * Name of the operation class used for serialization.
     *
     * @type {String}
     */
    static get className() {
        return 'Operation';
    }
    /**
     * Creates Operation object from deserilized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} doc Document on which this operation will be applied.
     * @returns {module:engine/model/operation/operation~Operation}
     */
    static fromJSON(json, document) {
        return new this(json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/utils
 */






/**
 * Contains functions used for composing model tree by {@link module:engine/model/operation/operation~Operation operations}.
 * Those functions are built on top of {@link module:engine/model/node~Node node}, and it's child classes', APIs.
 *
 * @protected
 * @namespace utils
 */
/**
 * Inserts given nodes at given position.
 *
 * @internal
 * @protected
 * @function module:engine/model/operation/utils~utils.insert
 * @param {module:engine/model/position~Position} position Position at which nodes should be inserted.
 * @param {module:engine/model/node~NodeSet} normalizedNodes Nodes to insert.
 * @returns {module:engine/model/range~Range} Range spanning over inserted elements.
 */
function _insert(position, nodes) {
    const normalizedNodes = _normalizeNodes(nodes);
    // We have to count offset before inserting nodes because they can get merged and we would get wrong offsets.
    const offset = normalizedNodes.reduce((sum, node) => sum + node.offsetSize, 0);
    const parent = position.parent;
    // Insertion might be in a text node, we should split it if that's the case.
    _splitNodeAtPosition(position);
    const index = position.index;
    // Insert nodes at given index. After splitting we have a proper index and insertion is between nodes,
    // using basic `Element` API.
    parent._insertChild(index, normalizedNodes);
    // Merge text nodes, if possible. Merging is needed only at points where inserted nodes "touch" "old" nodes.
    _mergeNodesAtIndex(parent, index + normalizedNodes.length);
    _mergeNodesAtIndex(parent, index);
    return new range_Range(position, position.getShiftedBy(offset));
}
/**
 * Removed nodes in given range. Only {@link module:engine/model/range~Range#isFlat flat} ranges are accepted.
 *
 * @internal
 * @protected
 * @function module:engine/model/operation/utils~utils._remove
 * @param {module:engine/model/range~Range} range Range containing nodes to remove.
 * @returns {Array.<module:engine/model/node~Node>}
 */
function _remove(range) {
    if (!range.isFlat) {
        /**
         * Trying to remove a range which starts and ends in different element.
         *
         * @error operation-utils-remove-range-not-flat
         */
        throw new CKEditorError('operation-utils-remove-range-not-flat', this);
    }
    const parent = range.start.parent;
    // Range may be inside text nodes, we have to split them if that's the case.
    _splitNodeAtPosition(range.start);
    _splitNodeAtPosition(range.end);
    // Remove the text nodes using basic `Element` API.
    const removed = parent._removeChildren(range.start.index, range.end.index - range.start.index);
    // Merge text nodes, if possible. After some nodes were removed, node before and after removed range will be
    // touching at the position equal to the removed range beginning. We check merging possibility there.
    _mergeNodesAtIndex(parent, range.start.index);
    return removed;
}
/**
 * Moves nodes in given range to given target position. Only {@link module:engine/model/range~Range#isFlat flat} ranges are accepted.
 *
 * @internal
 * @protected
 * @function module:engine/model/operation/utils~utils.move
 * @param {module:engine/model/range~Range} sourceRange Range containing nodes to move.
 * @param {module:engine/model/position~Position} targetPosition Position to which nodes should be moved.
 * @returns {module:engine/model/range~Range} Range containing moved nodes.
 */
function _move(sourceRange, targetPosition) {
    if (!sourceRange.isFlat) {
        /**
         * Trying to move a range which starts and ends in different element.
         *
         * @error operation-utils-move-range-not-flat
         */
        throw new CKEditorError('operation-utils-move-range-not-flat', this);
    }
    const nodes = _remove(sourceRange);
    // We have to fix `targetPosition` because model changed after nodes from `sourceRange` got removed and
    // that change might have an impact on `targetPosition`.
    targetPosition = targetPosition._getTransformedByDeletion(sourceRange.start, sourceRange.end.offset - sourceRange.start.offset);
    return _insert(targetPosition, nodes);
}
/**
 * Sets given attribute on nodes in given range. The attributes are only set on top-level nodes of the range, not on its children.
 *
 * @internal
 * @protected
 * @function module:engine/model/operation/utils~utils._setAttribute
 * @param {module:engine/model/range~Range} range Range containing nodes that should have the attribute set. Must be a flat range.
 * @param {String} key Key of attribute to set.
 * @param {*} value Attribute value.
 */
function _setAttribute(range, key, value) {
    // Range might start or end in text nodes, so we have to split them.
    _splitNodeAtPosition(range.start);
    _splitNodeAtPosition(range.end);
    // Iterate over all items in the range.
    for (const item of range.getItems({ shallow: true })) {
        // Iterator will return `TextProxy` instances but we know that those text proxies will
        // always represent full text nodes (this is guaranteed thanks to splitting we did before).
        // So, we can operate on those text proxies' text nodes.
        const node = item.is('$textProxy') ? item.textNode : item;
        if (value !== null) {
            node._setAttribute(key, value);
        }
        else {
            node._removeAttribute(key);
        }
        // After attributes changing it may happen that some text nodes can be merged. Try to merge with previous node.
        _mergeNodesAtIndex(node.parent, node.index);
    }
    // Try to merge last changed node with it's previous sibling (not covered by the loop above).
    _mergeNodesAtIndex(range.end.parent, range.end.index);
}
/**
 * Normalizes given object or an array of objects to an array of {@link module:engine/model/node~Node nodes}. See
 * {@link module:engine/model/node~NodeSet NodeSet} for details on how normalization is performed.
 *
 * @protected
 * @function module:engine/model/operation/utils~utils.normalizeNodes
 * @param {module:engine/model/node~NodeSet} nodes Objects to normalize.
 * @returns {Array.<module:engine/model/node~Node>} Normalized nodes.
 */
function _normalizeNodes(nodes) {
    const normalized = [];
    function convert(nodes) {
        if (typeof nodes == 'string') {
            normalized.push(new model_text_Text(nodes));
        }
        else if (nodes instanceof textproxy_TextProxy) {
            normalized.push(new model_text_Text(nodes.data, nodes.getAttributes()));
        }
        else if (nodes instanceof model_node_Node) {
            normalized.push(nodes);
        }
        else if (isIterable(nodes)) {
            for (const node of nodes) {
                convert(node);
            }
        }
        // Skip unrecognized type.
    }
    convert(nodes);
    // Merge text nodes.
    for (let i = 1; i < normalized.length; i++) {
        const node = normalized[i];
        const prev = normalized[i - 1];
        if (node instanceof model_text_Text && prev instanceof model_text_Text && _haveSameAttributes(node, prev)) {
            // Doing this instead changing `prev.data` because `data` is readonly.
            normalized.splice(i - 1, 2, new model_text_Text(prev.data + node.data, prev.getAttributes()));
            i--;
        }
    }
    return normalized;
}
// Checks if nodes before and after given index in given element are {@link module:engine/model/text~Text text nodes} and
// merges them into one node if they have same attributes.
//
// Merging is done by removing two text nodes and inserting a new text node containing data from both merged text nodes.
//
// @private
// @param {module:engine/model/element~Element} element Parent element of nodes to merge.
// @param {Number} index Index between nodes to merge.
function _mergeNodesAtIndex(element, index) {
    const nodeBefore = element.getChild(index - 1);
    const nodeAfter = element.getChild(index);
    // Check if both of those nodes are text objects with same attributes.
    if (nodeBefore && nodeAfter && nodeBefore.is('$text') && nodeAfter.is('$text') && _haveSameAttributes(nodeBefore, nodeAfter)) {
        // Append text of text node after index to the before one.
        const mergedNode = new model_text_Text(nodeBefore.data + nodeAfter.data, nodeBefore.getAttributes());
        // Remove separate text nodes.
        element._removeChildren(index - 1, 2);
        // Insert merged text node.
        element._insertChild(index - 1, mergedNode);
    }
}
// Checks if given position is in a text node, and if so, splits the text node in two text nodes, each of them
// containing a part of original text node.
//
// @private
// @param {module:engine/model/position~Position} position Position at which node should be split.
function _splitNodeAtPosition(position) {
    const textNode = position.textNode;
    const element = position.parent;
    if (textNode) {
        const offsetDiff = position.offset - textNode.startOffset;
        const index = textNode.index;
        element._removeChildren(index, 1);
        const firstPart = new model_text_Text(textNode.data.substr(0, offsetDiff), textNode.getAttributes());
        const secondPart = new model_text_Text(textNode.data.substr(offsetDiff), textNode.getAttributes());
        element._insertChild(index, [firstPart, secondPart]);
    }
}
// Checks whether two given nodes have same attributes.
//
// @private
// @param {module:engine/model/node~Node} nodeA Node to check.
// @param {module:engine/model/node~Node} nodeB Node to check.
// @returns {Boolean} `true` if nodes have same attributes, `false` otherwise.
function _haveSameAttributes(nodeA, nodeB) {
    const iteratorA = nodeA.getAttributes();
    const iteratorB = nodeB.getAttributes();
    for (const attr of iteratorA) {
        if (attr[1] !== nodeB.getAttribute(attr[0])) {
            return false;
        }
        iteratorB.next();
    }
    return iteratorB.next().done;
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/isEqual.js


/**
 * Performs a deep comparison between two values to determine if they are
 * equivalent.
 *
 * **Note:** This method supports comparing arrays, array buffers, booleans,
 * date objects, error objects, maps, numbers, `Object` objects, regexes,
 * sets, strings, symbols, and typed arrays. `Object` objects are compared
 * by their own, not inherited, enumerable properties. Functions and DOM
 * nodes are compared by strict equality, i.e. `===`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Lang
 * @param {*} value The value to compare.
 * @param {*} other The other value to compare.
 * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
 * @example
 *
 * var object = { 'a': 1 };
 * var other = { 'a': 1 };
 *
 * _.isEqual(object, other);
 * // => true
 *
 * object === other;
 * // => false
 */
function isEqual(value, other) {
  return _baseIsEqual(value, other);
}

/* harmony default export */ const lodash_es_isEqual = (isEqual);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/attributeoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/attributeoperation
 */





/**
 * Operation to change nodes' attribute.
 *
 * Using this class you can add, remove or change value of the attribute.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class AttributeOperation extends Operation {
    /**
     * Creates an operation that changes, removes or adds attributes.
     *
     * If only `newValue` is set, attribute will be added on a node. Note that all nodes in operation's range must not
     * have an attribute with the same key as the added attribute.
     *
     * If only `oldValue` is set, then attribute with given key will be removed. Note that all nodes in operation's range
     * must have an attribute with that key added.
     *
     * If both `newValue` and `oldValue` are set, then the operation will change the attribute value. Note that all nodes in
     * operation's ranges must already have an attribute with given key and `oldValue` as value
     *
     * @param {module:engine/model/range~Range} range Range on which the operation should be applied. Must be a flat range.
     * @param {String} key Key of an attribute to change or remove.
     * @param {*} oldValue Old value of the attribute with given key or `null`, if attribute was not set before.
     * @param {*} newValue New value of the attribute with given key or `null`, if operation should remove attribute.
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(range, key, oldValue, newValue, baseVersion) {
        super(baseVersion);
        /**
         * Range on which operation should be applied.
         *
         * @readonly
         * @member {module:engine/model/range~Range}
         */
        this.range = range.clone();
        /**
         * Key of an attribute to change or remove.
         *
         * @readonly
         * @member {String}
         */
        this.key = key;
        /**
         * Old value of the attribute with given key or `null`, if attribute was not set before.
         *
         * @readonly
         * @member {*}
         */
        this.oldValue = oldValue === undefined ? null : oldValue;
        /**
         * New value of the attribute with given key or `null`, if operation should remove attribute.
         *
         * @readonly
         * @member {*}
         */
        this.newValue = newValue === undefined ? null : newValue;
    }
    /**
     * @inheritDoc
     */
    get type() {
        if (this.oldValue === null) {
            return 'addAttribute';
        }
        else if (this.newValue === null) {
            return 'removeAttribute';
        }
        else {
            return 'changeAttribute';
        }
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/attributeoperation~AttributeOperation} Clone of this operation.
     */
    clone() {
        return new AttributeOperation(this.range, this.key, this.oldValue, this.newValue, this.baseVersion);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/attributeoperation~AttributeOperation}
     */
    getReversed() {
        return new AttributeOperation(this.range, this.key, this.newValue, this.oldValue, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.range = this.range.toJSON();
        return json;
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        if (!this.range.isFlat) {
            /**
             * The range to change is not flat.
             *
             * @error attribute-operation-range-not-flat
             */
            throw new CKEditorError('attribute-operation-range-not-flat', this);
        }
        for (const item of this.range.getItems({ shallow: true })) {
            if (this.oldValue !== null && !lodash_es_isEqual(item.getAttribute(this.key), this.oldValue)) {
                /**
                 * Changed node has different attribute value than operation's old attribute value.
                 *
                 * @error attribute-operation-wrong-old-value
                 * @param {module:engine/model/item~Item} item
                 * @param {String} key
                 * @param {*} value
                 */
                throw new CKEditorError('attribute-operation-wrong-old-value', this, { item, key: this.key, value: this.oldValue });
            }
            if (this.oldValue === null && this.newValue !== null && item.hasAttribute(this.key)) {
                /**
                 * The attribute with given key already exists for the given node.
                 *
                 * @error attribute-operation-attribute-exists
                 * @param {module:engine/model/node~Node} node
                 * @param {String} key
                 */
                throw new CKEditorError('attribute-operation-attribute-exists', this, { node: item, key: this.key });
            }
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        // If value to set is same as old value, don't do anything.
        if (!lodash_es_isEqual(this.oldValue, this.newValue)) {
            // Execution.
            _setAttribute(this.range, this.key, this.newValue);
        }
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'AttributeOperation';
    }
    /**
     * Creates `AttributeOperation` object from deserilized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/attributeoperation~AttributeOperation}
     */
    static fromJSON(json, document) {
        return new AttributeOperation(range_Range.fromJSON(json.range, document), json.key, json.oldValue, json.newValue, json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/moveoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/moveoperation
 */






// @if CK_DEBUG_ENGINE // const ModelRange = require( '../range' ).default;
/**
 * Operation to move a range of {@link module:engine/model/item~Item model items}
 * to given {@link module:engine/model/position~Position target position}.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class MoveOperation extends Operation {
    /**
     * Creates a move operation.
     *
     * @param {module:engine/model/position~Position} sourcePosition
     * Position before the first {@link module:engine/model/item~Item model item} to move.
     * @param {Number} howMany Offset size of moved range. Moved range will start from `sourcePosition` and end at
     * `sourcePosition` with offset shifted by `howMany`.
     * @param {module:engine/model/position~Position} targetPosition Position at which moved nodes will be inserted.
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(sourcePosition, howMany, targetPosition, baseVersion) {
        super(baseVersion);
        /**
         * Position before the first {@link module:engine/model/item~Item model item} to move.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/moveoperation~MoveOperation#sourcePosition
         */
        this.sourcePosition = sourcePosition.clone();
        // `'toNext'` because `sourcePosition` is a bit like a start of the moved range.
        this.sourcePosition.stickiness = 'toNext';
        /**
         * Offset size of moved range.
         *
         * @member {Number} module:engine/model/operation/moveoperation~MoveOperation#howMany
         */
        this.howMany = howMany;
        /**
         * Position at which moved nodes will be inserted.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/moveoperation~MoveOperation#targetPosition
         */
        this.targetPosition = targetPosition.clone();
        this.targetPosition.stickiness = 'toNone';
    }
    /**
     * @inheritDoc
     */
    get type() {
        if (this.targetPosition.root.rootName == '$graveyard') {
            return 'remove';
        }
        else if (this.sourcePosition.root.rootName == '$graveyard') {
            return 'reinsert';
        }
        return 'move';
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/moveoperation~MoveOperation} Clone of this operation.
     */
    clone() {
        return new MoveOperation(this.sourcePosition, this.howMany, this.targetPosition, this.baseVersion);
    }
    /**
     * Returns the start position of the moved range after it got moved. This may be different than
     * {@link module:engine/model/operation/moveoperation~MoveOperation#targetPosition} in some cases, i.e. when a range is moved
     * inside the same parent but {@link module:engine/model/operation/moveoperation~MoveOperation#targetPosition targetPosition}
     * is after {@link module:engine/model/operation/moveoperation~MoveOperation#sourcePosition sourcePosition}.
     *
     *		 vv              vv
     *		abcdefg ===> adefbcg
     *		     ^          ^
     *		     targetPos	movedRangeStart
     *		     offset 6	offset 4
     *
     * @returns {module:engine/model/position~Position}
     */
    getMovedRangeStart() {
        return this.targetPosition._getTransformedByDeletion(this.sourcePosition, this.howMany);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/moveoperation~MoveOperation}
     */
    getReversed() {
        const newTargetPosition = this.sourcePosition._getTransformedByInsertion(this.targetPosition, this.howMany);
        return new MoveOperation(this.getMovedRangeStart(), this.howMany, newTargetPosition, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        const sourceElement = this.sourcePosition.parent;
        const targetElement = this.targetPosition.parent;
        const sourceOffset = this.sourcePosition.offset;
        const targetOffset = this.targetPosition.offset;
        // Validate whether move operation has correct parameters.
        // Validation is pretty complex but move operation is one of the core ways to manipulate the document state.
        // We expect that many errors might be connected with one of scenarios described below.
        if (sourceOffset + this.howMany > sourceElement.maxOffset) {
            /**
             * The nodes which should be moved do not exist.
             *
             * @error move-operation-nodes-do-not-exist
             */
            throw new CKEditorError('move-operation-nodes-do-not-exist', this);
        }
        else if (sourceElement === targetElement && sourceOffset < targetOffset && targetOffset < sourceOffset + this.howMany) {
            /**
             * Trying to move a range of nodes into the middle of that range.
             *
             * @error move-operation-range-into-itself
             */
            throw new CKEditorError('move-operation-range-into-itself', this);
        }
        else if (this.sourcePosition.root == this.targetPosition.root) {
            if (compareArrays(this.sourcePosition.getParentPath(), this.targetPosition.getParentPath()) == 'prefix') {
                const i = this.sourcePosition.path.length - 1;
                if (this.targetPosition.path[i] >= sourceOffset && this.targetPosition.path[i] < sourceOffset + this.howMany) {
                    /**
                     * Trying to move a range of nodes into one of nodes from that range.
                     *
                     * @error move-operation-node-into-itself
                     */
                    throw new CKEditorError('move-operation-node-into-itself', this);
                }
            }
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        _move(range_Range._createFromPositionAndShift(this.sourcePosition, this.howMany), this.targetPosition);
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.sourcePosition = this.sourcePosition.toJSON();
        json.targetPosition = this.targetPosition.toJSON();
        return json;
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'MoveOperation';
    }
    /**
     * Creates `MoveOperation` object from deserilized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/moveoperation~MoveOperation}
     */
    static fromJSON(json, document) {
        const sourcePosition = position_Position.fromJSON(json.sourcePosition, document);
        const targetPosition = position_Position.fromJSON(json.targetPosition, document);
        return new this(sourcePosition, json.howMany, targetPosition, json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/insertoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/insertoperation
 */








/**
 * Operation to insert one or more nodes at given position in the model.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class InsertOperation extends Operation {
    /**
     * Creates an insert operation.
     *
     * @param {module:engine/model/position~Position} position Position of insertion.
     * @param {module:engine/model/node~NodeSet} nodes The list of nodes to be inserted.
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(position, nodes, baseVersion) {
        super(baseVersion);
        /**
         * Position of insertion.
         *
         * @readonly
         * @member {module:engine/model/position~Position} module:engine/model/operation/insertoperation~InsertOperation#position
         */
        this.position = position.clone();
        this.position.stickiness = 'toNone';
        /**
         * List of nodes to insert.
         *
         * @readonly
         * @member {module:engine/model/nodelist~NodeList} module:engine/model/operation/insertoperation~InsertOperation#nodeList
         */
        this.nodes = new NodeList(_normalizeNodes(nodes));
        /**
         * Flag deciding how the operation should be transformed. If set to `true`, nodes might get additional attributes
         * during operational transformation. This happens when the operation insertion position is inside of a range
         * where attributes have changed.
         *
         * @member {Boolean} module:engine/model/operation/insertoperation~InsertOperation#shouldReceiveAttributes
         */
        this.shouldReceiveAttributes = false;
    }
    /**
     * @inheritDoc
     */
    get type() {
        return 'insert';
    }
    /**
     * Total offset size of inserted nodes.
     *
     * @returns {Number}
     */
    get howMany() {
        return this.nodes.maxOffset;
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/insertoperation~InsertOperation} Clone of this operation.
     */
    clone() {
        const nodes = new NodeList([...this.nodes].map(node => node._clone(true)));
        const insert = new InsertOperation(this.position, nodes, this.baseVersion);
        insert.shouldReceiveAttributes = this.shouldReceiveAttributes;
        return insert;
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/moveoperation~MoveOperation}
     */
    getReversed() {
        const graveyard = this.position.root.document.graveyard;
        const gyPosition = new position_Position(graveyard, [0]);
        return new MoveOperation(this.position, this.nodes.maxOffset, gyPosition, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        const targetElement = this.position.parent;
        if (!targetElement || targetElement.maxOffset < this.position.offset) {
            /**
             * Insertion position is invalid.
             *
             * @error insert-operation-position-invalid
             */
            throw new CKEditorError('insert-operation-position-invalid', this);
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        // What happens here is that we want original nodes be passed to writer because we want original nodes
        // to be inserted to the model. But in InsertOperation, we want to keep those nodes as they were added
        // to the operation, not modified. For example, text nodes can get merged or cropped while Elements can
        // get children. It is important that InsertOperation has the copy of original nodes in intact state.
        const originalNodes = this.nodes;
        this.nodes = new NodeList([...originalNodes].map(node => node._clone(true)));
        _insert(this.position, originalNodes);
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.position = this.position.toJSON();
        json.nodes = this.nodes.toJSON();
        return json;
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'InsertOperation';
    }
    /**
     * Creates `InsertOperation` object from deserilized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/insertoperation~InsertOperation}
     */
    static fromJSON(json, document) {
        const children = [];
        for (const child of json.nodes) {
            if (child.name) {
                // If child has name property, it is an Element.
                children.push(element_Element.fromJSON(child));
            }
            else {
                // Otherwise, it is a Text node.
                children.push(model_text_Text.fromJSON(child));
            }
        }
        const insert = new InsertOperation(position_Position.fromJSON(json.position, document), children, json.baseVersion);
        insert.shouldReceiveAttributes = json.shouldReceiveAttributes;
        return insert;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/markeroperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/markeroperation
 */


/**
 * @extends module:engine/model/operation/operation~Operation
 */
class MarkerOperation extends Operation {
    /**
     * @param {String} name Marker name.
     * @param {module:engine/model/range~Range|null} oldRange Marker range before the change.
     * @param {module:engine/model/range~Range|null} newRange Marker range after the change.
     * @param {module:engine/model/markercollection~MarkerCollection} markers Marker collection on which change should be executed.
     * @param {Boolean} affectsData Specifies whether the marker operation affects the data produced by the data pipeline
     * (is persisted in the editor's data).
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(name, oldRange, newRange, markers, affectsData, baseVersion) {
        super(baseVersion);
        /**
         * Marker name.
         *
         * @readonly
         * @member {String}
         */
        this.name = name;
        /**
         * Marker range before the change.
         *
         * @readonly
         * @member {module:engine/model/range~Range|null}
         */
        this.oldRange = oldRange ? oldRange.clone() : null;
        /**
         * Marker range after the change.
         *
         * @readonly
         * @member {module:engine/model/range~Range}
         */
        this.newRange = newRange ? newRange.clone() : null;
        /**
         * Specifies whether the marker operation affects the data produced by the data pipeline
         * (is persisted in the editor's data).
         *
         * @readonly
         * @member {Boolean}
         */
        this.affectsData = affectsData;
        /**
         * Marker collection on which change should be executed.
         *
         * @private
         * @member {module:engine/model/markercollection~MarkerCollection}
         */
        this._markers = markers;
    }
    /**
     * @inheritDoc
     */
    get type() {
        return 'marker';
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/markeroperation~MarkerOperation} Clone of this operation.
     */
    clone() {
        return new MarkerOperation(this.name, this.oldRange, this.newRange, this._markers, this.affectsData, this.baseVersion);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/markeroperation~MarkerOperation}
     */
    getReversed() {
        return new MarkerOperation(this.name, this.newRange, this.oldRange, this._markers, this.affectsData, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        if (this.newRange) {
            this._markers._set(this.name, this.newRange, true, this.affectsData);
        }
        else {
            this._markers._remove(this.name);
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    toJSON() {
        const json = super.toJSON();
        if (this.oldRange) {
            json.oldRange = this.oldRange.toJSON();
        }
        if (this.newRange) {
            json.newRange = this.newRange.toJSON();
        }
        delete json._markers;
        return json;
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'MarkerOperation';
    }
    /**
     * Creates `MarkerOperation` object from deserialized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/markeroperation~MarkerOperation}
     */
    static fromJSON(json, document) {
        return new MarkerOperation(json.name, json.oldRange ? range_Range.fromJSON(json.oldRange, document) : null, json.newRange ? range_Range.fromJSON(json.newRange, document) : null, document.model.markers, json.affectsData, json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/nooperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/nooperation
 */

/**
 * Operation which is doing nothing ("empty operation", "do-nothing operation", "noop"). This is an operation,
 * which when executed does not change the tree model. It still has some parameters defined for transformation purposes.
 *
 * In most cases this operation is a result of transforming operations. When transformation returns
 * {@link module:engine/model/operation/nooperation~NoOperation} it means that changes done by the transformed operation
 * have already been applied.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class NoOperation extends Operation {
    get type() {
        return 'noop';
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/nooperation~NoOperation} Clone of this operation.
     */
    clone() {
        return new NoOperation(this.baseVersion);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/nooperation~NoOperation}
     */
    getReversed() {
        return new NoOperation(this.baseVersion + 1);
    }
    /** @internal */
    _execute() {
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'NoOperation';
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/renameoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/renameoperation
 */




/**
 * Operation to change element's name.
 *
 * Using this class you can change element's name.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class RenameOperation extends Operation {
    /**
     * Creates an operation that changes element's name.
     *
     * @param {module:engine/model/position~Position} position Position before an element to change.
     * @param {String} oldName Current name of the element.
     * @param {String} newName New name for the element.
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(position, oldName, newName, baseVersion) {
        super(baseVersion);
        /**
         * Position before an element to change.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/renameoperation~RenameOperation#position
         */
        this.position = position;
        // This position sticks to the next node because it is a position before the node that we want to change.
        this.position.stickiness = 'toNext';
        /**
         * Current name of the element.
         *
         * @member {String} module:engine/model/operation/renameoperation~RenameOperation#oldName
         */
        this.oldName = oldName;
        /**
         * New name for the element.
         *
         * @member {String} module:engine/model/operation/renameoperation~RenameOperation#newName
         */
        this.newName = newName;
    }
    /**
     * @inheritDoc
     */
    get type() {
        return 'rename';
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/renameoperation~RenameOperation} Clone of this operation.
     */
    clone() {
        return new RenameOperation(this.position.clone(), this.oldName, this.newName, this.baseVersion);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/renameoperation~RenameOperation}
     */
    getReversed() {
        return new RenameOperation(this.position.clone(), this.newName, this.oldName, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        const element = this.position.nodeAfter;
        if (!(element instanceof element_Element)) {
            /**
             * Given position is invalid or node after it is not instance of Element.
             *
             * @error rename-operation-wrong-position
             */
            throw new CKEditorError('rename-operation-wrong-position', this);
        }
        else if (element.name !== this.oldName) {
            /**
             * Element to change has different name than operation's old name.
             *
             * @error rename-operation-wrong-name
             */
            throw new CKEditorError('rename-operation-wrong-name', this);
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        const element = this.position.nodeAfter;
        element.name = this.newName;
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.position = this.position.toJSON();
        return json;
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'RenameOperation';
    }
    /**
     * Creates `RenameOperation` object from deserialized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/attributeoperation~AttributeOperation}
     */
    static fromJSON(json, document) {
        return new RenameOperation(position_Position.fromJSON(json.position, document), json.oldName, json.newName, json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/rootattributeoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/rootattributeoperation
 */


/**
 * Operation to change root element's attribute. Using this class you can add, remove or change value of the attribute.
 *
 * This operation is needed, because root elements can't be changed through
 * @link module:engine/model/operation/attributeoperation~AttributeOperation}.
 * It is because {@link module:engine/model/operation/attributeoperation~AttributeOperation}
 * requires a range to change and root element can't
 * be a part of range because every {@link module:engine/model/position~Position} has to be inside a root.
 * {@link module:engine/model/position~Position} can't be created before a root element.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class RootAttributeOperation extends Operation {
    /**
     * Creates an operation that changes, removes or adds attributes on root element.
     *
     * @see module:engine/model/operation/attributeoperation~AttributeOperation
     * @param {module:engine/model/rootelement~RootElement} root Root element to change.
     * @param {String} key Key of an attribute to change or remove.
     * @param {*} oldValue Old value of the attribute with given key or `null` if adding a new attribute.
     * @param {*} newValue New value to set for the attribute. If `null`, then the operation just removes the attribute.
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(root, key, oldValue, newValue, baseVersion) {
        super(baseVersion);
        /**
         * Root element to change.
         *
         * @readonly
         * @member {module:engine/model/rootelement~RootElement}
         */
        this.root = root;
        /**
         * Key of an attribute to change or remove.
         *
         * @readonly
         * @member {String}
         */
        this.key = key;
        /**
         * Old value of the attribute with given key or `null` if adding a new attribute.
         *
         * @readonly
         * @member {*}
         */
        this.oldValue = oldValue;
        /**
         * New value to set for the attribute. If `null`, then the operation just removes the attribute.
         *
         * @readonly
         * @member {*}
         */
        this.newValue = newValue;
    }
    /**
     * @inheritDoc
     */
    get type() {
        if (this.oldValue === null) {
            return 'addRootAttribute';
        }
        else if (this.newValue === null) {
            return 'removeRootAttribute';
        }
        else {
            return 'changeRootAttribute';
        }
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/rootattributeoperation~RootAttributeOperation} Clone of this operation.
     */
    clone() {
        return new RootAttributeOperation(this.root, this.key, this.oldValue, this.newValue, this.baseVersion);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/rootattributeoperation~RootAttributeOperation}
     */
    getReversed() {
        return new RootAttributeOperation(this.root, this.key, this.newValue, this.oldValue, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        if (this.root != this.root.root || this.root.is('documentFragment')) {
            /**
             * The element to change is not a root element.
             *
             * @error rootattribute-operation-not-a-root
             * @param {module:engine/model/rootelement~RootElement} root
             * @param {String} key
             * @param {*} value
             */
            throw new CKEditorError('rootattribute-operation-not-a-root', this, { root: this.root, key: this.key });
        }
        if (this.oldValue !== null && this.root.getAttribute(this.key) !== this.oldValue) {
            /**
             * The attribute which should be removed does not exists for the given node.
             *
             * @error rootattribute-operation-wrong-old-value
             * @param {module:engine/model/rootelement~RootElement} root
             * @param {String} key
             * @param {*} value
             */
            throw new CKEditorError('rootattribute-operation-wrong-old-value', this, { root: this.root, key: this.key });
        }
        if (this.oldValue === null && this.newValue !== null && this.root.hasAttribute(this.key)) {
            /**
             * The attribute with given key already exists for the given node.
             *
             * @error rootattribute-operation-attribute-exists
             * @param {module:engine/model/rootelement~RootElement} root
             * @param {String} key
             */
            throw new CKEditorError('rootattribute-operation-attribute-exists', this, { root: this.root, key: this.key });
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        if (this.newValue !== null) {
            this.root._setAttribute(this.key, this.newValue);
        }
        else {
            this.root._removeAttribute(this.key);
        }
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.root = this.root.toJSON();
        return json;
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'RootAttributeOperation';
    }
    /**
     * Creates RootAttributeOperation object from deserilized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/rootattributeoperation~RootAttributeOperation}
     */
    static fromJSON(json, document) {
        if (!document.getRoot(json.root)) {
            /**
             * Cannot create RootAttributeOperation for document. Root with specified name does not exist.
             *
             * @error rootattribute-operation-fromjson-no-root
             * @param {String} rootName
             */
            throw new CKEditorError('rootattribute-operation-fromjson-no-root', this, { rootName: json.root });
        }
        return new RootAttributeOperation(document.getRoot(json.root), json.key, json.oldValue, json.newValue, json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/mergeoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/mergeoperation
 */






/**
 * Operation to merge two {@link module:engine/model/element~Element elements}.
 *
 * The merged element is the parent of {@link ~MergeOperation#sourcePosition} and it is merged into the parent of
 * {@link ~MergeOperation#targetPosition}. All nodes from the merged element are moved to {@link ~MergeOperation#targetPosition}.
 *
 * The merged element is moved to the graveyard at {@link ~MergeOperation#graveyardPosition}.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class MergeOperation extends Operation {
    /**
     * Creates a merge operation.
     *
     * @param {module:engine/model/position~Position} sourcePosition Position inside the merged element. All nodes from that
     * element after that position will be moved to {@link ~#targetPosition}.
     * @param {Number} howMany Summary offset size of nodes which will be moved from the merged element to the new parent.
     * @param {module:engine/model/position~Position} targetPosition Position which the nodes from the merged elements will be moved to.
     * @param {module:engine/model/position~Position} graveyardPosition Position in graveyard to which the merged element will be moved.
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(sourcePosition, howMany, targetPosition, graveyardPosition, baseVersion) {
        super(baseVersion);
        /**
         * Position inside the merged element. All nodes from that element after that position will be moved to {@link ~#targetPosition}.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#sourcePosition
         */
        this.sourcePosition = sourcePosition.clone();
        // This is, and should always remain, the first position in its parent.
        this.sourcePosition.stickiness = 'toPrevious';
        /**
         * Summary offset size of nodes which will be moved from the merged element to the new parent.
         *
         * @member {Number} module:engine/model/operation/mergeoperation~MergeOperation#howMany
         */
        this.howMany = howMany;
        /**
         * Position which the nodes from the merged elements will be moved to.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#targetPosition
         */
        this.targetPosition = targetPosition.clone();
        // Except of a rare scenario in `MergeOperation` x `MergeOperation` transformation,
        // this is, and should always remain, the last position in its parent.
        this.targetPosition.stickiness = 'toNext';
        /**
         * Position in graveyard to which the merged element will be moved.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/mergeoperation~MergeOperation#graveyardPosition
         */
        this.graveyardPosition = graveyardPosition.clone();
    }
    /**
     * @inheritDoc
     */
    get type() {
        return 'merge';
    }
    /**
     * Position before the merged element (which will be deleted).
     *
     * @readonly
     * @type {module:engine/model/position~Position}
     */
    get deletionPosition() {
        return new position_Position(this.sourcePosition.root, this.sourcePosition.path.slice(0, -1));
    }
    /**
     * Artificial range that contains all the nodes from the merged element that will be moved to {@link ~MergeOperation#sourcePosition}.
     * The range starts at {@link ~MergeOperation#sourcePosition} and ends in the same parent, at `POSITIVE_INFINITY` offset.
     *
     * @readonly
     * @type {module:engine/model/range~Range}
     */
    get movedRange() {
        const end = this.sourcePosition.getShiftedBy(Number.POSITIVE_INFINITY);
        return new range_Range(this.sourcePosition, end);
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/mergeoperation~MergeOperation} Clone of this operation.
     */
    clone() {
        return new MergeOperation(this.sourcePosition, this.howMany, this.targetPosition, this.graveyardPosition, this.baseVersion);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/splitoperation~SplitOperation}
     */
    getReversed() {
        // Positions in this method are transformed by this merge operation because the split operation bases on
        // the context after this merge operation happened (because split operation reverses it).
        // So we need to acknowledge that the merge operation happened and those positions changed a little.
        const targetPosition = this.targetPosition._getTransformedByMergeOperation(this);
        const path = this.sourcePosition.path.slice(0, -1);
        const insertionPosition = new position_Position(this.sourcePosition.root, path)._getTransformedByMergeOperation(this);
        return new SplitOperation(targetPosition, this.howMany, insertionPosition, this.graveyardPosition, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        const sourceElement = this.sourcePosition.parent;
        const targetElement = this.targetPosition.parent;
        // Validate whether merge operation has correct parameters.
        if (!sourceElement.parent) {
            /**
             * Merge source position is invalid. The element to be merged must have a parent node.
             *
             * @error merge-operation-source-position-invalid
             */
            throw new CKEditorError('merge-operation-source-position-invalid', this);
        }
        else if (!targetElement.parent) {
            /**
             * Merge target position is invalid. The element to be merged must have a parent node.
             *
             * @error merge-operation-target-position-invalid
             */
            throw new CKEditorError('merge-operation-target-position-invalid', this);
        }
        else if (this.howMany != sourceElement.maxOffset) {
            /**
             * Merge operation specifies wrong number of nodes to move.
             *
             * @error merge-operation-how-many-invalid
             */
            throw new CKEditorError('merge-operation-how-many-invalid', this);
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        const mergedElement = this.sourcePosition.parent;
        const sourceRange = range_Range._createIn(mergedElement);
        _move(sourceRange, this.targetPosition);
        _move(range_Range._createOn(mergedElement), this.graveyardPosition);
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.sourcePosition = json.sourcePosition.toJSON();
        json.targetPosition = json.targetPosition.toJSON();
        json.graveyardPosition = json.graveyardPosition.toJSON();
        return json;
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'MergeOperation';
    }
    /**
     * Creates `MergeOperation` object from deserilized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/mergeoperation~MergeOperation}
     */
    static fromJSON(json, document) {
        const sourcePosition = position_Position.fromJSON(json.sourcePosition, document);
        const targetPosition = position_Position.fromJSON(json.targetPosition, document);
        const graveyardPosition = position_Position.fromJSON(json.graveyardPosition, document);
        return new this(sourcePosition, json.howMany, targetPosition, graveyardPosition, json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/splitoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/splitoperation
 */






/**
 * Operation to split {@link module:engine/model/element~Element an element} at given
 * {@link module:engine/model/operation/splitoperation~SplitOperation#splitPosition split position} into two elements,
 * both containing a part of the element's original content.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class SplitOperation extends Operation {
    /**
     * Creates a split operation.
     *
     * @param {module:engine/model/position~Position} splitPosition Position at which an element should be split.
     * @param {Number} howMany Total offset size of elements that are in the split element after `position`.
     * @param {module:engine/model/position~Position} insertionPosition Position at which the clone of split element
     * (or element from graveyard) will be inserted.
     * @param {module:engine/model/position~Position|null} graveyardPosition Position in the graveyard root before the element which
     * should be used as a parent of the nodes after `position`. If it is not set, a copy of the the `position` parent will be used.
     * @param {Number|null} baseVersion Document {@link module:engine/model/document~Document#version} on which operation
     * can be applied or `null` if the operation operates on detached (non-document) tree.
     */
    constructor(splitPosition, howMany, insertionPosition, graveyardPosition, baseVersion) {
        super(baseVersion);
        /**
         * Position at which an element should be split.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/splitoperation~SplitOperation#splitPosition
         */
        this.splitPosition = splitPosition.clone();
        // Keep position sticking to the next node. This way any new content added at the place where the element is split
        // will be left in the original element.
        this.splitPosition.stickiness = 'toNext';
        /**
         * Total offset size of elements that are in the split element after `position`.
         *
         * @member {Number} module:engine/model/operation/splitoperation~SplitOperation#howMany
         */
        this.howMany = howMany;
        /**
         * Position at which the clone of split element (or element from graveyard) will be inserted.
         *
         * @member {module:engine/model/position~Position} module:engine/model/operation/splitoperation~SplitOperation#insertionPosition
         */
        this.insertionPosition = insertionPosition;
        /**
         * Position in the graveyard root before the element which should be used as a parent of the nodes after `position`.
         * If it is not set, a copy of the the `position` parent will be used.
         *
         * The default behavior is to clone the split element. Element from graveyard is used during undo.
         *
         * @member {module:engine/model/position~Position|null} #graveyardPosition
         */
        this.graveyardPosition = graveyardPosition ? graveyardPosition.clone() : null;
        if (this.graveyardPosition) {
            this.graveyardPosition.stickiness = 'toNext';
        }
    }
    /**
     * @inheritDoc
     */
    get type() {
        return 'split';
    }
    /**
     * Position inside the new clone of a split element.
     *
     * This is a position where nodes that are after the split position will be moved to.
     *
     * @readonly
     * @type {module:engine/model/position~Position}
     */
    get moveTargetPosition() {
        const path = this.insertionPosition.path.slice();
        path.push(0);
        return new position_Position(this.insertionPosition.root, path);
    }
    /**
     * Artificial range that contains all the nodes from the split element that will be moved to the new element.
     * The range starts at {@link ~#splitPosition} and ends in the same parent, at `POSITIVE_INFINITY` offset.
     *
     * @readonly
     * @type {module:engine/model/range~Range}
     */
    get movedRange() {
        const end = this.splitPosition.getShiftedBy(Number.POSITIVE_INFINITY);
        return new range_Range(this.splitPosition, end);
    }
    /**
     * Creates and returns an operation that has the same parameters as this operation.
     *
     * @returns {module:engine/model/operation/splitoperation~SplitOperation} Clone of this operation.
     */
    clone() {
        return new SplitOperation(this.splitPosition, this.howMany, this.insertionPosition, this.graveyardPosition, this.baseVersion);
    }
    /**
     * See {@link module:engine/model/operation/operation~Operation#getReversed `Operation#getReversed()`}.
     *
     * @returns {module:engine/model/operation/mergeoperation~MergeOperation}
     */
    getReversed() {
        const graveyard = this.splitPosition.root.document.graveyard;
        const graveyardPosition = new position_Position(graveyard, [0]);
        return new MergeOperation(this.moveTargetPosition, this.howMany, this.splitPosition, graveyardPosition, this.baseVersion + 1);
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        const element = this.splitPosition.parent;
        const offset = this.splitPosition.offset;
        // Validate whether split operation has correct parameters.
        if (!element || element.maxOffset < offset) {
            /**
             * Split position is invalid.
             *
             * @error split-operation-position-invalid
             */
            throw new CKEditorError('split-operation-position-invalid', this);
        }
        else if (!element.parent) {
            /**
             * Cannot split root element.
             *
             * @error split-operation-split-in-root
             */
            throw new CKEditorError('split-operation-split-in-root', this);
        }
        else if (this.howMany != element.maxOffset - this.splitPosition.offset) {
            /**
             * Split operation specifies wrong number of nodes to move.
             *
             * @error split-operation-how-many-invalid
             */
            throw new CKEditorError('split-operation-how-many-invalid', this);
        }
        else if (this.graveyardPosition && !this.graveyardPosition.nodeAfter) {
            /**
             * Graveyard position invalid.
             *
             * @error split-operation-graveyard-position-invalid
             */
            throw new CKEditorError('split-operation-graveyard-position-invalid', this);
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        const splitElement = this.splitPosition.parent;
        if (this.graveyardPosition) {
            _move(range_Range._createFromPositionAndShift(this.graveyardPosition, 1), this.insertionPosition);
        }
        else {
            const newElement = splitElement._clone();
            _insert(this.insertionPosition, newElement);
        }
        const sourceRange = new range_Range(position_Position._createAt(splitElement, this.splitPosition.offset), position_Position._createAt(splitElement, splitElement.maxOffset));
        _move(sourceRange, this.moveTargetPosition);
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.splitPosition = this.splitPosition.toJSON();
        json.insertionPosition = this.insertionPosition.toJSON();
        if (this.graveyardPosition) {
            json.graveyardPosition = this.graveyardPosition.toJSON();
        }
        return json;
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'SplitOperation';
    }
    /**
     * Helper function that returns a default insertion position basing on given `splitPosition`. The default insertion
     * position is after the split element.
     *
     * @param {module:engine/model/position~Position} splitPosition
     * @returns {module:engine/model/position~Position}
     */
    static getInsertionPosition(splitPosition) {
        const path = splitPosition.path.slice(0, -1);
        path[path.length - 1]++;
        return new position_Position(splitPosition.root, path, 'toPrevious');
    }
    /**
     * Creates `SplitOperation` object from deserilized object, i.e. from parsed JSON string.
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/splitoperation~SplitOperation}
     */
    static fromJSON(json, document) {
        const splitPosition = position_Position.fromJSON(json.splitPosition, document);
        const insertionPosition = position_Position.fromJSON(json.insertionPosition, document);
        const graveyardPosition = json.graveyardPosition ? position_Position.fromJSON(json.graveyardPosition, document) : null;
        return new this(splitPosition, json.howMany, insertionPosition, graveyardPosition, json.baseVersion);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/operationfactory.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/operationfactory
 */










const operations = {};
operations[AttributeOperation.className] = AttributeOperation;
operations[InsertOperation.className] = InsertOperation;
operations[MarkerOperation.className] = MarkerOperation;
operations[MoveOperation.className] = MoveOperation;
operations[NoOperation.className] = NoOperation;
operations[Operation.className] = Operation;
operations[RenameOperation.className] = RenameOperation;
operations[RootAttributeOperation.className] = RootAttributeOperation;
operations[SplitOperation.className] = SplitOperation;
operations[MergeOperation.className] = MergeOperation;
/**
 * A factory class for creating operations.
 *
 * @abstract
 */
class OperationFactory {
    /**
     * Creates an operation instance from a JSON object (parsed JSON string).
     *
     * @param {Object} json Deserialized JSON object.
     * @param {module:engine/model/document~Document} document Document on which this operation will be applied.
     * @returns {module:engine/model/operation/operation~Operation}
     */
    static fromJSON(json, document) {
        return operations[json.__className].fromJSON(json, document);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/detachoperation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/operation/detachoperation
 */




// @if CK_DEBUG_ENGINE // const ModelRange = require( '../range' ).default;
/**
 * Operation to permanently remove node from detached root.
 * Note this operation is only a local operation and won't be send to the other clients.
 *
 * @extends module:engine/model/operation/operation~Operation
 */
class DetachOperation extends Operation {
    /**
     * Creates an insert operation.
     *
     * @param {module:engine/model/position~Position} sourcePosition
     * Position before the first {@link module:engine/model/item~Item model item} to move.
     * @param {Number} howMany Offset size of moved range. Moved range will start from `sourcePosition` and end at
     * `sourcePosition` with offset shifted by `howMany`.
     */
    constructor(sourcePosition, howMany) {
        super(null);
        /**
         * Position before the first {@link module:engine/model/item~Item model item} to detach.
         *
         * @member {module:engine/model/position~Position} #sourcePosition
         */
        this.sourcePosition = sourcePosition.clone();
        /**
         * Offset size of moved range.
         *
         * @member {Number} #howMany
         */
        this.howMany = howMany;
    }
    /**
     * @inheritDoc
     */
    get type() {
        return 'detach';
    }
    /**
     * @inheritDoc
     */
    toJSON() {
        const json = super.toJSON();
        json.sourcePosition = this.sourcePosition.toJSON();
        return json;
    }
    /**
     * @inheritDoc
     * @internal
     */
    _validate() {
        if (this.sourcePosition.root.document) {
            /**
             * Cannot detach document node.
             *
             * @error detach-operation-on-document-node
             */
            throw new CKEditorError('detach-operation-on-document-node', this);
        }
    }
    /**
     * @inheritDoc
     * @internal
     */
    _execute() {
        _remove(range_Range._createFromPositionAndShift(this.sourcePosition, this.howMany));
    }
    /**
     * @inheritDoc
     */
    static get className() {
        return 'DetachOperation';
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/documentfragment.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module module:engine/model/documentfragment
 */






// @if CK_DEBUG_ENGINE // const { stringifyMap } = require( '../dev-utils/utils' );
/**
 * DocumentFragment represents a part of model which does not have a common root but its top-level nodes
 * can be seen as siblings. In other words, it is a detached part of model tree, without a root.
 *
 * DocumentFragment has own {@link module:engine/model/markercollection~MarkerCollection}. Markers from this collection
 * will be set to the {@link module:engine/model/model~Model#markers model markers} by a
 * {@link module:engine/model/writer~Writer#insert} function.
 */
class documentfragment_DocumentFragment extends typecheckable_TypeCheckable {
    /**
     * Creates an empty `DocumentFragment`.
     *
     * **Note:** Constructor of this class shouldn't be used directly in the code.
     * Use the {@link module:engine/model/writer~Writer#createDocumentFragment} method instead.
     *
     * @protected
     * @param {module:engine/model/node~Node|Iterable.<module:engine/model/node~Node>} [children]
     * Nodes to be contained inside the `DocumentFragment`.
     */
    constructor(children) {
        super();
        /**
         * DocumentFragment static markers map. This is a list of names and {@link module:engine/model/range~Range ranges}
         * which will be set as Markers to {@link module:engine/model/model~Model#markers model markers collection}
         * when DocumentFragment will be inserted to the document.
         *
         * @readonly
         * @member {Map<String,module:engine/model/range~Range>} module:engine/model/documentfragment~DocumentFragment#markers
         */
        this.markers = new Map();
        /**
         * List of nodes contained inside the document fragment.
         *
         * @private
         * @member {module:engine/model/nodelist~NodeList} module:engine/model/documentfragment~DocumentFragment#_children
         */
        this._children = new NodeList();
        if (children) {
            this._insertChild(0, children);
        }
    }
    /**
     * Returns an iterator that iterates over all nodes contained inside this document fragment.
     *
     * @returns {Iterator.<module:engine/model/node~Node>}
     */
    [Symbol.iterator]() {
        return this.getChildren();
    }
    /**
     * Number of this document fragment's children.
     *
     * @readonly
     * @type {Number}
     */
    get childCount() {
        return this._children.length;
    }
    /**
     * Sum of {@link module:engine/model/node~Node#offsetSize offset sizes} of all of this document fragment's children.
     *
     * @readonly
     * @type {Number}
     */
    get maxOffset() {
        return this._children.maxOffset;
    }
    /**
     * Is `true` if there are no nodes inside this document fragment, `false` otherwise.
     *
     * @readonly
     * @type {Boolean}
     */
    get isEmpty() {
        return this.childCount === 0;
    }
    /**
     * Artificial next sibling. Returns `null`. Added for compatibility reasons.
     *
     * @readonly
     * @type {null}
     */
    get nextSibling() {
        return null;
    }
    /**
     * Artificial previous sibling. Returns `null`. Added for compatibility reasons.
     *
     * @readonly
     * @type {null}
     */
    get previousSibling() {
        return null;
    }
    /**
     * Artificial root of `DocumentFragment`. Returns itself. Added for compatibility reasons.
     *
     * @readonly
     * @type {module:engine/model/documentfragment~DocumentFragment}
     */
    get root() {
        return this;
    }
    /**
     * Artificial parent of `DocumentFragment`. Returns `null`. Added for compatibility reasons.
     *
     * @readonly
     * @type {null}
     */
    get parent() {
        return null;
    }
    /**
     * Artificial owner of `DocumentFragment`. Returns `null`. Added for compatibility reasons.
     *
     * @readonly
     * @type {null}
     */
    get document() {
        return null;
    }
    /**
     * Returns empty array. Added for compatibility reasons.
     *
     * @returns {Array}
     */
    getAncestors() {
        return [];
    }
    /**
     * Gets the child at the given index. Returns `null` if incorrect index was passed.
     *
     * @param {Number} index Index of child.
     * @returns {module:engine/model/node~Node|null} Child node.
     */
    getChild(index) {
        return this._children.getNode(index);
    }
    /**
     * Returns an iterator that iterates over all of this document fragment's children.
     *
     * @returns {Iterable.<module:engine/model/node~Node>}
     */
    getChildren() {
        return this._children[Symbol.iterator]();
    }
    /**
     * Returns an index of the given child node. Returns `null` if given node is not a child of this document fragment.
     *
     * @param {module:engine/model/node~Node} node Child node to look for.
     * @returns {Number|null} Child node's index.
     */
    getChildIndex(node) {
        return this._children.getNodeIndex(node);
    }
    /**
     * Returns the starting offset of given child. Starting offset is equal to the sum of
     * {@link module:engine/model/node~Node#offsetSize offset sizes} of all node's siblings that are before it. Returns `null` if
     * given node is not a child of this document fragment.
     *
     * @param {module:engine/model/node~Node} node Child node to look for.
     * @returns {Number|null} Child node's starting offset.
     */
    getChildStartOffset(node) {
        return this._children.getNodeStartOffset(node);
    }
    /**
     * Returns path to a `DocumentFragment`, which is an empty array. Added for compatibility reasons.
     *
     * @returns {Array}
     */
    getPath() {
        return [];
    }
    /**
     * Returns a descendant node by its path relative to this element.
     *
     *		// <this>a<b>c</b></this>
     *		this.getNodeByPath( [ 0 ] );     // -> "a"
     *		this.getNodeByPath( [ 1 ] );     // -> <b>
     *		this.getNodeByPath( [ 1, 0 ] );  // -> "c"
     *
     * @param {Array.<Number>} relativePath Path of the node to find, relative to this element.
     * @returns {module:engine/model/node~Node|module:engine/model/documentfragment~DocumentFragment}
     */
    getNodeByPath(relativePath) {
        // eslint-disable-next-line @typescript-eslint/no-this-alias, consistent-this
        let node = this;
        for (const index of relativePath) {
            node = node.getChild(node.offsetToIndex(index));
        }
        return node;
    }
    /**
     * Converts offset "position" to index "position".
     *
     * Returns index of a node that occupies given offset. If given offset is too low, returns `0`. If given offset is
     * too high, returns index after last child}.
     *
     *		const textNode = new Text( 'foo' );
     *		const pElement = new Element( 'p' );
     *		const docFrag = new DocumentFragment( [ textNode, pElement ] );
     *		docFrag.offsetToIndex( -1 ); // Returns 0, because offset is too low.
     *		docFrag.offsetToIndex( 0 ); // Returns 0, because offset 0 is taken by `textNode` which is at index 0.
     *		docFrag.offsetToIndex( 1 ); // Returns 0, because `textNode` has `offsetSize` equal to 3, so it occupies offset 1 too.
     *		docFrag.offsetToIndex( 2 ); // Returns 0.
     *		docFrag.offsetToIndex( 3 ); // Returns 1.
     *		docFrag.offsetToIndex( 4 ); // Returns 2. There are no nodes at offset 4, so last available index is returned.
     *
     * @param {Number} offset Offset to look for.
     * @returns {Number} Index of a node that occupies given offset.
     */
    offsetToIndex(offset) {
        return this._children.offsetToIndex(offset);
    }
    /**
     * Converts `DocumentFragment` instance to plain object and returns it.
     * Takes care of converting all of this document fragment's children.
     *
     * @returns {Object} `DocumentFragment` instance converted to plain object.
     */
    toJSON() {
        const json = [];
        for (const node of this._children) {
            json.push(node.toJSON());
        }
        return json;
    }
    /**
     * Creates a `DocumentFragment` instance from given plain object (i.e. parsed JSON string).
     * Converts `DocumentFragment` children to proper nodes.
     *
     * @param {Object} json Plain object to be converted to `DocumentFragment`.
     * @returns {module:engine/model/documentfragment~DocumentFragment} `DocumentFragment` instance created using given plain object.
     */
    static fromJSON(json) {
        const children = [];
        for (const child of json) {
            if (child.name) {
                // If child has name property, it is an Element.
                children.push(element_Element.fromJSON(child));
            }
            else {
                // Otherwise, it is a Text node.
                children.push(model_text_Text.fromJSON(child));
            }
        }
        return new documentfragment_DocumentFragment(children);
    }
    /**
     * {@link #_insertChild Inserts} one or more nodes at the end of this document fragment.
     *
     * @internal
     * @protected
     * @param {String|module:engine/model/item~Item|Iterable.<String|module:engine/model/item~Item>} items Items to be inserted.
     */
    _appendChild(items) {
        this._insertChild(this.childCount, items);
    }
    /**
     * Inserts one or more nodes at the given index and sets {@link module:engine/model/node~Node#parent parent} of these nodes
     * to this document fragment.
     *
     * @internal
     * @protected
     * @param {Number} index Index at which nodes should be inserted.
     * @param {String|module:engine/model/item~Item|Iterable.<String|module:engine/model/item~Item>} items Items to be inserted.
     */
    _insertChild(index, items) {
        const nodes = model_documentfragment_normalize(items);
        for (const node of nodes) {
            // If node that is being added to this element is already inside another element, first remove it from the old parent.
            if (node.parent !== null) {
                node._remove();
            }
            node.parent = this;
        }
        this._children._insertNodes(index, nodes);
    }
    /**
     * Removes one or more nodes starting at the given index
     * and sets {@link module:engine/model/node~Node#parent parent} of these nodes to `null`.
     *
     * @internal
     * @protected
     * @param {Number} index Index of the first node to remove.
     * @param {Number} [howMany=1] Number of nodes to remove.
     * @returns {Array.<module:engine/model/node~Node>} Array containing removed nodes.
     */
    _removeChildren(index, howMany = 1) {
        const nodes = this._children._removeNodes(index, howMany);
        for (const node of nodes) {
            node.parent = null;
        }
        return nodes;
    }
}
/**
 * Checks whether this object is of the given type.
 *
 *		docFrag.is( 'documentFragment' ); // -> true
 *		docFrag.is( 'model:documentFragment' ); // -> true
 *
 *		docFrag.is( 'view:documentFragment' ); // -> false
 *		docFrag.is( 'element' ); // -> false
 *		docFrag.is( 'node' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
documentfragment_DocumentFragment.prototype.is = function (type) {
    return type === 'documentFragment' || type === 'model:documentFragment';
};
// Converts strings to Text and non-iterables to arrays.
//
// @param {String|module:engine/model/item~Item|Iterable.<module:engine/model/item~Item>}
// @returns {Iterable.<module:engine/model/node~Node>}
function model_documentfragment_normalize(nodes) {
    // Separate condition because string is iterable.
    if (typeof nodes == 'string') {
        return [new model_text_Text(nodes)];
    }
    if (!isIterable(nodes)) {
        nodes = [nodes];
    }
    // Array.from to enable .map() on non-arrays.
    return Array.from(nodes)
        .map(node => {
        if (typeof node == 'string') {
            return new model_text_Text(node);
        }
        if (node instanceof textproxy_TextProxy) {
            return new model_text_Text(node.data, node.getAttributes());
        }
        return node;
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/writer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/writer
 */


















/**
 * The model can only be modified by using the writer. It should be used whenever you want to create a node, modify
 * child nodes, attributes or text, set the selection's position and its attributes.
 *
 * The instance of the writer is only available in the {@link module:engine/model/model~Model#change `change()`} or
 * {@link module:engine/model/model~Model#enqueueChange `enqueueChange()`}.
 *
 *		model.change( writer => {
 *			writer.insertText( 'foo', paragraph, 'end' );
 *		} );
 *
 * Note that the writer should never be stored and used outside of the `change()` and
 * `enqueueChange()` blocks.
 *
 * Note that writer's methods do not check the {@link module:engine/model/schema~Schema}. It is possible
 * to create incorrect model structures by using the writer. Read more about in
 * {@glink framework/guides/deep-dive/schema#who-checks-the-schema "Who checks the schema?"}.
 *
 * @see module:engine/model/model~Model#change
 * @see module:engine/model/model~Model#enqueueChange
 */
class Writer {
    /**
     * Creates a writer instance.
     *
     * **Note:** It is not recommended to use it directly. Use {@link module:engine/model/model~Model#change `Model#change()`} or
     * {@link module:engine/model/model~Model#enqueueChange `Model#enqueueChange()`} instead.
     *
     * @protected
     * @param {module:engine/model/model~Model} model
     * @param {module:engine/model/batch~Batch} batch
     */
    constructor(model, batch) {
        /**
         * Instance of the model on which this writer operates.
         *
         * @readonly
         * @type {module:engine/model/model~Model}
         */
        this.model = model;
        /**
         * The batch to which this writer will add changes.
         *
         * @readonly
         * @type {module:engine/model/batch~Batch}
         */
        this.batch = batch;
    }
    /**
     * Creates a new {@link module:engine/model/text~Text text node}.
     *
     *		writer.createText( 'foo' );
     *		writer.createText( 'foo', { bold: true } );
     *
     * @param {String} data Text data.
     * @param {Object} [attributes] Text attributes.
     * @returns {module:engine/model/text~Text} Created text node.
     */
    createText(data, attributes) {
        return new model_text_Text(data, attributes);
    }
    /**
     * Creates a new {@link module:engine/model/element~Element element}.
     *
     *		writer.createElement( 'paragraph' );
     *		writer.createElement( 'paragraph', { alignment: 'center' } );
     *
     * @param {String} name Name of the element.
     * @param {Object} [attributes] Elements attributes.
     * @returns {module:engine/model/element~Element} Created element.
     */
    createElement(name, attributes) {
        return new element_Element(name, attributes);
    }
    /**
     * Creates a new {@link module:engine/model/documentfragment~DocumentFragment document fragment}.
     *
     * @returns {module:engine/model/documentfragment~DocumentFragment} Created document fragment.
     */
    createDocumentFragment() {
        return new documentfragment_DocumentFragment();
    }
    /**
     * Creates a copy of the element and returns it. Created element has the same name and attributes as the original element.
     * If clone is deep, the original element's children are also cloned. If not, then empty element is returned.
     *
     * @param {module:engine/model/element~Element} element The element to clone.
     * @param {Boolean} [deep=true] If set to `true` clones element and all its children recursively. When set to `false`,
     * element will be cloned without any child.
     */
    cloneElement(element, deep = true) {
        return element._clone(deep);
    }
    /**
     * Inserts item on given position.
     *
     *		const paragraph = writer.createElement( 'paragraph' );
     *		writer.insert( paragraph, position );
     *
     * Instead of using position you can use parent and offset:
     *
     *		const text = writer.createText( 'foo' );
     *		writer.insert( text, paragraph, 5 );
     *
     * You can also use `end` instead of the offset to insert at the end:
     *
     *		const text = writer.createText( 'foo' );
     *		writer.insert( text, paragraph, 'end' );
     *
     * Or insert before or after another element:
     *
     *		const paragraph = writer.createElement( 'paragraph' );
     *		writer.insert( paragraph, anotherParagraph, 'after' );
     *
     * These parameters works the same way as {@link #createPositionAt `writer.createPositionAt()`}.
     *
     * Note that if the item already has parent it will be removed from the previous parent.
     *
     * Note that you cannot re-insert a node from a document to a different document or a document fragment. In this case,
     * `model-writer-insert-forbidden-move` is thrown.
     *
     * If you want to move {@link module:engine/model/range~Range range} instead of an
     * {@link module:engine/model/item~Item item} use {@link module:engine/model/writer~Writer#move `Writer#move()`}.
     *
     * **Note:** For a paste-like content insertion mechanism see
     * {@link module:engine/model/model~Model#insertContent `model.insertContent()`}.
     *
     * @param {module:engine/model/item~Item|module:engine/model/documentfragment~DocumentFragment} item Item or document
     * fragment to insert.
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * second parameter is a {@link module:engine/model/item~Item model item}.
     */
    insert(item, itemOrPosition, offset = 0) {
        this._assertWriterUsedCorrectly();
        if (item instanceof model_text_Text && item.data == '') {
            return;
        }
        const position = position_Position._createAt(itemOrPosition, offset);
        // If item has a parent already.
        if (item.parent) {
            // We need to check if item is going to be inserted within the same document.
            if (isSameTree(item.root, position.root)) {
                // If it's we just need to move it.
                this.move(range_Range._createOn(item), position);
                return;
            }
            // If it isn't the same root.
            else {
                if (item.root.document) {
                    /**
                     * Cannot move a node from a document to a different tree.
                     * It is forbidden to move a node that was already in a document outside of it.
                     *
                     * @error model-writer-insert-forbidden-move
                     */
                    throw new CKEditorError('model-writer-insert-forbidden-move', this);
                }
                else {
                    // Move between two different document fragments or from document fragment to a document is possible.
                    // In that case, remove the item from it's original parent.
                    this.remove(item);
                }
            }
        }
        const version = position.root.document ? position.root.document.version : null;
        const insert = new InsertOperation(position, item, version);
        if (item instanceof model_text_Text) {
            insert.shouldReceiveAttributes = true;
        }
        this.batch.addOperation(insert);
        this.model.applyOperation(insert);
        // When element is a DocumentFragment we need to move its markers to Document#markers.
        if (item instanceof documentfragment_DocumentFragment) {
            for (const [markerName, markerRange] of item.markers) {
                // We need to migrate marker range from DocumentFragment to Document.
                const rangeRootPosition = position_Position._createAt(markerRange.root, 0);
                const range = new range_Range(markerRange.start._getCombined(rangeRootPosition, position), markerRange.end._getCombined(rangeRootPosition, position));
                const options = { range, usingOperation: true, affectsData: true };
                if (this.model.markers.has(markerName)) {
                    this.updateMarker(markerName, options);
                }
                else {
                    this.addMarker(markerName, options);
                }
            }
        }
    }
    /**
     * Creates and inserts text on given position. You can optionally set text attributes:
     *
     *		writer.insertText( 'foo', position );
     *		writer.insertText( 'foo', { bold: true }, position );
     *
     * Instead of using position you can use parent and offset or define that text should be inserted at the end
     * or before or after other node:
     *
     *		// Inserts 'foo' in paragraph, at offset 5:
     *		writer.insertText( 'foo', paragraph, 5 );
     *		// Inserts 'foo' at the end of a paragraph:
     *		writer.insertText( 'foo', paragraph, 'end' );
     *		// Inserts 'foo' after an image:
     *		writer.insertText( 'foo', image, 'after' );
     *
     * These parameters work in the same way as {@link #createPositionAt `writer.createPositionAt()`}.
     *
     * @param {String} dattexta Text data.
     * @param {Object} [attributes] Text attributes.
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * third parameter is a {@link module:engine/model/item~Item model item}.
     */
    insertText(text, attributes, // Too complicated when not using `any`.
    itemOrPosition, // Too complicated when not using `any`.
    offset // Too complicated when not using `any`.
    ) {
        if (attributes instanceof documentfragment_DocumentFragment || attributes instanceof element_Element || attributes instanceof position_Position) {
            this.insert(this.createText(text), attributes, itemOrPosition);
        }
        else {
            this.insert(this.createText(text, attributes), itemOrPosition, offset);
        }
    }
    /**
     * Creates and inserts element on given position. You can optionally set attributes:
     *
     *		writer.insertElement( 'paragraph', position );
     *		writer.insertElement( 'paragraph', { alignment: 'center' }, position );
     *
     * Instead of using position you can use parent and offset or define that text should be inserted at the end
     * or before or after other node:
     *
     *		// Inserts paragraph in the root at offset 5:
     *		writer.insertElement( 'paragraph', root, 5 );
     *		// Inserts paragraph at the end of a blockquote:
     *		writer.insertElement( 'paragraph', blockquote, 'end' );
     *		// Inserts after an image:
     *		writer.insertElement( 'paragraph', image, 'after' );
     *
     * These parameters works the same way as {@link #createPositionAt `writer.createPositionAt()`}.
     *
     * @param {String} name Name of the element.
     * @param {Object} [attributes] Elements attributes.
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * third parameter is a {@link module:engine/model/item~Item model item}.
     */
    insertElement(name, attributes, // Too complicated when not using `any`.
    itemOrPositionOrOffset, // Too complicated when not using `any`.
    offset // Too complicated when not using `any`.
    ) {
        if (attributes instanceof documentfragment_DocumentFragment || attributes instanceof element_Element || attributes instanceof position_Position) {
            this.insert(this.createElement(name), attributes, itemOrPositionOrOffset);
        }
        else {
            this.insert(this.createElement(name, attributes), itemOrPositionOrOffset, offset);
        }
    }
    /**
     * Inserts item at the end of the given parent.
     *
     *		const paragraph = writer.createElement( 'paragraph' );
     *		writer.append( paragraph, root );
     *
     * Note that if the item already has parent it will be removed from the previous parent.
     *
     * If you want to move {@link module:engine/model/range~Range range} instead of an
     * {@link module:engine/model/item~Item item} use {@link module:engine/model/writer~Writer#move `Writer#move()`}.
     *
     * @param {module:engine/model/item~Item|module:engine/model/documentfragment~DocumentFragment}
     * item Item or document fragment to insert.
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} parent
     */
    append(item, parent) {
        this.insert(item, parent, 'end');
    }
    /**
     * Creates text node and inserts it at the end of the parent. You can optionally set text attributes:
     *
     *		writer.appendText( 'foo', paragraph );
     *		writer.appendText( 'foo', { bold: true }, paragraph );
     *
     * @param {String} text Text data.
     * @param {Object} [attributes] Text attributes.
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} parent
     */
    appendText(text, attributes, parent) {
        if (attributes instanceof documentfragment_DocumentFragment || attributes instanceof element_Element) {
            this.insert(this.createText(text), attributes, 'end');
        }
        else {
            this.insert(this.createText(text, attributes), parent, 'end');
        }
    }
    /**
     * Creates element and inserts it at the end of the parent. You can optionally set attributes:
     *
     *		writer.appendElement( 'paragraph', root );
     *		writer.appendElement( 'paragraph', { alignment: 'center' }, root );
     *
     * @param {String} name Name of the element.
     * @param {Object} [attributes] Elements attributes.
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} parent
     */
    appendElement(name, attributes, parent) {
        if (attributes instanceof documentfragment_DocumentFragment || attributes instanceof element_Element) {
            this.insert(this.createElement(name), attributes, 'end');
        }
        else {
            this.insert(this.createElement(name, attributes), parent, 'end');
        }
    }
    /**
     * Sets value of the attribute with given key on a {@link module:engine/model/item~Item model item}
     * or on a {@link module:engine/model/range~Range range}.
     *
     * @param {String} key Attribute key.
     * @param {*} value Attribute new value.
     * @param {module:engine/model/item~Item|module:engine/model/range~Range} itemOrRange
     * Model item or range on which the attribute will be set.
     */
    setAttribute(key, value, itemOrRange) {
        this._assertWriterUsedCorrectly();
        if (itemOrRange instanceof range_Range) {
            const ranges = itemOrRange.getMinimalFlatRanges();
            for (const range of ranges) {
                setAttributeOnRange(this, key, value, range);
            }
        }
        else {
            setAttributeOnItem(this, key, value, itemOrRange);
        }
    }
    /**
     * Sets values of attributes on a {@link module:engine/model/item~Item model item}
     * or on a {@link module:engine/model/range~Range range}.
     *
     *		writer.setAttributes( {
     *			bold: true,
     *			italic: true
     *		}, range );
     *
     * @param {Object} attributes Attributes keys and values.
     * @param {module:engine/model/item~Item|module:engine/model/range~Range} itemOrRange
     * Model item or range on which the attributes will be set.
     */
    setAttributes(attributes, itemOrRange) {
        for (const [key, val] of toMap(attributes)) {
            this.setAttribute(key, val, itemOrRange);
        }
    }
    /**
     * Removes an attribute with given key from a {@link module:engine/model/item~Item model item}
     * or from a {@link module:engine/model/range~Range range}.
     *
     * @param {String} key Attribute key.
     * @param {module:engine/model/item~Item|module:engine/model/range~Range} itemOrRange
     * Model item or range from which the attribute will be removed.
     */
    removeAttribute(key, itemOrRange) {
        this._assertWriterUsedCorrectly();
        if (itemOrRange instanceof range_Range) {
            const ranges = itemOrRange.getMinimalFlatRanges();
            for (const range of ranges) {
                setAttributeOnRange(this, key, null, range);
            }
        }
        else {
            setAttributeOnItem(this, key, null, itemOrRange);
        }
    }
    /**
     * Removes all attributes from all elements in the range or from the given item.
     *
     * @param {module:engine/model/item~Item|module:engine/model/range~Range} itemOrRange
     * Model item or range from which all attributes will be removed.
     */
    clearAttributes(itemOrRange) {
        this._assertWriterUsedCorrectly();
        const removeAttributesFromItem = (item) => {
            for (const attribute of item.getAttributeKeys()) {
                this.removeAttribute(attribute, item);
            }
        };
        if (!(itemOrRange instanceof range_Range)) {
            removeAttributesFromItem(itemOrRange);
        }
        else {
            for (const item of itemOrRange.getItems()) {
                removeAttributesFromItem(item);
            }
        }
    }
    /**
     * Moves all items in the source range to the target position.
     *
     *		writer.move( sourceRange, targetPosition );
     *
     * Instead of the target position you can use parent and offset or define that range should be moved to the end
     * or before or after chosen item:
     *
     *		// Moves all items in the range to the paragraph at offset 5:
     *		writer.move( sourceRange, paragraph, 5 );
     *		// Moves all items in the range to the end of a blockquote:
     *		writer.move( sourceRange, blockquote, 'end' );
     *		// Moves all items in the range to a position after an image:
     *		writer.move( sourceRange, image, 'after' );
     *
     * These parameters works the same way as {@link #createPositionAt `writer.createPositionAt()`}.
     *
     * Note that items can be moved only within the same tree. It means that you can move items within the same root
     * (element or document fragment) or between {@link module:engine/model/document~Document#roots documents roots},
     * but you can not move items from document fragment to the document or from one detached element to another. Use
     * {@link module:engine/model/writer~Writer#insert} in such cases.
     *
     * @param {module:engine/model/range~Range} range Source range.
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * second parameter is a {@link module:engine/model/item~Item model item}.
     */
    move(range, itemOrPosition, offset) {
        this._assertWriterUsedCorrectly();
        if (!(range instanceof range_Range)) {
            /**
             * Invalid range to move.
             *
             * @error writer-move-invalid-range
             */
            throw new CKEditorError('writer-move-invalid-range', this);
        }
        if (!range.isFlat) {
            /**
             * Range to move is not flat.
             *
             * @error writer-move-range-not-flat
             */
            throw new CKEditorError('writer-move-range-not-flat', this);
        }
        const position = position_Position._createAt(itemOrPosition, offset);
        // Do not move anything if the move target is same as moved range start.
        if (position.isEqual(range.start)) {
            return;
        }
        // If part of the marker is removed, create additional marker operation for undo purposes.
        this._addOperationForAffectedMarkers('move', range);
        if (!isSameTree(range.root, position.root)) {
            /**
             * Range is going to be moved within not the same document. Please use
             * {@link module:engine/model/writer~Writer#insert insert} instead.
             *
             * @error writer-move-different-document
             */
            throw new CKEditorError('writer-move-different-document', this);
        }
        const version = range.root.document ? range.root.document.version : null;
        const operation = new MoveOperation(range.start, range.end.offset - range.start.offset, position, version);
        this.batch.addOperation(operation);
        this.model.applyOperation(operation);
    }
    /**
     * Removes given model {@link module:engine/model/item~Item item} or {@link module:engine/model/range~Range range}.
     *
     * @param {module:engine/model/item~Item|module:engine/model/range~Range} itemOrRange Model item or range to remove.
     */
    remove(itemOrRange) {
        this._assertWriterUsedCorrectly();
        const rangeToRemove = itemOrRange instanceof range_Range ? itemOrRange : range_Range._createOn(itemOrRange);
        const ranges = rangeToRemove.getMinimalFlatRanges().reverse();
        for (const flat of ranges) {
            // If part of the marker is removed, create additional marker operation for undo purposes.
            this._addOperationForAffectedMarkers('move', flat);
            applyRemoveOperation(flat.start, flat.end.offset - flat.start.offset, this.batch, this.model);
        }
    }
    /**
     * Merges two siblings at the given position.
     *
     * Node before and after the position have to be an element. Otherwise `writer-merge-no-element-before` or
     * `writer-merge-no-element-after` error will be thrown.
     *
     * @param {module:engine/model/position~Position} position Position between merged elements.
     */
    merge(position) {
        this._assertWriterUsedCorrectly();
        const nodeBefore = position.nodeBefore;
        const nodeAfter = position.nodeAfter;
        // If part of the marker is removed, create additional marker operation for undo purposes.
        this._addOperationForAffectedMarkers('merge', position);
        if (!(nodeBefore instanceof element_Element)) {
            /**
             * Node before merge position must be an element.
             *
             * @error writer-merge-no-element-before
             */
            throw new CKEditorError('writer-merge-no-element-before', this);
        }
        if (!(nodeAfter instanceof element_Element)) {
            /**
             * Node after merge position must be an element.
             *
             * @error writer-merge-no-element-after
             */
            throw new CKEditorError('writer-merge-no-element-after', this);
        }
        if (!position.root.document) {
            this._mergeDetached(position);
        }
        else {
            this._merge(position);
        }
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createPositionFromPath `Model#createPositionFromPath()`}.
     *
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} root Root of the position.
     * @param {Array.<Number>} path Position path. See {@link module:engine/model/position~Position#path}.
     * @param {module:engine/model/position~PositionStickiness} [stickiness='toNone'] Position stickiness.
     * See {@link module:engine/model/position~PositionStickiness}.
     * @returns {module:engine/model/position~Position}
     */
    createPositionFromPath(root, path, stickiness) {
        return this.model.createPositionFromPath(root, path, stickiness);
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createPositionAt `Model#createPositionAt()`}.
     *
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/model/item~Item model item}.
     * @returns {module:engine/model/position~Position}
     */
    createPositionAt(itemOrPosition, offset) {
        return this.model.createPositionAt(itemOrPosition, offset);
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createPositionAfter `Model#createPositionAfter()`}.
     *
     * @param {module:engine/model/item~Item} item Item after which the position should be placed.
     * @returns {module:engine/model/position~Position}
     */
    createPositionAfter(item) {
        return this.model.createPositionAfter(item);
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createPositionBefore `Model#createPositionBefore()`}.
     *
     * @param {module:engine/model/item~Item} item Item after which the position should be placed.
     * @returns {module:engine/model/position~Position}
     */
    createPositionBefore(item) {
        return this.model.createPositionBefore(item);
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createRange `Model#createRange()`}.
     *
     * @param {module:engine/model/position~Position} start Start position.
     * @param {module:engine/model/position~Position} [end] End position. If not set, range will be collapsed at `start` position.
     * @returns {module:engine/model/range~Range}
     */
    createRange(start, end) {
        return this.model.createRange(start, end);
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createRangeIn `Model#createRangeIn()`}.
     *
     * @param {module:engine/model/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/model/range~Range}
     */
    createRangeIn(element) {
        return this.model.createRangeIn(element);
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createRangeOn `Model#createRangeOn()`}.
     *
     * @param {module:engine/model/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/model/range~Range}
     */
    createRangeOn(element) {
        return this.model.createRangeOn(element);
    }
    /**
     * Shortcut for {@link module:engine/model/model~Model#createSelection `Model#createSelection()`}.
     *
     * @param {module:engine/model/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @returns {module:engine/model/selection~Selection}
     */
    createSelection(...args) {
        return this.model.createSelection(...args);
    }
    /**
     * Performs merge action in a detached tree.
     *
     * @private
     * @param {module:engine/model/position~Position} position Position between merged elements.
     */
    _mergeDetached(position) {
        const nodeBefore = position.nodeBefore;
        const nodeAfter = position.nodeAfter;
        this.move(range_Range._createIn(nodeAfter), position_Position._createAt(nodeBefore, 'end'));
        this.remove(nodeAfter);
    }
    /**
     * Performs merge action in a non-detached tree.
     *
     * @private
     * @param {module:engine/model/position~Position} position Position between merged elements.
     */
    _merge(position) {
        const targetPosition = position_Position._createAt(position.nodeBefore, 'end');
        const sourcePosition = position_Position._createAt(position.nodeAfter, 0);
        const graveyard = position.root.document.graveyard;
        const graveyardPosition = new position_Position(graveyard, [0]);
        const version = position.root.document.version;
        const merge = new MergeOperation(sourcePosition, position.nodeAfter.maxOffset, targetPosition, graveyardPosition, version);
        this.batch.addOperation(merge);
        this.model.applyOperation(merge);
    }
    /**
     * Renames the given element.
     *
     * @param {module:engine/model/element~Element} element The element to rename.
     * @param {String} newName New element name.
     */
    rename(element, newName) {
        this._assertWriterUsedCorrectly();
        if (!(element instanceof element_Element)) {
            /**
             * Trying to rename an object which is not an instance of Element.
             *
             * @error writer-rename-not-element-instance
             */
            throw new CKEditorError('writer-rename-not-element-instance', this);
        }
        const version = element.root.document ? element.root.document.version : null;
        const renameOperation = new RenameOperation(position_Position._createBefore(element), element.name, newName, version);
        this.batch.addOperation(renameOperation);
        this.model.applyOperation(renameOperation);
    }
    /**
     * Splits elements starting from the given position and going to the top of the model tree as long as given
     * `limitElement` is reached. When `limitElement` is not defined then only the parent of the given position will be split.
     *
     * The element needs to have a parent. It cannot be a root element nor a document fragment.
     * The `writer-split-element-no-parent` error will be thrown if you try to split an element with no parent.
     *
     * @param {module:engine/model/position~Position} position Position of split.
     * @param {module:engine/model/node~Node} [limitElement] Stop splitting when this element will be reached.
     * @returns {Object} result Split result.
     * @returns {module:engine/model/position~Position} result.position Position between split elements.
     * @returns {module:engine/model/range~Range} result.range Range that stars from the end of the first split element and ends
     * at the beginning of the first copy element.
     */
    split(position, limitElement) {
        this._assertWriterUsedCorrectly();
        let splitElement = position.parent;
        if (!splitElement.parent) {
            /**
             * Element with no parent can not be split.
             *
             * @error writer-split-element-no-parent
             */
            throw new CKEditorError('writer-split-element-no-parent', this);
        }
        // When limit element is not defined lets set splitElement parent as limit.
        if (!limitElement) {
            limitElement = splitElement.parent;
        }
        if (!position.parent.getAncestors({ includeSelf: true }).includes(limitElement)) {
            /**
             * Limit element is not a position ancestor.
             *
             * @error writer-split-invalid-limit-element
             */
            throw new CKEditorError('writer-split-invalid-limit-element', this);
        }
        // We need to cache elements that will be created as a result of the first split because
        // we need to create a range from the end of the first split element to the beginning of the
        // first copy element. This should be handled by LiveRange but it doesn't work on detached nodes.
        let firstSplitElement;
        let firstCopyElement;
        do {
            const version = splitElement.root.document ? splitElement.root.document.version : null;
            const howMany = splitElement.maxOffset - position.offset;
            const insertionPosition = SplitOperation.getInsertionPosition(position);
            const split = new SplitOperation(position, howMany, insertionPosition, null, version);
            this.batch.addOperation(split);
            this.model.applyOperation(split);
            // Cache result of the first split.
            if (!firstSplitElement && !firstCopyElement) {
                firstSplitElement = splitElement;
                firstCopyElement = position.parent.nextSibling;
            }
            position = this.createPositionAfter(position.parent);
            splitElement = position.parent;
        } while (splitElement !== limitElement);
        return {
            position,
            range: new range_Range(position_Position._createAt(firstSplitElement, 'end'), position_Position._createAt(firstCopyElement, 0))
        };
    }
    /**
     * Wraps the given range with the given element or with a new element (if a string was passed).
     *
     * **Note:** range to wrap should be a "flat range" (see {@link module:engine/model/range~Range#isFlat `Range#isFlat`}).
     * If not, an error will be thrown.
     *
     * @param {module:engine/model/range~Range} range Range to wrap.
     * @param {module:engine/model/element~Element|String} elementOrString Element or name of element to wrap the range with.
     */
    wrap(range, elementOrString) {
        this._assertWriterUsedCorrectly();
        if (!range.isFlat) {
            /**
             * Range to wrap is not flat.
             *
             * @error writer-wrap-range-not-flat
             */
            throw new CKEditorError('writer-wrap-range-not-flat', this);
        }
        const element = elementOrString instanceof element_Element ? elementOrString : new element_Element(elementOrString);
        if (element.childCount > 0) {
            /**
             * Element to wrap with is not empty.
             *
             * @error writer-wrap-element-not-empty
             */
            throw new CKEditorError('writer-wrap-element-not-empty', this);
        }
        if (element.parent !== null) {
            /**
             * Element to wrap with is already attached to a tree model.
             *
             * @error writer-wrap-element-attached
             */
            throw new CKEditorError('writer-wrap-element-attached', this);
        }
        this.insert(element, range.start);
        // Shift the range-to-wrap because we just inserted an element before that range.
        const shiftedRange = new range_Range(range.start.getShiftedBy(1), range.end.getShiftedBy(1));
        this.move(shiftedRange, position_Position._createAt(element, 0));
    }
    /**
     * Unwraps children of the given element – all its children are moved before it and then the element is removed.
     * Throws error if you try to unwrap an element which does not have a parent.
     *
     * @param {module:engine/model/element~Element} element Element to unwrap.
     */
    unwrap(element) {
        this._assertWriterUsedCorrectly();
        if (element.parent === null) {
            /**
             * Trying to unwrap an element which has no parent.
             *
             * @error writer-unwrap-element-no-parent
             */
            throw new CKEditorError('writer-unwrap-element-no-parent', this);
        }
        this.move(range_Range._createIn(element), this.createPositionAfter(element));
        this.remove(element);
    }
    /**
     * Adds a {@link module:engine/model/markercollection~Marker marker}. Marker is a named range, which tracks
     * changes in the document and updates its range automatically, when model tree changes.
     *
     * As the first parameter you can set marker name.
     *
     * The required `options.usingOperation` parameter lets you decide if the marker should be managed by operations or not. See
     * {@link module:engine/model/markercollection~Marker marker class description} to learn about the difference between
     * markers managed by operations and not-managed by operations.
     *
     * The `options.affectsData` parameter, which defaults to `false`, allows you to define if a marker affects the data. It should be
     * `true` when the marker change changes the data returned by the
     * {@link module:core/editor/utils/dataapimixin~DataApi#getData `editor.getData()`} method.
     * When set to `true` it fires the {@link module:engine/model/document~Document#event:change:data `change:data`} event.
     * When set to `false` it fires the {@link module:engine/model/document~Document#event:change `change`} event.
     *
     * Create marker directly base on marker's name:
     *
     *		addMarker( markerName, { range, usingOperation: false } );
     *
     * Create marker using operation:
     *
     *		addMarker( markerName, { range, usingOperation: true } );
     *
     * Create marker that affects the editor data:
     *
     *		addMarker( markerName, { range, usingOperation: false, affectsData: true } );
     *
     * Note: For efficiency reasons, it's best to create and keep as little markers as possible.
     *
     * @see module:engine/model/markercollection~Marker
     * @param {String} name Name of a marker to create - must be unique.
     * @param {Object} options
     * @param {Boolean} options.usingOperation Flag indicating that the marker should be added by MarkerOperation.
     * See {@link module:engine/model/markercollection~Marker#managedUsingOperations}.
     * @param {module:engine/model/range~Range} options.range Marker range.
     * @param {Boolean} [options.affectsData=false] Flag indicating that the marker changes the editor data.
     * @returns {module:engine/model/markercollection~Marker} Marker that was set.
     */
    addMarker(name, options) {
        this._assertWriterUsedCorrectly();
        if (!options || typeof options.usingOperation != 'boolean') {
            /**
             * The `options.usingOperation` parameter is required when adding a new marker.
             *
             * @error writer-addmarker-no-usingoperation
             */
            throw new CKEditorError('writer-addmarker-no-usingoperation', this);
        }
        const usingOperation = options.usingOperation;
        const range = options.range;
        const affectsData = options.affectsData === undefined ? false : options.affectsData;
        if (this.model.markers.has(name)) {
            /**
             * Marker with provided name already exists.
             *
             * @error writer-addmarker-marker-exists
             */
            throw new CKEditorError('writer-addmarker-marker-exists', this);
        }
        if (!range) {
            /**
             * Range parameter is required when adding a new marker.
             *
             * @error writer-addmarker-no-range
             */
            throw new CKEditorError('writer-addmarker-no-range', this);
        }
        if (!usingOperation) {
            return this.model.markers._set(name, range, usingOperation, affectsData);
        }
        applyMarkerOperation(this, name, null, range, affectsData);
        return this.model.markers.get(name);
    }
    /**
     * Adds, updates or refreshes a {@link module:engine/model/markercollection~Marker marker}. Marker is a named range, which tracks
     * changes in the document and updates its range automatically, when model tree changes. Still, it is possible to change the
     * marker's range directly using this method.
     *
     * As the first parameter you can set marker name or instance. If none of them is provided, new marker, with a unique
     * name is created and returned.
     *
     * **Note**: If you want to change the {@link module:engine/view/element~Element view element} of the marker while its data in the model
     * remains the same, use the dedicated {@link module:engine/controller/editingcontroller~EditingController#reconvertMarker} method.
     *
     * The `options.usingOperation` parameter lets you change if the marker should be managed by operations or not. See
     * {@link module:engine/model/markercollection~Marker marker class description} to learn about the difference between
     * markers managed by operations and not-managed by operations. It is possible to change this option for an existing marker.
     *
     * The `options.affectsData` parameter, which defaults to `false`, allows you to define if a marker affects the data. It should be
     * `true` when the marker change changes the data returned by
     * the {@link module:core/editor/utils/dataapimixin~DataApi#getData `editor.getData()`} method.
     * When set to `true` it fires the {@link module:engine/model/document~Document#event:change:data `change:data`} event.
     * When set to `false` it fires the {@link module:engine/model/document~Document#event:change `change`} event.
     *
     * Update marker directly base on marker's name:
     *
     *		updateMarker( markerName, { range } );
     *
     * Update marker using operation:
     *
     *		updateMarker( marker, { range, usingOperation: true } );
     *		updateMarker( markerName, { range, usingOperation: true } );
     *
     * Change marker's option (start using operations to manage it):
     *
     *		updateMarker( marker, { usingOperation: true } );
     *
     * Change marker's option (inform the engine, that the marker does not affect the data anymore):
     *
     *		updateMarker( markerName, { affectsData: false } );
     *
     * @see module:engine/model/markercollection~Marker
     * @param {String|module:engine/model/markercollection~Marker} markerOrName Name of a marker to update, or a marker instance.
     * @param {Object} [options] If options object is not defined then marker will be refreshed by triggering
     * downcast conversion for this marker with the same data.
     * @param {module:engine/model/range~Range} [options.range] Marker range to update.
     * @param {Boolean} [options.usingOperation] Flag indicated whether the marker should be added by MarkerOperation.
     * See {@link module:engine/model/markercollection~Marker#managedUsingOperations}.
     * @param {Boolean} [options.affectsData] Flag indicating that the marker changes the editor data.
     */
    updateMarker(markerOrName, options) {
        this._assertWriterUsedCorrectly();
        const markerName = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
        const currentMarker = this.model.markers.get(markerName);
        if (!currentMarker) {
            /**
             * Marker with provided name does not exist and will not be updated.
             *
             * @error writer-updatemarker-marker-not-exists
             */
            throw new CKEditorError('writer-updatemarker-marker-not-exists', this);
        }
        if (!options) {
            /**
             * The usage of `writer.updateMarker()` only to reconvert (refresh) a
             * {@link module:engine/model/markercollection~Marker model marker} was deprecated and may not work in the future.
             * Please update your code to use
             * {@link module:engine/controller/editingcontroller~EditingController#reconvertMarker `editor.editing.reconvertMarker()`}
             * instead.
             *
             * @error writer-updatemarker-reconvert-using-editingcontroller
             * @param {String} markerName The name of the updated marker.
             */
            logWarning('writer-updatemarker-reconvert-using-editingcontroller', { markerName });
            this.model.markers._refresh(currentMarker);
            return;
        }
        const hasUsingOperationDefined = typeof options.usingOperation == 'boolean';
        const affectsDataDefined = typeof options.affectsData == 'boolean';
        // Use previously defined marker's affectsData if the property is not provided.
        const affectsData = affectsDataDefined ? options.affectsData : currentMarker.affectsData;
        if (!hasUsingOperationDefined && !options.range && !affectsDataDefined) {
            /**
             * One of the options is required - provide range, usingOperations or affectsData.
             *
             * @error writer-updatemarker-wrong-options
             */
            throw new CKEditorError('writer-updatemarker-wrong-options', this);
        }
        const currentRange = currentMarker.getRange();
        const updatedRange = options.range ? options.range : currentRange;
        if (hasUsingOperationDefined && options.usingOperation !== currentMarker.managedUsingOperations) {
            // The marker type is changed so it's necessary to create proper operations.
            if (options.usingOperation) {
                // If marker changes to a managed one treat this as synchronizing existing marker.
                // Create `MarkerOperation` with `oldRange` set to `null`, so reverse operation will remove the marker.
                applyMarkerOperation(this, markerName, null, updatedRange, affectsData);
            }
            else {
                // If marker changes to a marker that do not use operations then we need to create additional operation
                // that removes that marker first.
                applyMarkerOperation(this, markerName, currentRange, null, affectsData);
                // Although not managed the marker itself should stay in model and its range should be preserver or changed to passed range.
                this.model.markers._set(markerName, updatedRange, undefined, affectsData);
            }
            return;
        }
        // Marker's type doesn't change so update it accordingly.
        if (currentMarker.managedUsingOperations) {
            applyMarkerOperation(this, markerName, currentRange, updatedRange, affectsData);
        }
        else {
            this.model.markers._set(markerName, updatedRange, undefined, affectsData);
        }
    }
    /**
     * Removes given {@link module:engine/model/markercollection~Marker marker} or marker with given name.
     * The marker is removed accordingly to how it has been created, so if the marker was created using operation,
     * it will be destroyed using operation.
     *
     * @param {module:engine/model/markercollection~Marker|String} markerOrName Marker or marker name to remove.
     */
    removeMarker(markerOrName) {
        this._assertWriterUsedCorrectly();
        const name = typeof markerOrName == 'string' ? markerOrName : markerOrName.name;
        if (!this.model.markers.has(name)) {
            /**
             * Trying to remove marker which does not exist.
             *
             * @error writer-removemarker-no-marker
             */
            throw new CKEditorError('writer-removemarker-no-marker', this);
        }
        const marker = this.model.markers.get(name);
        if (!marker.managedUsingOperations) {
            this.model.markers._remove(name);
            return;
        }
        const oldRange = marker.getRange();
        applyMarkerOperation(this, name, oldRange, null, marker.affectsData);
    }
    /**
     * Sets the document's selection (ranges and direction) to the specified location based on the given
     * {@link module:engine/model/selection~Selectable selectable} or creates an empty selection if no arguments were passed.
     *
     *		// Sets selection to the given range.
     *		const range = writer.createRange( start, end );
     *		writer.setSelection( range );
     *
     *		// Sets selection to given ranges.
     *		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		writer.setSelection( ranges );
     *
     *		// Sets selection to other selection.
     *		const otherSelection = writer.createSelection();
     *		writer.setSelection( otherSelection );
     *
     *		// Sets selection to the given document selection.
     *		const documentSelection = model.document.selection;
     *		writer.setSelection( documentSelection );
     *
     *		// Sets collapsed selection at the given position.
     *		const position = writer.createPosition( root, path );
     *		writer.setSelection( position );
     *
     *		// Sets collapsed selection at the position of the given node and an offset.
     *		writer.setSelection( paragraph, offset );
     *
     * Creates a range inside an {@link module:engine/model/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     *		writer.setSelection( paragraph, 'in' );
     *
     * Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends just after the item.
     *
     *		writer.setSelection( paragraph, 'on' );
     *
     *		// Removes all selection's ranges.
     *		writer.setSelection( null );
     *
     * `Writer#setSelection()` allow passing additional options (`backward`) as the last argument.
     *
     *		// Sets selection as backward.
     *		writer.setSelection( range, { backward: true } );
     *
     * Throws `writer-incorrect-use` error when the writer is used outside the `change()` block.
     *
     * @param {module:engine/model/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     */
    setSelection(...args) {
        this._assertWriterUsedCorrectly();
        this.model.document.selection._setTo(...args);
    }
    /**
     * Moves {@link module:engine/model/documentselection~DocumentSelection#focus} to the specified location.
     *
     * The location can be specified in the same form as
     * {@link #createPositionAt `writer.createPositionAt()`} parameters.
     *
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset=0] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/model/item~Item model item}.
     */
    setSelectionFocus(itemOrPosition, offset) {
        this._assertWriterUsedCorrectly();
        this.model.document.selection._setFocus(itemOrPosition, offset);
    }
    /**
     * Sets attribute(s) on the selection. If attribute with the same key already is set, it's value is overwritten.
     *
     * Using key and value pair:
     *
     * 	writer.setSelectionAttribute( 'italic', true );
     *
     * Using key-value object:
     *
     * 	writer.setSelectionAttribute( { italic: true, bold: false } );
     *
     * Using iterable object:
     *
     * 	writer.setSelectionAttribute( new Map( [ [ 'italic', true ] ] ) );
     *
     * @param {String|Object|Iterable.<*>} keyOrObjectOrIterable Key of the attribute to set
     * or object / iterable of key => value attribute pairs.
     * @param {*} [value] Attribute value.
     */
    setSelectionAttribute(keyOrObjectOrIterable, value) {
        this._assertWriterUsedCorrectly();
        if (typeof keyOrObjectOrIterable === 'string') {
            this._setSelectionAttribute(keyOrObjectOrIterable, value);
        }
        else {
            for (const [key, value] of toMap(keyOrObjectOrIterable)) {
                this._setSelectionAttribute(key, value);
            }
        }
    }
    /**
     * Removes attribute(s) with given key(s) from the selection.
     *
     * Remove one attribute:
     *
     *		writer.removeSelectionAttribute( 'italic' );
     *
     * Remove multiple attributes:
     *
     *		writer.removeSelectionAttribute( [ 'italic', 'bold' ] );
     *
     * @param {String|Iterable.<String>} keyOrIterableOfKeys Key of the attribute to remove or an iterable of attribute keys to remove.
     */
    removeSelectionAttribute(keyOrIterableOfKeys) {
        this._assertWriterUsedCorrectly();
        if (typeof keyOrIterableOfKeys === 'string') {
            this._removeSelectionAttribute(keyOrIterableOfKeys);
        }
        else {
            for (const key of keyOrIterableOfKeys) {
                this._removeSelectionAttribute(key);
            }
        }
    }
    /**
     * Temporarily changes the {@link module:engine/model/documentselection~DocumentSelection#isGravityOverridden gravity}
     * of the selection from left to right.
     *
     * The gravity defines from which direction the selection inherits its attributes. If it's the default left gravity,
     * then the selection (after being moved by the user) inherits attributes from its left-hand side.
     * This method allows to temporarily override this behavior by forcing the gravity to the right.
     *
     * For the following model fragment:
     *
     *		<$text bold="true" linkHref="url">bar[]</$text><$text bold="true">biz</$text>
     *
     * * Default gravity: selection will have the `bold` and `linkHref` attributes.
     * * Overridden gravity: selection will have `bold` attribute.
     *
     * **Note**: It returns an unique identifier which is required to restore the gravity. It guarantees the symmetry
     * of the process.
     *
     * @returns {String} The unique id which allows restoring the gravity.
     */
    overrideSelectionGravity() {
        return this.model.document.selection._overrideGravity();
    }
    /**
     * Restores {@link ~Writer#overrideSelectionGravity} gravity to default.
     *
     * Restoring the gravity is only possible using the unique identifier returned by
     * {@link ~Writer#overrideSelectionGravity}. Note that the gravity remains overridden as long as won't be restored
     * the same number of times it was overridden.
     *
     * @param {String} uid The unique id returned by {@link ~Writer#overrideSelectionGravity}.
     */
    restoreSelectionGravity(uid) {
        this.model.document.selection._restoreGravity(uid);
    }
    /**
     * @private
     * @param {String} key Key of the attribute to remove.
     * @param {*} value Attribute value.
     */
    _setSelectionAttribute(key, value) {
        const selection = this.model.document.selection;
        // Store attribute in parent element if the selection is collapsed in an empty node.
        if (selection.isCollapsed && selection.anchor.parent.isEmpty) {
            const storeKey = documentselection_DocumentSelection._getStoreAttributeKey(key);
            this.setAttribute(storeKey, value, selection.anchor.parent);
        }
        selection._setAttribute(key, value);
    }
    /**
     * @private
     * @param {String} key Key of the attribute to remove.
     */
    _removeSelectionAttribute(key) {
        const selection = this.model.document.selection;
        // Remove stored attribute from parent element if the selection is collapsed in an empty node.
        if (selection.isCollapsed && selection.anchor.parent.isEmpty) {
            const storeKey = documentselection_DocumentSelection._getStoreAttributeKey(key);
            this.removeAttribute(storeKey, selection.anchor.parent);
        }
        selection._removeAttribute(key);
    }
    /**
     * Throws `writer-detached-writer-tries-to-modify-model` error when the writer is used outside of the `change()` block.
     *
     * @private
     */
    _assertWriterUsedCorrectly() {
        /**
         * Trying to use a writer outside a {@link module:engine/model/model~Model#change `change()`} or
         * {@link module:engine/model/model~Model#enqueueChange `enqueueChange()`} blocks.
         *
         * The writer can only be used inside these blocks which ensures that the model
         * can only be changed during such "sessions".
         *
         * @error writer-incorrect-use
         */
        if (this.model._currentWriter !== this) {
            throw new CKEditorError('writer-incorrect-use', this);
        }
    }
    /**
     * For given action `type` and `positionOrRange` where the action happens, this function finds all affected markers
     * and applies a marker operation with the new marker range equal to the current range. Thanks to this, the marker range
     * can be later correctly processed during undo.
     *
     * @private
     * @param {'move'|'merge'} type Writer action type.
     * @param {module:engine/model/position~Position|module:engine/model/range~Range} positionOrRange Position or range
     * where the writer action happens.
     */
    _addOperationForAffectedMarkers(type, positionOrRange) {
        for (const marker of this.model.markers) {
            if (!marker.managedUsingOperations) {
                continue;
            }
            const markerRange = marker.getRange();
            let isAffected = false;
            if (type === 'move') {
                const range = positionOrRange;
                isAffected =
                    range.containsPosition(markerRange.start) ||
                        range.start.isEqual(markerRange.start) ||
                        range.containsPosition(markerRange.end) ||
                        range.end.isEqual(markerRange.end);
            }
            else {
                // if type === 'merge'.
                const position = positionOrRange;
                const elementBefore = position.nodeBefore;
                const elementAfter = position.nodeAfter;
                //               Start:  <p>Foo[</p><p>Bar]</p>
                //         After merge:  <p>Foo[Bar]</p>
                // After undoing split:  <p>Foo</p><p>[Bar]</p>     <-- incorrect, needs remembering for undo.
                //
                const affectedInLeftElement = markerRange.start.parent == elementBefore && markerRange.start.isAtEnd;
                //               Start:  <p>[Foo</p><p>]Bar</p>
                //         After merge:  <p>[Foo]Bar</p>
                // After undoing split:  <p>[Foo]</p><p>Bar</p>     <-- incorrect, needs remembering for undo.
                //
                const affectedInRightElement = markerRange.end.parent == elementAfter && markerRange.end.offset == 0;
                //               Start:  <p>[Foo</p>]<p>Bar</p>
                //         After merge:  <p>[Foo]Bar</p>
                // After undoing split:  <p>[Foo]</p><p>Bar</p>     <-- incorrect, needs remembering for undo.
                //
                const affectedAfterLeftElement = markerRange.end.nodeAfter == elementAfter;
                //               Start:  <p>Foo</p>[<p>Bar]</p>
                //         After merge:  <p>Foo[Bar]</p>
                // After undoing split:  <p>Foo</p><p>[Bar]</p>     <-- incorrect, needs remembering for undo.
                //
                const affectedBeforeRightElement = markerRange.start.nodeAfter == elementAfter;
                isAffected = affectedInLeftElement || affectedInRightElement || affectedAfterLeftElement || affectedBeforeRightElement;
            }
            if (isAffected) {
                this.updateMarker(marker.name, { range: markerRange });
            }
        }
    }
}
// Sets given attribute to each node in given range. When attribute value is null then attribute will be removed.
//
// Because attribute operation needs to have the same attribute value on the whole range, this function splits
// the range into smaller parts.
//
// Given `range` must be flat.
//
// @private
// @param {module:engine/model/writer~Writer} writer
// @param {String} key Attribute key.
// @param {*} value Attribute new value.
// @param {module:engine/model/range~Range} range Model range on which the attribute will be set.
function setAttributeOnRange(writer, key, value, range) {
    const model = writer.model;
    const doc = model.document;
    // Position of the last split, the beginning of the new range.
    let lastSplitPosition = range.start;
    // Currently position in the scanning range. Because we need value after the position, it is not a current
    // position of the iterator but the previous one (we need to iterate one more time to get the value after).
    let position;
    // Value before the currently position.
    let valueBefore;
    // Value after the currently position.
    let valueAfter;
    for (const val of range.getWalker({ shallow: true })) {
        valueAfter = val.item.getAttribute(key);
        // At the first run of the iterator the position in undefined. We also do not have a valueBefore, but
        // because valueAfter may be null, valueBefore may be equal valueAfter ( undefined == null ).
        if (position && valueBefore != valueAfter) {
            // if valueBefore == value there is nothing to change, so we add operation only if these values are different.
            if (valueBefore != value) {
                addOperation();
            }
            lastSplitPosition = position;
        }
        position = val.nextPosition;
        valueBefore = valueAfter;
    }
    // Because position in the loop is not the iterator position (see let position comment), the last position in
    // the while loop will be last but one position in the range. We need to check the last position manually.
    if (position instanceof position_Position && position != lastSplitPosition && valueBefore != value) {
        addOperation();
    }
    function addOperation() {
        const range = new range_Range(lastSplitPosition, position);
        const version = range.root.document ? doc.version : null;
        const operation = new AttributeOperation(range, key, valueBefore, value, version);
        writer.batch.addOperation(operation);
        model.applyOperation(operation);
    }
}
// Sets given attribute to the given node. When attribute value is null then attribute will be removed.
//
// @private
// @param {module:engine/model/writer~Writer} writer
// @param {String} key Attribute key.
// @param {*} value Attribute new value.
// @param {module:engine/model/item~Item} item Model item on which the attribute will be set.
function setAttributeOnItem(writer, key, value, item) {
    const model = writer.model;
    const doc = model.document;
    const previousValue = item.getAttribute(key);
    let range, operation;
    if (previousValue != value) {
        const isRootChanged = item.root === item;
        if (isRootChanged) {
            // If we change attributes of root element, we have to use `RootAttributeOperation`.
            const version = item.document ? doc.version : null;
            operation = new RootAttributeOperation(item, key, previousValue, value, version);
        }
        else {
            range = new range_Range(position_Position._createBefore(item), writer.createPositionAfter(item));
            const version = range.root.document ? doc.version : null;
            operation = new AttributeOperation(range, key, previousValue, value, version);
        }
        writer.batch.addOperation(operation);
        model.applyOperation(operation);
    }
}
// Creates and applies marker operation to {@link module:engine/model/operation/operation~Operation operation}.
//
// @private
// @param {module:engine/model/writer~Writer} writer
// @param {String} name Marker name.
// @param {module:engine/model/range~Range} oldRange Marker range before the change.
// @param {module:engine/model/range~Range} newRange Marker range after the change.
// @param {Boolean} affectsData
function applyMarkerOperation(writer, name, oldRange, newRange, affectsData) {
    const model = writer.model;
    const doc = model.document;
    const operation = new MarkerOperation(name, oldRange, newRange, model.markers, !!affectsData, doc.version);
    writer.batch.addOperation(operation);
    model.applyOperation(operation);
}
// Creates `MoveOperation` or `DetachOperation` that removes `howMany` nodes starting from `position`.
// The operation will be applied on given model instance and added to given operation instance.
//
// @private
// @param {module:engine/model/position~Position} position Position from which nodes are removed.
// @param {Number} howMany Number of nodes to remove.
// @param {Batch} batch Batch to which the operation will be added.
// @param {module:engine/model/model~Model} model Model instance on which operation will be applied.
function applyRemoveOperation(position, howMany, batch, model) {
    let operation;
    if (position.root.document) {
        const doc = model.document;
        const graveyardPosition = new position_Position(doc.graveyard, [0]);
        operation = new MoveOperation(position, howMany, graveyardPosition, doc.version);
    }
    else {
        operation = new DetachOperation(position, howMany);
    }
    batch.addOperation(operation);
    model.applyOperation(operation);
}
// Returns `true` if both root elements are the same element or both are documents root elements.
//
// Elements in the same tree can be moved (for instance you can move element form one documents root to another, or
// within the same document fragment), but when element supposed to be moved from document fragment to the document, or
// to another document it should be removed and inserted to avoid problems with OT. This is because features like undo or
// collaboration may track changes on the document but ignore changes on detached fragments and should not get
// unexpected `move` operation.
function isSameTree(rootA, rootB) {
    // If it is the same root this is the same tree.
    if (rootA === rootB) {
        return true;
    }
    // If both roots are documents root it is operation within the document what we still treat as the same tree.
    if (rootA instanceof RootElement && rootB instanceof RootElement) {
        return true;
    }
    return false;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/selection-post-fixer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/selection-post-fixer
 */


/**
 * Injects selection post-fixer to the model.
 *
 * The role of the selection post-fixer is to ensure that the selection is in a correct place
 * after a {@link module:engine/model/model~Model#change `change()`} block was executed.
 *
 * The correct position means that:
 *
 * * All collapsed selection ranges are in a place where the {@link module:engine/model/schema~Schema}
 * allows a `$text`.
 * * None of the selection's non-collapsed ranges crosses a {@link module:engine/model/schema~Schema#isLimit limit element}
 * boundary (a range must be rooted within one limit element).
 * * Only {@link module:engine/model/schema~Schema#isSelectable selectable elements} can be selected from the outside
 * (e.g. `[<paragraph>foo</paragraph>]` is invalid). This rule applies independently to both selection ends, so this
 * selection is correct: `<paragraph>f[oo</paragraph><imageBlock></imageBlock>]`.
 *
 * If the position is not correct, the post-fixer will automatically correct it.
 *
 * ## Fixing a non-collapsed selection
 *
 * See as an example a selection that starts in a P1 element and ends inside the text of a TD element
 * (`[` and `]` are range boundaries and `(l)` denotes an element defined as `isLimit=true`):
 *
 *		root
 *		 |- element P1
 *		 |   |- "foo"                                      root
 *		 |- element TABLE (l)                   P1         TABLE             P2
 *		 |   |- element TR (l)                 f o[o     TR      TR         b a r
 *		 |   |   |- element TD (l)                       TD      TD
 *		 |   |       |- "aaa"                          a]a a    b b b
 *		 |   |- element TR (l)
 *		 |   |   |- element TD (l)                           ||
 *		 |   |       |- "bbb"                                ||
 *		 |- element P2                                       VV
 *		 |   |- "bar"
 *		                                                   root
 *		                                        P1         TABLE]            P2
 *		                                       f o[o     TR      TR         b a r
 *		                                                 TD      TD
 *		                                               a a a    b b b
 *
 * In the example above, the TABLE, TR and TD are defined as `isLimit=true` in the schema. The range which is not contained within
 * a single limit element must be expanded to select the outermost limit element. The range end is inside the text node of the TD element.
 * As the TD element is a child of the TR and TABLE elements, where both are defined as `isLimit=true` in the schema, the range must be
 * expanded to select the whole TABLE element.
 *
 * **Note** If the selection contains multiple ranges, the method returns a minimal set of ranges that are not intersecting after expanding
 * them to select `isLimit=true` elements.
 *
 * @param {module:engine/model/model~Model} model
 */
function injectSelectionPostFixer(model) {
    model.document.registerPostFixer(writer => selectionPostFixer(writer, model));
}
// The selection post-fixer.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/model~Model} model
function selectionPostFixer(writer, model) {
    const selection = model.document.selection;
    const schema = model.schema;
    const ranges = [];
    let wasFixed = false;
    for (const modelRange of selection.getRanges()) {
        // Go through all ranges in selection and try fixing each of them.
        // Those ranges might overlap but will be corrected later.
        const correctedRange = tryFixingRange(modelRange, schema);
        // "Selection fixing" algorithms sometimes get lost. In consequence, it may happen
        // that a new range is returned but, in fact, it has the same positions as the original
        // range anyway. If this range is not discarded, a new selection will be set and that,
        // for instance, would destroy the selection attributes. Let's make sure that the post-fixer
        // actually worked first before setting a new selection.
        //
        // https://github.com/ckeditor/ckeditor5/issues/6693
        if (correctedRange && !correctedRange.isEqual(modelRange)) {
            ranges.push(correctedRange);
            wasFixed = true;
        }
        else {
            ranges.push(modelRange);
        }
    }
    // If any of ranges were corrected update the selection.
    if (wasFixed) {
        writer.setSelection(mergeIntersectingRanges(ranges), { backward: selection.isBackward });
    }
    return false;
}
// Tries fixing a range if it's incorrect.
//
// @param {module:engine/model/range~Range} range
// @param {module:engine/model/schema~Schema} schema
// @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.
function tryFixingRange(range, schema) {
    if (range.isCollapsed) {
        return tryFixingCollapsedRange(range, schema);
    }
    return tryFixingNonCollapsedRage(range, schema);
}
// Tries to fix collapsed ranges.
//
// * Fixes situation when a range is in a place where $text is not allowed
//
// @param {module:engine/model/range~Range} range Collapsed range to fix.
// @param {module:engine/model/schema~Schema} schema
// @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.
function tryFixingCollapsedRange(range, schema) {
    const originalPosition = range.start;
    const nearestSelectionRange = schema.getNearestSelectionRange(originalPosition);
    // This might be null, i.e. when the editor data is empty or the selection is inside a limit element
    // that doesn't allow text inside.
    // In the first case, there is no need to fix the selection range.
    // In the second, let's go up to the outer selectable element
    if (!nearestSelectionRange) {
        const ancestorObject = originalPosition.getAncestors().reverse().find((item) => schema.isObject(item));
        if (ancestorObject) {
            return range_Range._createOn(ancestorObject);
        }
        return null;
    }
    if (!nearestSelectionRange.isCollapsed) {
        return nearestSelectionRange;
    }
    const fixedPosition = nearestSelectionRange.start;
    // Fixed position is the same as original - no need to return corrected range.
    if (originalPosition.isEqual(fixedPosition)) {
        return null;
    }
    return new range_Range(fixedPosition);
}
// Tries to fix an expanded range.
//
// @param {module:engine/model/range~Range} range Expanded range to fix.
// @param {module:engine/model/schema~Schema} schema
// @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.
function tryFixingNonCollapsedRage(range, schema) {
    const { start, end } = range;
    const isTextAllowedOnStart = schema.checkChild(start, '$text');
    const isTextAllowedOnEnd = schema.checkChild(end, '$text');
    const startLimitElement = schema.getLimitElement(start);
    const endLimitElement = schema.getLimitElement(end);
    // Ranges which both end are inside the same limit element (or root) might needs only minor fix.
    if (startLimitElement === endLimitElement) {
        // Range is valid when both position allows to place a text:
        // - <block>f[oobarba]z</block>
        // This would be "fixed" by a next check but as it will be the same it's better to return null so the selection stays the same.
        if (isTextAllowedOnStart && isTextAllowedOnEnd) {
            return null;
        }
        // Range that is on non-limit element (or is partially) must be fixed so it is placed inside the block around $text:
        // - [<block>foo</block>]    ->    <block>[foo]</block>
        // - [<block>foo]</block>    ->    <block>[foo]</block>
        // - <block>f[oo</block>]    ->    <block>f[oo]</block>
        // - [<block>foo</block><selectable></selectable>]    ->    <block>[foo</block><selectable></selectable>]
        if (checkSelectionOnNonLimitElements(start, end, schema)) {
            const isStartBeforeSelectable = start.nodeAfter && schema.isSelectable(start.nodeAfter);
            const fixedStart = isStartBeforeSelectable ? null : schema.getNearestSelectionRange(start, 'forward');
            const isEndAfterSelectable = end.nodeBefore && schema.isSelectable(end.nodeBefore);
            const fixedEnd = isEndAfterSelectable ? null : schema.getNearestSelectionRange(end, 'backward');
            // The schema.getNearestSelectionRange might return null - if that happens use original position.
            const rangeStart = fixedStart ? fixedStart.start : start;
            const rangeEnd = fixedEnd ? fixedEnd.end : end;
            return new range_Range(rangeStart, rangeEnd);
        }
    }
    const isStartInLimit = startLimitElement && !startLimitElement.is('rootElement');
    const isEndInLimit = endLimitElement && !endLimitElement.is('rootElement');
    // At this point we eliminated valid positions on text nodes so if one of range positions is placed inside a limit element
    // then the range crossed limit element boundaries and needs to be fixed.
    if (isStartInLimit || isEndInLimit) {
        const bothInSameParent = (start.nodeAfter && end.nodeBefore) && start.nodeAfter.parent === end.nodeBefore.parent;
        const expandStart = isStartInLimit && (!bothInSameParent || !isSelectable(start.nodeAfter, schema));
        const expandEnd = isEndInLimit && (!bothInSameParent || !isSelectable(end.nodeBefore, schema));
        // Although we've already found limit element on start/end positions we must find the outer-most limit element.
        // as limit elements might be nested directly inside (ie table > tableRow > tableCell).
        let fixedStart = start;
        let fixedEnd = end;
        if (expandStart) {
            fixedStart = position_Position._createBefore(findOutermostLimitAncestor(startLimitElement, schema));
        }
        if (expandEnd) {
            fixedEnd = position_Position._createAfter(findOutermostLimitAncestor(endLimitElement, schema));
        }
        return new range_Range(fixedStart, fixedEnd);
    }
    // Range was not fixed at this point so it is valid - ie it was placed around limit element already.
    return null;
}
// Finds the outer-most ancestor.
//
// @param {module:engine/model/node~Node} startingNode
// @param {module:engine/model/schema~Schema} schema
// @returns {module:engine/model/node~Node}
function findOutermostLimitAncestor(startingNode, schema) {
    let isLimitNode = startingNode;
    let parent = isLimitNode;
    // Find outer most isLimit block as such blocks might be nested (ie. in tables).
    while (schema.isLimit(parent) && parent.parent) {
        isLimitNode = parent;
        parent = parent.parent;
    }
    return isLimitNode;
}
// Checks whether any of range boundaries is placed around non-limit elements.
//
// @param {module:engine/model/position~Position} start
// @param {module:engine/model/position~Position} end
// @param {module:engine/model/schema~Schema} schema
// @returns {Boolean}
function checkSelectionOnNonLimitElements(start, end, schema) {
    const startIsOnBlock = (start.nodeAfter && !schema.isLimit(start.nodeAfter)) || schema.checkChild(start, '$text');
    const endIsOnBlock = (end.nodeBefore && !schema.isLimit(end.nodeBefore)) || schema.checkChild(end, '$text');
    // We should fix such selection when one of those nodes needs fixing.
    return startIsOnBlock || endIsOnBlock;
}
/**
 * Returns a minimal non-intersecting array of ranges without duplicates.
 *
 * @param {Array.<module:engine/model/range~Range>} Ranges to merge.
 * @returns {Array.<module:engine/model/range~Range>} Array of unique and nonIntersecting ranges.
 */
function mergeIntersectingRanges(ranges) {
    const rangesToMerge = [...ranges];
    const rangeIndexesToRemove = new Set();
    let currentRangeIndex = 1;
    while (currentRangeIndex < rangesToMerge.length) {
        const currentRange = rangesToMerge[currentRangeIndex];
        const previousRanges = rangesToMerge.slice(0, currentRangeIndex);
        for (const [previousRangeIndex, previousRange] of previousRanges.entries()) {
            if (rangeIndexesToRemove.has(previousRangeIndex)) {
                continue;
            }
            if (currentRange.isEqual(previousRange)) {
                rangeIndexesToRemove.add(previousRangeIndex);
            }
            else if (currentRange.isIntersecting(previousRange)) {
                rangeIndexesToRemove.add(previousRangeIndex);
                rangeIndexesToRemove.add(currentRangeIndex);
                const mergedRange = currentRange.getJoined(previousRange);
                rangesToMerge.push(mergedRange);
            }
        }
        currentRangeIndex++;
    }
    const nonIntersectingRanges = rangesToMerge.filter((_, index) => !rangeIndexesToRemove.has(index));
    return nonIntersectingRanges;
}
// Checks if node exists and if it's a selectable.
//
// @param {module:engine/model/node~Node} node
// @param {module:engine/model/schema~Schema} schema
// @returns {Boolean}
function isSelectable(node, schema) {
    return node && schema.isSelectable(node);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/liveposition.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable new-cap */
/**
 * @module engine/model/liveposition
 */



/**
 * `LivePosition` is a type of {@link module:engine/model/position~Position Position}
 * that updates itself as {@link module:engine/model/document~Document document}
 * is changed through operations. It may be used as a bookmark.
 *
 * **Note:** Contrary to {@link module:engine/model/position~Position}, `LivePosition` works only in roots that are
 * {@link module:engine/model/rootelement~RootElement}.
 * If {@link module:engine/model/documentfragment~DocumentFragment} is passed, error will be thrown.
 *
 * **Note:** Be very careful when dealing with `LivePosition`. Each `LivePosition` instance bind events that might
 * have to be unbound.
 * Use {@link module:engine/model/liveposition~LivePosition#detach} whenever you don't need `LivePosition` anymore.
 *
 * @extends module:engine/model/position~Position
 */
class LivePosition extends EmitterMixin(position_Position) {
    /**
     * Creates a live position.
     *
     * @see module:engine/model/position~Position
     * @param {module:engine/model/rootelement~RootElement} root
     * @param {Array.<Number>} path
     * @param {module:engine/model/position~PositionStickiness} [stickiness]
     */
    constructor(root, path, stickiness = 'toNone') {
        super(root, path, stickiness);
        if (!this.root.is('rootElement')) {
            /**
             * LivePosition's root has to be an instance of RootElement.
             *
             * @error model-liveposition-root-not-rootelement
             */
            throw new CKEditorError('model-liveposition-root-not-rootelement', root);
        }
        liveposition_bindWithDocument.call(this);
    }
    /**
     * Unbinds all events previously bound by `LivePosition`. Use it whenever you don't need `LivePosition` instance
     * anymore (i.e. when leaving scope in which it was declared or before re-assigning variable that was
     * referring to it).
     */
    detach() {
        this.stopListening();
    }
    /**
     * Creates a {@link module:engine/model/position~Position position instance}, which is equal to this live position.
     *
     * @returns {module:engine/model/position~Position}
     */
    toPosition() {
        return new position_Position(this.root, this.path.slice(), this.stickiness);
    }
    /**
     * Creates a `LivePosition` instance that is equal to position.
     *
     * @param {module:engine/model/position~Position} position
     * @param {module:engine/model/position~PositionStickiness} [stickiness]
     * @returns {module:engine/model/liveposition~LivePosition}
     */
    static fromPosition(position, stickiness) {
        return new this(position.root, position.path.slice(), stickiness ? stickiness : position.stickiness);
    }
}
/**
 * Checks whether this object is of the given.
 *
 *		livePosition.is( 'position' ); // -> true
 *		livePosition.is( 'model:position' ); // -> true
 *		livePosition.is( 'liveposition' ); // -> true
 *		livePosition.is( 'model:livePosition' ); // -> true
 *
 *		livePosition.is( 'view:position' ); // -> false
 *		livePosition.is( 'documentSelection' ); // -> false
 *
 * {@link module:engine/model/node~Node#is Check the entire list of model objects} which implement the `is()` method.
 *
 * @param {String} type
 * @returns {Boolean}
 */
LivePosition.prototype.is = function (type) {
    return type === 'livePosition' || type === 'model:livePosition' ||
        // From super.is(). This is highly utilised method and cannot call super. See ckeditor/ckeditor5#6529.
        type == 'position' || type === 'model:position';
};
// Binds this `LivePosition` to the {@link module:engine/model/document~Document document} that owns
// this position's {@link module:engine/model/position~Position#root root}.
//
// @private
function liveposition_bindWithDocument() {
    this.listenTo(this.root.document.model, 'applyOperation', (event, args) => {
        const operation = args[0];
        if (!operation.isDocumentOperation) {
            return;
        }
        liveposition_transform.call(this, operation);
    }, { priority: 'low' });
}
// Updates this position accordingly to the updates applied to the model. Bases on change events.
//
// @private
// @param {module:engine/model/operation/operation~Operation} operation Executed operation.
function liveposition_transform(operation) {
    const result = this.getTransformedByOperation(operation);
    if (!this.isEqual(result)) {
        const oldPosition = this.toPosition();
        this.path = result.path;
        this.root = result.root;
        this.fire('change', oldPosition);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/deletecontent.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/deletecontent
 */



/**
 * Deletes content of the selection and merge siblings. The resulting selection is always collapsed.
 *
 * **Note:** Use {@link module:engine/model/model~Model#deleteContent} instead of this function.
 * This function is only exposed to be reusable in algorithms
 * which change the {@link module:engine/model/model~Model#deleteContent}
 * method's behavior.
 *
 * @param {module:engine/model/model~Model} model The model in context of which the insertion
 * should be performed.
 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
 * Selection of which the content should be deleted.
 * @param {Object} [options]
 * @param {Boolean} [options.leaveUnmerged=false] Whether to merge elements after removing the content of the selection.
 *
 * For example `<heading>x[x</heading><paragraph>y]y</paragraph>` will become:
 *
 * * `<heading>x^y</heading>` with the option disabled (`leaveUnmerged == false`)
 * * `<heading>x^</heading><paragraph>y</paragraph>` with enabled (`leaveUnmerged == true`).
 *
 * Note: {@link module:engine/model/schema~Schema#isObject object} and {@link module:engine/model/schema~Schema#isLimit limit}
 * elements will not be merged.
 *
 * @param {Boolean} [options.doNotResetEntireContent=false] Whether to skip replacing the entire content with a
 * paragraph when the entire content was selected.
 *
 * For example `<heading>[x</heading><paragraph>y]</paragraph>` will become:
 *
 * * `<paragraph>^</paragraph>` with the option disabled (`doNotResetEntireContent == false`)
 * * `<heading>^</heading>` with enabled (`doNotResetEntireContent == true`).
 *
 * @param {Boolean} [options.doNotAutoparagraph=false] Whether to create a paragraph if after content deletion selection is moved
 * to a place where text cannot be inserted.
 *
 * For example `<paragraph>x</paragraph>[<imageBlock src="foo.jpg"></imageBlock>]` will become:
 *
 * * `<paragraph>x</paragraph><paragraph>[]</paragraph>` with the option disabled (`doNotAutoparagraph == false`)
 * * `<paragraph>x</paragraph>[]` with the option enabled (`doNotAutoparagraph == true`).
 *
 * If you use this option you need to make sure to handle invalid selections yourself or leave
 * them to the selection post-fixer (may not always work).
 *
 * **Note:** If there is no valid position for the selection, the paragraph will always be created:
 *
 * `[<imageBlock src="foo.jpg"></imageBlock>]` -> `<paragraph>[]</paragraph>`.
 */
function deleteContent(model, selection, options = {}) {
    if (selection.isCollapsed) {
        return;
    }
    const selRange = selection.getFirstRange();
    // If the selection is already removed, don't do anything.
    if (selRange.root.rootName == '$graveyard') {
        return;
    }
    const schema = model.schema;
    model.change(writer => {
        // 1. Replace the entire content with paragraph.
        // See: https://github.com/ckeditor/ckeditor5-engine/issues/1012#issuecomment-315017594.
        if (!options.doNotResetEntireContent && shouldEntireContentBeReplacedWithParagraph(schema, selection)) {
            replaceEntireContentWithParagraph(writer, selection);
            return;
        }
        // Collect attributes to copy in case of autoparagraphing.
        const attributesForAutoparagraph = {};
        if (!options.doNotAutoparagraph) {
            const selectedElement = selection.getSelectedElement();
            if (selectedElement) {
                Object.assign(attributesForAutoparagraph, schema.getAttributesWithProperty(selectedElement, 'copyOnReplace', true));
            }
        }
        // Get the live positions for the range adjusted to span only blocks selected from the user perspective.
        const [startPosition, endPosition] = getLivePositionsForSelectedBlocks(selRange);
        // 2. Remove the content if there is any.
        if (!startPosition.isTouching(endPosition)) {
            writer.remove(writer.createRange(startPosition, endPosition));
        }
        // 3. Merge elements in the right branch to the elements in the left branch.
        // The only reasonable (in terms of data and selection correctness) case in which we need to do that is:
        //
        // <heading type=1>Fo[</heading><paragraph>]ar</paragraph> => <heading type=1>Fo^ar</heading>
        //
        // However, the algorithm supports also merging deeper structures (up to the depth of the shallower branch),
        // as it's hard to imagine what should actually be the default behavior. Usually, specific features will
        // want to override that behavior anyway.
        if (!options.leaveUnmerged) {
            mergeBranches(writer, startPosition, endPosition);
            // TMP this will be replaced with a postfixer.
            // We need to check and strip disallowed attributes in all nested nodes because after merge
            // some attributes could end up in a path where are disallowed.
            //
            // e.g. bold is disallowed for <H1>
            // <h1>Fo{o</h1><p>b}a<b>r</b><p> -> <h1>Fo{}a<b>r</b><h1> -> <h1>Fo{}ar<h1>.
            schema.removeDisallowedAttributes(startPosition.parent.getChildren(), writer);
        }
        collapseSelectionAt(writer, selection, startPosition);
        // 4. Add a paragraph to set selection in it.
        // Check if a text is allowed in the new container. If not, try to create a new paragraph (if it's allowed here).
        // If autoparagraphing is off, we assume that you know what you do so we leave the selection wherever it was.
        if (!options.doNotAutoparagraph && shouldAutoparagraph(schema, startPosition)) {
            insertParagraph(writer, startPosition, selection, attributesForAutoparagraph);
        }
        startPosition.detach();
        endPosition.detach();
    });
}
// Returns the live positions for the range adjusted to span only blocks selected from the user perspective. Example:
//
//     <heading1>[foo</heading1>
//     <paragraph>bar</paragraph>
//     <heading1>]abc</heading1>  <-- this block is not considered as selected
//
// This is the same behavior as in Selection#getSelectedBlocks() "special case".
function getLivePositionsForSelectedBlocks(range) {
    const model = range.root.document.model;
    const startPosition = range.start;
    let endPosition = range.end;
    // If the end of selection is at the start position of last block in the selection, then
    // shrink it to not include that trailing block. Note that this should happen only for not empty selection.
    if (model.hasContent(range, { ignoreMarkers: true })) {
        const endBlock = deletecontent_getParentBlock(endPosition);
        if (endBlock && endPosition.isTouching(model.createPositionAt(endBlock, 0))) {
            // Create forward selection as a probe to find a valid position after excluding last block from the range.
            const selection = model.createSelection(range);
            // Modify the forward selection in backward direction to shrink it and remove first position of following block from it.
            // This is how modifySelection works and here we are making use of it.
            model.modifySelection(selection, { direction: 'backward' });
            const newEndPosition = selection.getLastPosition();
            // For such a model and selection:
            //     <paragraph>A[</paragraph><imageBlock></imageBlock><paragraph>]B</paragraph>
            //
            // After modifySelection(), we would end up with this:
            //     <paragraph>A[</paragraph>]<imageBlock></imageBlock><paragraph>B</paragraph>
            //
            // So we need to check if there is no content in the skipped range (because we want to include the <imageBlock>).
            const skippedRange = model.createRange(newEndPosition, endPosition);
            if (!model.hasContent(skippedRange, { ignoreMarkers: true })) {
                endPosition = newEndPosition;
            }
        }
    }
    return [
        LivePosition.fromPosition(startPosition, 'toPrevious'),
        LivePosition.fromPosition(endPosition, 'toNext')
    ];
}
// Finds the lowest element in position's ancestors which is a block.
// Returns null if a limit element is encountered before reaching a block element.
function deletecontent_getParentBlock(position) {
    const element = position.parent;
    const schema = element.root.document.model.schema;
    const ancestors = element.getAncestors({ parentFirst: true, includeSelf: true });
    for (const element of ancestors) {
        if (schema.isLimit(element)) {
            return null;
        }
        if (schema.isBlock(element)) {
            return element;
        }
    }
}
// This function is a result of reaching the Ballmer's peak for just the right amount of time.
// Even I had troubles documenting it after a while and after reading it again I couldn't believe that it really works.
function mergeBranches(writer, startPosition, endPosition) {
    const model = writer.model;
    // Verify if there is a need and possibility to merge.
    if (!checkShouldMerge(writer.model.schema, startPosition, endPosition)) {
        return;
    }
    // If the start element on the common ancestor level is empty, and the end element on the same level is not empty
    // then merge those to the right element so that it's properties are preserved (name, attributes).
    // Because of OT merging is used instead of removing elements.
    //
    // Merge left:
    //     <heading1>foo[</heading1>    ->  <heading1>foo[]bar</heading1>
    //     <paragraph>]bar</paragraph>  ->               --^
    //
    // Merge right:
    //     <heading1>[</heading1>       ->
    //     <paragraph>]bar</paragraph>  ->  <paragraph>[]bar</paragraph>
    //
    // Merge left:
    //     <blockQuote>                     ->  <blockQuote>
    //         <heading1>foo[</heading1>    ->      <heading1>foo[]bar</heading1>
    //         <paragraph>]bar</paragraph>  ->                   --^
    //     </blockQuote>                    ->  </blockQuote>
    //
    // Merge right:
    //     <blockQuote>                     ->  <blockQuote>
    //         <heading1>[</heading1>       ->
    //         <paragraph>]bar</paragraph>  ->      <paragraph>[]bar</paragraph>
    //     </blockQuote>                    ->  </blockQuote>
    // Merging should not go deeper than common ancestor.
    const [startAncestor, endAncestor] = getAncestorsJustBelowCommonAncestor(startPosition, endPosition);
    // Branches can't be merged if one of the positions is directly inside a common ancestor.
    //
    // Example:
    //     <blockQuote>
    //         <paragraph>[foo</paragraph>]
    //         <table> ... </table>
    //     <blockQuote>
    //
    if (!startAncestor || !endAncestor) {
        return;
    }
    if (!model.hasContent(startAncestor, { ignoreMarkers: true }) && model.hasContent(endAncestor, { ignoreMarkers: true })) {
        mergeBranchesRight(writer, startPosition, endPosition, startAncestor.parent);
    }
    else {
        mergeBranchesLeft(writer, startPosition, endPosition, startAncestor.parent);
    }
}
// Merging blocks to the left (properties of the left block are preserved).
// Simple example:
//     <heading1>foo[</heading1>    ->  <heading1>foo[bar</heading1>]
//     <paragraph>]bar</paragraph>  ->              --^
//
// Nested example:
//     <blockQuote>                     ->  <blockQuote>
//         <heading1>foo[</heading1>    ->      <heading1>foo[bar</heading1>
//     </blockQuote>                    ->  </blockQuote>]    ^
//     <blockBlock>                     ->                    |
//         <paragraph>]bar</paragraph>  ->                 ---
//     </blockBlock>                    ->
//
function mergeBranchesLeft(writer, startPosition, endPosition, commonAncestor) {
    const startElement = startPosition.parent;
    const endElement = endPosition.parent;
    // Merging reached the common ancestor element, stop here.
    if (startElement == commonAncestor || endElement == commonAncestor) {
        return;
    }
    // Remember next positions to merge in next recursive step (also used as modification points pointers).
    startPosition = writer.createPositionAfter(startElement);
    endPosition = writer.createPositionBefore(endElement);
    // Move endElement just after startElement if they aren't siblings.
    if (!endPosition.isEqual(startPosition)) {
        //
        //     <blockQuote>                     ->  <blockQuote>
        //         <heading1>foo[</heading1>    ->      <heading1>foo</heading1>[<paragraph>bar</paragraph>
        //     </blockQuote>                    ->  </blockQuote>                ^
        //     <blockBlock>                     ->  <blockBlock>                 |
        //         <paragraph>]bar</paragraph>  ->      ]                     ---
        //     </blockBlock>                    ->  </blockBlock>
        //
        writer.insert(endElement, startPosition);
    }
    // Merge two siblings (nodes on sides of startPosition):
    //
    //     <blockQuote>                                             ->  <blockQuote>
    //         <heading1>foo</heading1>[<paragraph>bar</paragraph>  ->      <heading1>foo[bar</heading1>
    //     </blockQuote>                                            ->  </blockQuote>
    //     <blockBlock>                                             ->  <blockBlock>
    //         ]                                                    ->      ]
    //     </blockBlock>                                            ->  </blockBlock>
    //
    // Or in simple case (without moving elements in above if):
    //     <heading1>foo</heading1>[<paragraph>bar</paragraph>]  ->  <heading1>foo[bar</heading1>]
    //
    writer.merge(startPosition);
    // Remove empty end ancestors:
    //
    //     <blockQuote>                      ->  <blockQuote>
    //         <heading1>foo[bar</heading1>  ->      <heading1>foo[bar</heading1>
    //     </blockQuote>                     ->  </blockQuote>
    //     <blockBlock>                      ->
    //         ]                             ->  ]
    //     </blockBlock>                     ->
    //
    while (endPosition.parent.isEmpty) {
        const parentToRemove = endPosition.parent;
        endPosition = writer.createPositionBefore(parentToRemove);
        writer.remove(parentToRemove);
    }
    // Verify if there is a need and possibility to merge next level.
    if (!checkShouldMerge(writer.model.schema, startPosition, endPosition)) {
        return;
    }
    // Continue merging next level (blockQuote with blockBlock in the examples above if it would not be empty and got removed).
    mergeBranchesLeft(writer, startPosition, endPosition, commonAncestor);
}
// Merging blocks to the right (properties of the right block are preserved).
// Simple example:
//     <heading1>foo[</heading1>    ->            --v
//     <paragraph>]bar</paragraph>  ->  [<paragraph>foo]bar</paragraph>
//
// Nested example:
//     <blockQuote>                     ->
//         <heading1>foo[</heading1>    ->              ---
//     </blockQuote>                    ->                 |
//     <blockBlock>                     ->  [<blockBlock>  v
//         <paragraph>]bar</paragraph>  ->      <paragraph>foo]bar</paragraph>
//     </blockBlock>                    ->  </blockBlock>
//
function mergeBranchesRight(writer, startPosition, endPosition, commonAncestor) {
    const startElement = startPosition.parent;
    const endElement = endPosition.parent;
    // Merging reached the common ancestor element, stop here.
    if (startElement == commonAncestor || endElement == commonAncestor) {
        return;
    }
    // Remember next positions to merge in next recursive step (also used as modification points pointers).
    startPosition = writer.createPositionAfter(startElement);
    endPosition = writer.createPositionBefore(endElement);
    // Move startElement just before endElement if they aren't siblings.
    if (!endPosition.isEqual(startPosition)) {
        //
        //     <blockQuote>                     ->  <blockQuote>
        //         <heading1>foo[</heading1>    ->      [                   ---
        //     </blockQuote>                    ->  </blockQuote>              |
        //     <blockBlock>                     ->  <blockBlock>               v
        //         <paragraph>]bar</paragraph>  ->      <heading1>foo</heading1>]<paragraph>bar</paragraph>
        //     </blockBlock>                    ->  </blockBlock>
        //
        writer.insert(startElement, endPosition);
    }
    // Remove empty end ancestors:
    //
    //     <blockQuote>                                             ->
    //         [                                                    ->  [
    //     </blockQuote>                                            ->
    //     <blockBlock>                                             ->  <blockBlock>
    //         <heading1>foo</heading1>]<paragraph>bar</paragraph>  ->      <heading1>foo</heading1>]<paragraph>bar</paragraph>
    //     </blockBlock>                                            ->  </blockBlock>
    //
    while (startPosition.parent.isEmpty) {
        const parentToRemove = startPosition.parent;
        startPosition = writer.createPositionBefore(parentToRemove);
        writer.remove(parentToRemove);
    }
    // Update endPosition after inserting and removing elements.
    endPosition = writer.createPositionBefore(endElement);
    // Merge right two siblings (nodes on sides of endPosition):
    //                                                              ->
    //     [                                                        ->  [
    //                                                              ->
    //     <blockBlock>                                             ->  <blockBlock>
    //         <heading1>foo</heading1>]<paragraph>bar</paragraph>  ->      <paragraph>foo]bar</paragraph>
    //     </blockBlock>                                            ->  </blockBlock>
    //
    // Or in simple case (without moving elements in above if):
    //     [<heading1>foo</heading1>]<paragraph>bar</paragraph>  ->  [<heading1>foo]bar</heading1>
    //
    mergeRight(writer, endPosition);
    // Verify if there is a need and possibility to merge next level.
    if (!checkShouldMerge(writer.model.schema, startPosition, endPosition)) {
        return;
    }
    // Continue merging next level (blockQuote with blockBlock in the examples above if it would not be empty and got removed).
    mergeBranchesRight(writer, startPosition, endPosition, commonAncestor);
}
// There is no right merge operation so we need to simulate it.
function mergeRight(writer, position) {
    const startElement = position.nodeBefore;
    const endElement = position.nodeAfter;
    if (startElement.name != endElement.name) {
        writer.rename(startElement, endElement.name);
    }
    writer.clearAttributes(startElement);
    writer.setAttributes(Object.fromEntries(endElement.getAttributes()), startElement);
    writer.merge(position);
}
// Verifies if merging is needed and possible. It's not needed if both positions are in the same element
// and it's not possible if some element is a limit or the range crosses a limit element.
function checkShouldMerge(schema, startPosition, endPosition) {
    const startElement = startPosition.parent;
    const endElement = endPosition.parent;
    // If both positions ended up in the same parent, then there's nothing more to merge:
    // <$root><p>x[</p><p>]y</p></$root> => <$root><p>xy</p>[]</$root>
    if (startElement == endElement) {
        return false;
    }
    // If one of the positions is a limit element, then there's nothing to merge because we don't want to cross the limit boundaries.
    if (schema.isLimit(startElement) || schema.isLimit(endElement)) {
        return false;
    }
    // Check if operations we'll need to do won't need to cross object or limit boundaries.
    // E.g., we can't merge endElement into startElement in this case:
    // <limit><startElement>x[</startElement></limit><endElement>]</endElement>
    return isCrossingLimitElement(startPosition, endPosition, schema);
}
// Returns the elements that are the ancestors of the provided positions that are direct children of the common ancestor.
function getAncestorsJustBelowCommonAncestor(positionA, positionB) {
    const ancestorsA = positionA.getAncestors();
    const ancestorsB = positionB.getAncestors();
    let i = 0;
    while (ancestorsA[i] && ancestorsA[i] == ancestorsB[i]) {
        i++;
    }
    return [ancestorsA[i], ancestorsB[i]];
}
function shouldAutoparagraph(schema, position) {
    const isTextAllowed = schema.checkChild(position, '$text');
    const isParagraphAllowed = schema.checkChild(position, 'paragraph');
    return !isTextAllowed && isParagraphAllowed;
}
// Check if parents of two positions can be merged by checking if there are no limit/object
// boundaries between those two positions.
//
// E.g. in <bQ><p>x[]</p></bQ><widget><caption>{}</caption></widget>
// we'll check <p>, <bQ>, <widget> and <caption>.
// Usually, widget and caption are marked as objects/limits in the schema, so in this case merging will be blocked.
function isCrossingLimitElement(leftPos, rightPos, schema) {
    const rangeToCheck = new range_Range(leftPos, rightPos);
    for (const value of rangeToCheck.getWalker()) {
        if (schema.isLimit(value.item)) {
            return false;
        }
    }
    return true;
}
function insertParagraph(writer, position, selection, attributes = {}) {
    const paragraph = writer.createElement('paragraph');
    writer.model.schema.setAllowedAttributes(paragraph, attributes, writer);
    writer.insert(paragraph, position);
    collapseSelectionAt(writer, selection, writer.createPositionAt(paragraph, 0));
}
function replaceEntireContentWithParagraph(writer, selection) {
    const limitElement = writer.model.schema.getLimitElement(selection);
    writer.remove(writer.createRangeIn(limitElement));
    insertParagraph(writer, writer.createPositionAt(limitElement, 0), selection);
}
// We want to replace the entire content with a paragraph when:
// * the entire content is selected,
// * selection contains at least two elements,
// * whether the paragraph is allowed in schema in the common ancestor.
function shouldEntireContentBeReplacedWithParagraph(schema, selection) {
    const limitElement = schema.getLimitElement(selection);
    if (!selection.containsEntireContent(limitElement)) {
        return false;
    }
    const range = selection.getFirstRange();
    if (range.start.parent == range.end.parent) {
        return false;
    }
    return schema.checkChild(limitElement, 'paragraph');
}
// Helper function that sets the selection. Depending whether given `selection` is a document selection or not,
// uses a different method to set it.
function collapseSelectionAt(writer, selection, positionOrRange) {
    if (selection instanceof documentselection_DocumentSelection) {
        writer.setSelection(positionOrRange);
    }
    else {
        selection.setTo(positionOrRange);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/getselectedcontent.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/getselectedcontent
 */
/**
 * Gets a clone of the selected content.
 *
 * For example, for the following selection:
 *
 * ```html
 * <p>x</p><quote><p>y</p><h>fir[st</h></quote><p>se]cond</p><p>z</p>
 * ```
 *
 * It will return a document fragment with such a content:
 *
 * ```html
 * <quote><h>st</h></quote><p>se</p>
 * ```
 *
 * @param {module:engine/model/model~Model} model The model in context of which
 * the selection modification should be performed.
 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
 * The selection of which content will be returned.
 * @returns {module:engine/model/documentfragment~DocumentFragment}
 */
function getSelectedContent(model, selection) {
    return model.change(writer => {
        const frag = writer.createDocumentFragment();
        const range = selection.getFirstRange();
        if (!range || range.isCollapsed) {
            return frag;
        }
        const root = range.start.root;
        const commonPath = range.start.getCommonPath(range.end);
        const commonParent = root.getNodeByPath(commonPath);
        // ## 1st step
        //
        // First, we'll clone a fragment represented by a minimal flat range
        // containing the original range to be cloned.
        // E.g. let's consider such a range:
        //
        // <p>x</p><quote><p>y</p><h>fir[st</h></quote><p>se]cond</p><p>z</p>
        //
        // A minimal flat range containing this one is:
        //
        // <p>x</p>[<quote><p>y</p><h>first</h></quote><p>second</p>]<p>z</p>
        //
        // We can easily clone this structure, preserving e.g. the <quote> element.
        let flatSubtreeRange;
        if (range.start.parent == range.end.parent) {
            // The original range is flat, so take it.
            flatSubtreeRange = range;
        }
        else {
            flatSubtreeRange = writer.createRange(writer.createPositionAt(commonParent, range.start.path[commonPath.length]), writer.createPositionAt(commonParent, range.end.path[commonPath.length] + 1));
        }
        const howMany = flatSubtreeRange.end.offset - flatSubtreeRange.start.offset;
        // Clone the whole contents.
        for (const item of flatSubtreeRange.getItems({ shallow: true })) {
            if (item.is('$textProxy')) {
                writer.appendText(item.data, item.getAttributes(), frag);
            }
            else {
                writer.append(writer.cloneElement(item, true), frag);
            }
        }
        // ## 2nd step
        //
        // If the original range wasn't flat, then we need to remove the excess nodes from the both ends of the cloned fragment.
        //
        // For example, for the range shown in the 1st step comment, we need to remove these pieces:
        //
        // <quote>[<p>y</p>]<h>[fir]st</h></quote><p>se[cond]</p>
        //
        // So this will be the final copied content:
        //
        // <quote><h>st</h></quote><p>se</p>
        //
        // In order to do that, we remove content from these two ranges:
        //
        // [<quote><p>y</p><h>fir]st</h></quote><p>se[cond</p>]
        if (flatSubtreeRange != range) {
            // Find the position of the original range in the cloned fragment.
            const newRange = range._getTransformedByMove(flatSubtreeRange.start, writer.createPositionAt(frag, 0), howMany)[0];
            const leftExcessRange = writer.createRange(writer.createPositionAt(frag, 0), newRange.start);
            const rightExcessRange = writer.createRange(newRange.end, writer.createPositionAt(frag, 'end'));
            removeRangeContent(rightExcessRange, writer);
            removeRangeContent(leftExcessRange, writer);
        }
        return frag;
    });
}
// After https://github.com/ckeditor/ckeditor5-engine/issues/690 is fixed,
// this function will, most likely, be able to rewritten using getMinimalFlatRanges().
function removeRangeContent(range, writer) {
    const parentsToCheck = [];
    Array.from(range.getItems({ direction: 'backward' }))
        // We should better store ranges because text proxies will lose integrity
        // with the text nodes when we'll start removing content.
        .map(item => writer.createRangeOn(item))
        // Filter only these items which are fully contained in the passed range.
        //
        // E.g. for the following range: [<quote><p>y</p><h>fir]st</h>
        // the walker will return the entire <h> element, when only the "fir" item inside it is fully contained.
        .filter(itemRange => {
        // We should be able to use Range.containsRange, but https://github.com/ckeditor/ckeditor5-engine/issues/691.
        const contained = (itemRange.start.isAfter(range.start) || itemRange.start.isEqual(range.start)) &&
            (itemRange.end.isBefore(range.end) || itemRange.end.isEqual(range.end));
        return contained;
    })
        .forEach(itemRange => {
        parentsToCheck.push(itemRange.start.parent);
        writer.remove(itemRange);
    });
    // Remove ancestors of the removed items if they turned to be empty now
    // (their whole content was contained in the range).
    parentsToCheck.forEach(parentToCheck => {
        let parent = parentToCheck;
        while (parent.parent && parent.isEmpty) {
            const removeRange = writer.createRangeOn(parent);
            parent = parent.parent;
            writer.remove(removeRange);
        }
    });
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-engine/theme/placeholder.css
var placeholder = __webpack_require__(8894);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/theme/placeholder.css

            

var placeholder_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

placeholder_options.insert = "head";
placeholder_options.singleton = true;

var placeholder_update = injectStylesIntoStyleTag_default()(placeholder/* default */.Z, placeholder_options);



/* harmony default export */ const theme_placeholder = (placeholder/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/placeholder.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/placeholder
 */

// Each document stores information about its placeholder elements and check functions.
const documentPlaceholders = new WeakMap();
/**
 * A helper that enables a placeholder on the provided view element (also updates its visibility).
 * The placeholder is a CSS pseudo–element (with a text content) attached to the element.
 *
 * To change the placeholder text, simply call this method again with new options.
 *
 * To disable the placeholder, use {@link module:engine/view/placeholder~disablePlaceholder `disablePlaceholder()`} helper.
 *
 * @param {Object} [options] Configuration options of the placeholder.
 * @param {module:engine/view/view~View} options.view Editing view instance.
 * @param {module:engine/view/element~Element} options.element Element that will gain a placeholder.
 * See `options.isDirectHost` to learn more.
 * @param {String} options.text Placeholder text.
 * @param {Boolean} [options.isDirectHost=true] If set `false`, the placeholder will not be enabled directly
 * in the passed `element` but in one of its children (selected automatically, i.e. a first empty child element).
 * Useful when attaching placeholders to elements that can host other elements (not just text), for instance,
 * editable root elements.
 * @param {Boolean} [options.keepOnFocus=false] If set `true`, the placeholder stay visible when the host element is focused.
 */
function enablePlaceholder(options) {
    const { view, element, text, isDirectHost = true, keepOnFocus = false } = options;
    const doc = view.document;
    // Use a single a single post fixer per—document to update all placeholders.
    if (!documentPlaceholders.has(doc)) {
        documentPlaceholders.set(doc, new Map());
        // If a post-fixer callback makes a change, it should return `true` so other post–fixers
        // can re–evaluate the document again.
        doc.registerPostFixer(writer => updateDocumentPlaceholders(doc, writer));
        // Update placeholders on isComposing state change since rendering is disabled while in composition mode.
        doc.on('change:isComposing', () => {
            view.change(writer => updateDocumentPlaceholders(doc, writer));
        }, { priority: 'high' });
    }
    // Store information about the element placeholder under its document.
    documentPlaceholders.get(doc).set(element, {
        text,
        isDirectHost,
        keepOnFocus,
        hostElement: isDirectHost ? element : null
    });
    // Update the placeholders right away.
    view.change(writer => updateDocumentPlaceholders(doc, writer));
}
/**
 * Disables the placeholder functionality from a given element.
 *
 * See {@link module:engine/view/placeholder~enablePlaceholder `enablePlaceholder()`} to learn more.
 *
 * @param {module:engine/view/view~View} view
 * @param {module:engine/view/element~Element} element
 */
function disablePlaceholder(view, element) {
    const doc = element.document;
    view.change(writer => {
        if (!documentPlaceholders.has(doc)) {
            return;
        }
        const placeholders = documentPlaceholders.get(doc);
        const config = placeholders.get(element);
        writer.removeAttribute('data-placeholder', config.hostElement);
        hidePlaceholder(writer, config.hostElement);
        placeholders.delete(element);
    });
}
/**
 * Shows a placeholder in the provided element by changing related attributes and CSS classes.
 *
 * **Note**: This helper will not update the placeholder visibility nor manage the
 * it in any way in the future. What it does is a one–time state change of an element. Use
 * {@link module:engine/view/placeholder~enablePlaceholder `enablePlaceholder()`} and
 * {@link module:engine/view/placeholder~disablePlaceholder `disablePlaceholder()`} for full
 * placeholder functionality.
 *
 * **Note**: This helper will blindly show the placeholder directly in the root editable element if
 * one is passed, which could result in a visual clash if the editable element has some children
 * (for instance, an empty paragraph). Use {@link module:engine/view/placeholder~enablePlaceholder `enablePlaceholder()`}
 * in that case or make sure the correct element is passed to the helper.
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @param {module:engine/view/element~Element} element
 * @returns {Boolean} `true`, if any changes were made to the `element`.
 */
function showPlaceholder(writer, element) {
    if (!element.hasClass('ck-placeholder')) {
        writer.addClass('ck-placeholder', element);
        return true;
    }
    return false;
}
/**
 * Hides a placeholder in the element by changing related attributes and CSS classes.
 *
 * **Note**: This helper will not update the placeholder visibility nor manage the
 * it in any way in the future. What it does is a one–time state change of an element. Use
 * {@link module:engine/view/placeholder~enablePlaceholder `enablePlaceholder()`} and
 * {@link module:engine/view/placeholder~disablePlaceholder `disablePlaceholder()`} for full
 * placeholder functionality.
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @param {module:engine/view/element~Element} element
 * @returns {Boolean} `true`, if any changes were made to the `element`.
 */
function hidePlaceholder(writer, element) {
    if (element.hasClass('ck-placeholder')) {
        writer.removeClass('ck-placeholder', element);
        return true;
    }
    return false;
}
/**
 * Checks if a placeholder should be displayed in the element.
 *
 * **Note**: This helper will blindly check the possibility of showing a placeholder directly in the
 * root editable element if one is passed, which may not be the expected result. If an element can
 * host other elements (not just text), most likely one of its children should be checked instead
 * because it will be the final host for the placeholder. Use
 * {@link module:engine/view/placeholder~enablePlaceholder `enablePlaceholder()`} in that case or make
 * sure the correct element is passed to the helper.
 *
 * @param {module:engine/view/element~Element} element Element that holds the placeholder.
 * @param {Boolean} keepOnFocus Focusing the element will keep the placeholder visible.
 * @returns {Boolean}
 */
function needsPlaceholder(element, keepOnFocus) {
    if (!element.isAttached()) {
        return false;
    }
    // Anything but uiElement(s) counts as content.
    const hasContent = Array.from(element.getChildren())
        .some(element => !element.is('uiElement'));
    if (hasContent) {
        return false;
    }
    const doc = element.document;
    const viewSelection = doc.selection;
    const selectionAnchor = viewSelection.anchor;
    if (doc.isComposing && selectionAnchor && selectionAnchor.parent === element) {
        return false;
    }
    // Skip the focus check and make the placeholder visible already regardless of document focus state.
    if (keepOnFocus) {
        return true;
    }
    // If the document is blurred.
    if (!doc.isFocused) {
        return true;
    }
    // If document is focused and the element is empty but the selection is not anchored inside it.
    return !!selectionAnchor && selectionAnchor.parent !== element;
}
// Updates all placeholders associated with a document in a post–fixer callback.
//
// @private
// @param { module:engine/view/document~Document} doc
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @returns {Boolean} True if any changes were made to the view document.
function updateDocumentPlaceholders(doc, writer) {
    const placeholders = documentPlaceholders.get(doc);
    const directHostElements = [];
    let wasViewModified = false;
    // First set placeholders on the direct hosts.
    for (const [element, config] of placeholders) {
        if (config.isDirectHost) {
            directHostElements.push(element);
            if (updatePlaceholder(writer, element, config)) {
                wasViewModified = true;
            }
        }
    }
    // Then set placeholders on the indirect hosts but only on those that does not already have an direct host placeholder.
    for (const [element, config] of placeholders) {
        if (config.isDirectHost) {
            continue;
        }
        const hostElement = getChildPlaceholderHostSubstitute(element);
        // When not a direct host, it could happen that there is no child element
        // capable of displaying a placeholder.
        if (!hostElement) {
            continue;
        }
        // Don't override placeholder if the host element already has some direct placeholder.
        if (directHostElements.includes(hostElement)) {
            continue;
        }
        // Update the host element (used for setting and removing the placeholder).
        config.hostElement = hostElement;
        if (updatePlaceholder(writer, element, config)) {
            wasViewModified = true;
        }
    }
    return wasViewModified;
}
// Updates a single placeholder in a post–fixer callback.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {module:engine/view/element~Element} element
// @param {Object} config Configuration of the placeholder
// @param {String} config.text
// @param {Boolean} config.isDirectHost
// @returns {Boolean} True if any changes were made to the view document.
function updatePlaceholder(writer, element, config) {
    const { text, isDirectHost, hostElement } = config;
    let wasViewModified = false;
    // This may be necessary when updating the placeholder text to something else.
    if (hostElement.getAttribute('data-placeholder') !== text) {
        writer.setAttribute('data-placeholder', text, hostElement);
        wasViewModified = true;
    }
    // If the host element is not a direct host then placeholder is needed only when there is only one element.
    const isOnlyChild = isDirectHost || element.childCount == 1;
    if (isOnlyChild && needsPlaceholder(hostElement, config.keepOnFocus)) {
        if (showPlaceholder(writer, hostElement)) {
            wasViewModified = true;
        }
    }
    else if (hidePlaceholder(writer, hostElement)) {
        wasViewModified = true;
    }
    return wasViewModified;
}
// Gets a child element capable of displaying a placeholder if a parent element can host more
// than just text (for instance, when it is a root editable element). The child element
// can then be used in other placeholder helpers as a substitute of its parent.
//
// @private
// @param {module:engine/view/element~Element} parent
// @returns {module:engine/view/element~Element|null}
function getChildPlaceholderHostSubstitute(parent) {
    if (parent.childCount) {
        const firstChild = parent.getChild(0);
        if (firstChild.is('element') && !firstChild.is('uiElement') && !firstChild.is('attributeElement')) {
            return firstChild;
        }
    }
    return null;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/operation/transform.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */












const transformations = new Map();
/**
 * @module engine/model/operation/transform
 */
/**
 * Sets a transformation function to be be used to transform instances of class `OperationA` by instances of class `OperationB`.
 *
 * The `transformationFunction` is passed three parameters:
 *
 * * `a` - operation to be transformed, an instance of `OperationA`,
 * * `b` - operation to be transformed by, an instance of `OperationB`,
 * * {@link module:engine/model/operation/transform~TransformationContext `context`} - object with additional information about
 * transformation context.
 *
 * The `transformationFunction` should return transformation result, which is an array with one or multiple
 * {@link module:engine/model/operation/operation~Operation operation} instances.
 *
 * @protected
 * @param {Function} OperationA
 * @param {Function} OperationB
 * @param {Function} transformationFunction Function to use for transforming.
 */
function setTransformation(OperationA, OperationB, transformationFunction) {
    let aGroup = transformations.get(OperationA);
    if (!aGroup) {
        aGroup = new Map();
        transformations.set(OperationA, aGroup);
    }
    aGroup.set(OperationB, transformationFunction);
}
/**
 * Returns a previously set transformation function for transforming an instance of `OperationA` by an instance of `OperationB`.
 *
 * If no transformation was set for given pair of operations, {@link module:engine/model/operation/transform~noUpdateTransformation}
 * is returned. This means that if no transformation was set, the `OperationA` instance will not change when transformed
 * by the `OperationB` instance.
 *
 * @private
 * @param {Function} OperationA
 * @param {Function} OperationB
 * @returns {Function} Function set to transform an instance of `OperationA` by an instance of `OperationB`.
 */
function getTransformation(OperationA, OperationB) {
    const aGroup = transformations.get(OperationA);
    if (aGroup && aGroup.has(OperationB)) {
        return aGroup.get(OperationB);
    }
    return noUpdateTransformation;
}
/**
 * A transformation function that only clones operation to transform, without changing it.
 *
 * @private
 * @param {module:engine/model/operation/operation~Operation} a Operation to transform.
 * @returns {Array.<module:engine/model/operation/operation~Operation>}
 */
function noUpdateTransformation(a) {
    return [a];
}
/**
 * Transforms operation `a` by operation `b`.
 *
 * @param {module:engine/model/operation/operation~Operation} a Operation to be transformed.
 * @param {module:engine/model/operation/operation~Operation} b Operation to transform by.
 * @param {module:engine/model/operation/transform~TransformationContext} [context] Transformation context for this transformation.
 * @returns {Array.<module:engine/model/operation/operation~Operation>} Transformation result.
 */
function transform_transform(a, b, context = {}) {
    const transformationFunction = getTransformation(a.constructor, b.constructor);
    /* eslint-disable no-useless-catch */
    try {
        a = a.clone();
        return transformationFunction(a, b, context);
    }
    catch (e) {
        // @if CK_DEBUG // console.warn( 'Error during operation transformation!', e.message );
        // @if CK_DEBUG // console.warn( 'Transformed operation', a );
        // @if CK_DEBUG // console.warn( 'Operation transformed by', b );
        // @if CK_DEBUG // console.warn( 'context.aIsStrong', context.aIsStrong );
        // @if CK_DEBUG // console.warn( 'context.aWasUndone', context.aWasUndone );
        // @if CK_DEBUG // console.warn( 'context.bWasUndone', context.bWasUndone );
        // @if CK_DEBUG // console.warn( 'context.abRelation', context.abRelation );
        // @if CK_DEBUG // console.warn( 'context.baRelation', context.baRelation );
        throw e;
    }
    /* eslint-enable no-useless-catch */
}
/**
 * Performs a transformation of two sets of operations - `operationsA` and `operationsB`. The transformation is two-way -
 * both transformed `operationsA` and transformed `operationsB` are returned.
 *
 * Note, that the first operation in each set should base on the same document state (
 * {@link module:engine/model/document~Document#version document version}).
 *
 * It is assumed that `operationsA` are "more important" during conflict resolution between two operations.
 *
 * New copies of both passed arrays and operations inside them are returned. Passed arguments are not altered.
 *
 * Base versions of the transformed operations sets are updated accordingly. For example, assume that base versions are `4`
 * and there are `3` operations in `operationsA` and `5` operations in `operationsB`. Then:
 *
 * * transformed `operationsA` will start from base version `9` (`4` base version + `5` operations B),
 * * transformed `operationsB` will start from base version `7` (`4` base version + `3` operations A).
 *
 * If no operation was broken into two during transformation, then both sets will end up with an operation that bases on version `11`:
 *
 * * transformed `operationsA` start from `9` and there are `3` of them, so the last will have `baseVersion` equal to `11`,
 * * transformed `operationsB` start from `7` and there are `5` of them, so the last will have `baseVersion` equal to `11`.
 *
 * @param {Array.<module:engine/model/operation/operation~Operation>} operationsA
 * @param {Array.<module:engine/model/operation/operation~Operation>} operationsB
 * @param {Object} options Additional transformation options.
 * @param {module:engine/model/document~Document} options.document Document which the operations change.
 * @param {Boolean} [options.useRelations=false] Whether during transformation relations should be used (used during undo for
 * better conflict resolution).
 * @param {Boolean} [options.padWithNoOps=false] Whether additional {@link module:engine/model/operation/nooperation~NoOperation}s
 * should be added to the transformation results to force the same last base version for both transformed sets (in case
 * if some operations got broken into multiple operations during transformation).
 * @param {Boolean} [options.forceWeakRemove] If set to `false`, remove operation will be always stronger than move operation,
 * so the removed nodes won't end up back in the document root. When set to `true`, context data will be used.
 * @returns {Object} Transformation result.
 * @returns {Array.<module:engine/model/operation/operation~Operation>} return.operationsA Transformed `operationsA`.
 * @returns {Array.<module:engine/model/operation/operation~Operation>} return.operationsB Transformed `operationsB`.
 * @returns {Map} return.originalOperations A map that links transformed operations to original operations. The keys are the transformed
 * operations and the values are the original operations from the input (`operationsA` and `operationsB`).
 */
function transformSets(operationsA, operationsB, options) {
    // Create new arrays so the originally passed arguments are not changed.
    // No need to clone operations, they are cloned as they are transformed.
    operationsA = operationsA.slice();
    operationsB = operationsB.slice();
    const contextFactory = new ContextFactory(options.document, options.useRelations, options.forceWeakRemove);
    contextFactory.setOriginalOperations(operationsA);
    contextFactory.setOriginalOperations(operationsB);
    const originalOperations = contextFactory.originalOperations;
    // If one of sets is empty there is simply nothing to transform, so return sets as they are.
    if (operationsA.length == 0 || operationsB.length == 0) {
        return { operationsA, operationsB, originalOperations };
    }
    //
    // Following is a description of transformation process:
    //
    // There are `operationsA` and `operationsB` to be transformed, both by both.
    //
    // So, suppose we have sets of two operations each: `operationsA` = `[ a1, a2 ]`, `operationsB` = `[ b1, b2 ]`.
    //
    // Remember, that we can only transform operations that base on the same context. We assert that `a1` and `b1` base on
    // the same context and we transform them. Then, we get `a1'` and `b1'`. `a2` bases on a context with `a1` -- `a2`
    // is an operation that followed `a1`. Similarly, `b2` bases on a context with `b1`.
    //
    // However, since `a1'` is a result of transformation by `b1`, `a1'` now also has a context with `b1`. This means that
    // we can safely transform `a1'` by `b2`. As we finish transforming `a1`, we also transformed all `operationsB`.
    // All `operationsB` also have context including `a1`. Now, we can properly transform `a2` by those operations.
    //
    // The transformation process can be visualized on a transformation diagram ("diamond diagram"):
    //
    //          [the initial state]
    //         [common for a1 and b1]
    //
    //                   *
    //                  / \
    //                 /   \
    //               b1     a1
    //               /       \
    //              /         \
    //             *           *
    //            / \         / \
    //           /   \       /   \
    //         b2    a1'   b1'    a2
    //         /       \   /       \
    //        /         \ /         \
    //       *           *           *
    //        \         / \         /
    //         \       /   \       /
    //        a1''   b2'   a2'   b1''
    //           \   /       \   /
    //            \ /         \ /
    //             *           *
    //              \         /
    //               \       /
    //              a2''   b2''
    //                 \   /
    //                  \ /
    //                   *
    //
    //           [the final state]
    //
    // The final state can be reached from the initial state by applying `a1`, `a2`, `b1''` and `b2''`, as well as by
    // applying `b1`, `b2`, `a1''`, `a2''`. Note how the operations get to a proper common state before each pair is
    // transformed.
    //
    // Another thing to consider is that an operation during transformation can be broken into multiple operations.
    // Suppose that `a1` * `b1` = `[ a11', a12' ]` (instead of `a1'` that we considered previously).
    //
    // In that case, we leave `a12'` for later and we continue transforming `a11'` until it is transformed by all `operationsB`
    // (in our case it is just `b2`). At this point, `b1` is transformed by "whole" `a1`, while `b2` is only transformed
    // by `a11'`. Similarly, `a12'` is only transformed by `b1`. This leads to a conclusion that we need to start transforming `a12'`
    // from the moment just after it was broken. So, `a12'` is transformed by `b2`. Now, "the whole" `a1` is transformed
    // by `operationsB`, while all `operationsB` are transformed by "the whole" `a1`. This means that we can continue with
    // following `operationsA` (in our case it is just `a2`).
    //
    // Of course, also `operationsB` can be broken. However, since we focus on transforming operation `a` to the end,
    // the only thing to do is to store both pieces of operation `b`, so that the next transformed operation `a` will
    // be transformed by both of them.
    //
    //                       *
    //                      / \
    //                     /   \
    //                    /     \
    //                  b1       a1
    //                  /         \
    //                 /           \
    //                /             \
    //               *               *
    //              / \             / \
    //             /  a11'         /   \
    //            /     \         /     \
    //          b2       *      b1'      a2
    //          /       / \     /         \
    //         /       /  a12' /           \
    //        /       /     \ /             \
    //       *       b2'     *               *
    //        \     /       / \             /
    //       a11'' /     b21'' \           /
    //          \ /       /     \         /
    //           *       *      a2'     b1''
    //            \     / \       \     /
    //          a12'' b22''\       \   /
    //              \ /     \       \ /
    //               *      a2''     *
    //                \       \     /
    //                 \       \  b21'''
    //                  \       \ /
    //                a2'''      *
    //                    \     /
    //                     \  b22'''
    //                      \ /
    //                       *
    //
    // Note, how `a1` is broken and transformed into `a11'` and `a12'`, while `b2'` got broken and transformed into `b21''` and `b22''`.
    //
    // Having all that on mind, here is an outline for the transformation process algorithm:
    //
    // 1. We have `operationsA` and `operationsB` array, which we dynamically update as the transformation process goes.
    //
    // 2. We take next (or first) operation from `operationsA` and check from which operation `b` we need to start transforming it.
    // All original `operationsA` are set to be transformed starting from the first operation `b`.
    //
    // 3. We take operations from `operationsB`, one by one, starting from the correct one, and transform operation `a`
    // by operation `b` (and vice versa). We update `operationsA` and `operationsB` by replacing the original operations
    // with the transformation results.
    //
    // 4. If operation is broken into multiple operations, we save all the new operations in the place of the
    // original operation.
    //
    // 5. Additionally, if operation `a` was broken, for the "new" operation, we remember from which operation `b` it should
    // be transformed by.
    //
    // 6. We continue transforming "current" operation `a` until it is transformed by all `operationsB`. Then, go to 2.
    // unless the last operation `a` was transformed.
    //
    // The actual implementation of the above algorithm is slightly different, as only one loop (while) is used.
    // The difference is that we have "current" `a` operation to transform and we store the index of the next `b` operation
    // to transform by. Each loop operates on two indexes then: index pointing to currently processed `a` operation and
    // index pointing to next `b` operation. Each loop is just one `a * b` + `b * a` transformation. After each loop
    // operation `b` index is updated. If all `b` operations were visited for the current `a` operation, we change
    // current `a` operation index to the next one.
    //
    // For each operation `a`, keeps information what is the index in `operationsB` from which the transformation should start.
    const nextTransformIndex = new WeakMap();
    // For all the original `operationsA`, set that they should be transformed starting from the first of `operationsB`.
    for (const op of operationsA) {
        nextTransformIndex.set(op, 0);
    }
    // Additional data that is used for some postprocessing after the main transformation process is done.
    const data = {
        nextBaseVersionA: operationsA[operationsA.length - 1].baseVersion + 1,
        nextBaseVersionB: operationsB[operationsB.length - 1].baseVersion + 1,
        originalOperationsACount: operationsA.length,
        originalOperationsBCount: operationsB.length
    };
    // Index of currently transformed operation `a`.
    let i = 0;
    // While not all `operationsA` are transformed...
    while (i < operationsA.length) {
        // Get "current" operation `a`.
        const opA = operationsA[i];
        // For the "current" operation `a`, get the index of the next operation `b` to transform by.
        const indexB = nextTransformIndex.get(opA);
        // If operation `a` was already transformed by every operation `b`, change "current" operation `a` to the next one.
        if (indexB == operationsB.length) {
            i++;
            continue;
        }
        const opB = operationsB[indexB];
        // Transform `a` by `b` and `b` by `a`.
        const newOpsA = transform_transform(opA, opB, contextFactory.getContext(opA, opB, true));
        const newOpsB = transform_transform(opB, opA, contextFactory.getContext(opB, opA, false));
        // As a result we get one or more `newOpsA` and one or more `newOpsB` operations.
        // Update contextual information about operations.
        contextFactory.updateRelation(opA, opB);
        contextFactory.setOriginalOperations(newOpsA, opA);
        contextFactory.setOriginalOperations(newOpsB, opB);
        // For new `a` operations, update their index of the next operation `b` to transform them by.
        //
        // This is needed even if there was only one result (`a` was not broken) because that information is used
        // at the beginning of this loop every time.
        for (const newOpA of newOpsA) {
            // Acknowledge, that operation `b` also might be broken into multiple operations.
            //
            // This is why we raise `indexB` not just by 1. If `newOpsB` are multiple operations, they will be
            // spliced in the place of `opB`. So we need to change `transformBy` accordingly, so that an operation won't
            // be transformed by the same operation (part of it) again.
            nextTransformIndex.set(newOpA, indexB + newOpsB.length);
        }
        // Update `operationsA` and `operationsB` with the transformed versions.
        operationsA.splice(i, 1, ...newOpsA);
        operationsB.splice(indexB, 1, ...newOpsB);
    }
    if (options.padWithNoOps) {
        // If no-operations padding is enabled, count how many extra `a` and `b` operations were generated.
        const brokenOperationsACount = operationsA.length - data.originalOperationsACount;
        const brokenOperationsBCount = operationsB.length - data.originalOperationsBCount;
        // Then, if that number is not the same, pad `operationsA` or `operationsB` with correct number of no-ops so
        // that the base versions are equalled.
        //
        // Note that only one array will be updated, as only one of those subtractions can be greater than zero.
        padWithNoOps(operationsA, brokenOperationsBCount - brokenOperationsACount);
        padWithNoOps(operationsB, brokenOperationsACount - brokenOperationsBCount);
    }
    // Finally, update base versions of transformed operations.
    updateBaseVersions(operationsA, data.nextBaseVersionB);
    updateBaseVersions(operationsB, data.nextBaseVersionA);
    return { operationsA, operationsB, originalOperations };
}
// Gathers additional data about operations processed during transformation. Can be used to obtain contextual information
// about two operations that are about to be transformed. This contextual information can be used for better conflict resolution.
class ContextFactory {
    // Creates `ContextFactory` instance.
    //
    // @param {module:engine/model/document~Document} document Document which the operations change.
    // @param {Boolean} useRelations Whether during transformation relations should be used (used during undo for
    // better conflict resolution).
    // @param {Boolean} [forceWeakRemove=false] If set to `false`, remove operation will be always stronger than move operation,
    // so the removed nodes won't end up back in the document root. When set to `true`, context data will be used.
    constructor(document, useRelations, forceWeakRemove = false) {
        // For each operation that is created during transformation process, we keep a reference to the original operation
        // which it comes from. The original operation works as a kind of "identifier". Every contextual information
        // gathered during transformation that we want to save for given operation, is actually saved for the original operation.
        // This way no matter if operation `a` is cloned, then transformed, even breaks, we still have access to the previously
        // gathered data through original operation reference.
        this.originalOperations = new Map();
        // `model.History` instance which information about undone operations will be taken from.
        this._history = document.history;
        // Whether additional context should be used.
        this._useRelations = useRelations;
        this._forceWeakRemove = !!forceWeakRemove;
        // Relations is a double-map structure (maps in map) where for two operations we store how those operations were related
        // to each other. Those relations are evaluated during transformation process. For every transformated pair of operations
        // we keep relations between them.
        this._relations = new Map();
    }
    // Sets "original operation" for given operations.
    //
    // During transformation process, operations are cloned, then changed, then processed again, sometimes broken into two
    // or multiple operations. When gathering additional data it is important that all operations can be somehow linked
    // so a cloned and transformed "version" still kept track of the data assigned earlier to it.
    //
    // The original operation object will be used as such an universal linking id. Throughout the transformation process
    // all cloned operations will refer to "the original operation" when storing and reading additional data.
    //
    // If `takeFrom` is not set, each operation from `operations` array will be assigned itself as "the original operation".
    // This should be used as an initialization step.
    //
    // If `takeFrom` is set, each operation from `operations` will be assigned the same original operation as assigned
    // for `takeFrom` operation. This should be used to update original operations. It should be used in a way that
    // `operations` are the result of `takeFrom` transformation to ensure proper "original operation propagation".
    //
    // @param {Array.<module:engine/model/operation/operation~Operation>} operations
    // @param {module:engine/model/operation/operation~Operation|null} [takeFrom=null]
    setOriginalOperations(operations, takeFrom = null) {
        const originalOperation = takeFrom ? this.originalOperations.get(takeFrom) : null;
        for (const operation of operations) {
            this.originalOperations.set(operation, originalOperation || operation);
        }
    }
    // Saves a relation between operations `opA` and `opB`.
    //
    // Relations are then later used to help solve conflicts when operations are transformed.
    //
    // @param {module:engine/model/operation/operation~Operation} opA
    // @param {module:engine/model/operation/operation~Operation} opB
    updateRelation(opA, opB) {
        // The use of relations is described in a bigger detail in transformation functions.
        //
        // In brief, this function, for specified pairs of operation types, checks how positions defined in those operations relate.
        // Then those relations are saved. For example, for two move operations, it is saved if one of those operations target
        // position is before the other operation source position. This kind of information gives contextual information when
        // transformation is used during undo. Similar checks are done for other pairs of operations.
        //
        if (opA instanceof MoveOperation) {
            if (opB instanceof MergeOperation) {
                if (opA.targetPosition.isEqual(opB.sourcePosition) || opB.movedRange.containsPosition(opA.targetPosition)) {
                    this._setRelation(opA, opB, 'insertAtSource');
                }
                else if (opA.targetPosition.isEqual(opB.deletionPosition)) {
                    this._setRelation(opA, opB, 'insertBetween');
                }
                else if (opA.targetPosition.isAfter(opB.sourcePosition)) {
                    this._setRelation(opA, opB, 'moveTargetAfter');
                }
            }
            else if (opB instanceof MoveOperation) {
                if (opA.targetPosition.isEqual(opB.sourcePosition) || opA.targetPosition.isBefore(opB.sourcePosition)) {
                    this._setRelation(opA, opB, 'insertBefore');
                }
                else {
                    this._setRelation(opA, opB, 'insertAfter');
                }
            }
        }
        else if (opA instanceof SplitOperation) {
            if (opB instanceof MergeOperation) {
                if (opA.splitPosition.isBefore(opB.sourcePosition)) {
                    this._setRelation(opA, opB, 'splitBefore');
                }
            }
            else if (opB instanceof MoveOperation) {
                if (opA.splitPosition.isEqual(opB.sourcePosition) || opA.splitPosition.isBefore(opB.sourcePosition)) {
                    this._setRelation(opA, opB, 'splitBefore');
                }
                else {
                    const range = range_Range._createFromPositionAndShift(opB.sourcePosition, opB.howMany);
                    if (opA.splitPosition.hasSameParentAs(opB.sourcePosition) && range.containsPosition(opA.splitPosition)) {
                        const howMany = range.end.offset - opA.splitPosition.offset;
                        const offset = opA.splitPosition.offset - range.start.offset;
                        this._setRelation(opA, opB, { howMany, offset });
                    }
                }
            }
        }
        else if (opA instanceof MergeOperation) {
            if (opB instanceof MergeOperation) {
                if (!opA.targetPosition.isEqual(opB.sourcePosition)) {
                    this._setRelation(opA, opB, 'mergeTargetNotMoved');
                }
                if (opA.sourcePosition.isEqual(opB.targetPosition)) {
                    this._setRelation(opA, opB, 'mergeSourceNotMoved');
                }
                if (opA.sourcePosition.isEqual(opB.sourcePosition)) {
                    this._setRelation(opA, opB, 'mergeSameElement');
                }
            }
            else if (opB instanceof SplitOperation) {
                if (opA.sourcePosition.isEqual(opB.splitPosition)) {
                    this._setRelation(opA, opB, 'splitAtSource');
                }
            }
        }
        else if (opA instanceof MarkerOperation) {
            const markerRange = opA.newRange;
            if (!markerRange) {
                return;
            }
            if (opB instanceof MoveOperation) {
                const movedRange = range_Range._createFromPositionAndShift(opB.sourcePosition, opB.howMany);
                const affectedLeft = movedRange.containsPosition(markerRange.start) ||
                    movedRange.start.isEqual(markerRange.start);
                const affectedRight = movedRange.containsPosition(markerRange.end) ||
                    movedRange.end.isEqual(markerRange.end);
                if ((affectedLeft || affectedRight) && !movedRange.containsRange(markerRange)) {
                    this._setRelation(opA, opB, {
                        side: affectedLeft ? 'left' : 'right',
                        path: affectedLeft ? markerRange.start.path.slice() : markerRange.end.path.slice()
                    });
                }
            }
            else if (opB instanceof MergeOperation) {
                const wasInLeftElement = markerRange.start.isEqual(opB.targetPosition);
                const wasStartBeforeMergedElement = markerRange.start.isEqual(opB.deletionPosition);
                const wasEndBeforeMergedElement = markerRange.end.isEqual(opB.deletionPosition);
                const wasInRightElement = markerRange.end.isEqual(opB.sourcePosition);
                if (wasInLeftElement || wasStartBeforeMergedElement || wasEndBeforeMergedElement || wasInRightElement) {
                    this._setRelation(opA, opB, {
                        wasInLeftElement,
                        wasStartBeforeMergedElement,
                        wasEndBeforeMergedElement,
                        wasInRightElement
                    });
                }
            }
        }
    }
    // Evaluates and returns contextual information about two given operations `opA` and `opB` which are about to be transformed.
    //
    // @param {module:engine/model/operation/operation~Operation} opA
    // @param {module:engine/model/operation/operation~Operation} opB
    // @returns {module:engine/model/operation/transform~TransformationContext}
    getContext(opA, opB, aIsStrong) {
        return {
            aIsStrong,
            aWasUndone: this._wasUndone(opA),
            bWasUndone: this._wasUndone(opB),
            abRelation: this._useRelations ? this._getRelation(opA, opB) : null,
            baRelation: this._useRelations ? this._getRelation(opB, opA) : null,
            forceWeakRemove: this._forceWeakRemove
        };
    }
    // Returns whether given operation `op` has already been undone.
    //
    // Information whether an operation was undone gives more context when making a decision when two operations are in conflict.
    //
    // @param {module:engine/model/operation/operation~Operation} op
    // @returns {Boolean}
    _wasUndone(op) {
        // For `op`, get its original operation. After all, if `op` is a clone (or even transformed clone) of another
        // operation, literally `op` couldn't be undone. It was just generated. If anything, it was the operation it origins
        // from which was undone. So get that original operation.
        const originalOp = this.originalOperations.get(op);
        // And check with the document if the original operation was undone.
        return originalOp.wasUndone || this._history.isUndoneOperation(originalOp);
    }
    // Returns a relation between `opA` and an operation which is undone by `opB`. This can be `String` value if a relation
    // was set earlier or `null` if there was no relation between those operations.
    //
    // This is a little tricky to understand, so let's compare it to `ContextFactory#_wasUndone`.
    //
    // When `wasUndone( opB )` is used, we check if the `opB` has already been undone. It is obvious, that the
    // undoing operation must happen after the undone operation. So, essentially, we have `opB`, we take document history,
    // we look forward in the future and ask if in that future `opB` was undone.
    //
    // Relations is a backward process to `wasUndone()`.
    //
    // Long story short - using relations is asking what happened in the past. Looking back. This time we have an undoing
    // operation `opB` which has undone some other operation. When there is a transformation `opA` x `opB` and there is
    // a conflict to solve and `opB` is an undoing operation, we can look back in the history and see what was a relation
    // between `opA` and the operation which `opB` undone. Basing on that relation from the past, we can now make
    // a better decision when resolving a conflict between two operations, because we know more about the context of
    // those two operations.
    //
    // This is why this function does not return a relation directly between `opA` and `opB` because we need to look
    // back to search for a meaningful contextual information.
    //
    // @param {module:engine/model/operation/operation~Operation} opA
    // @param {module:engine/model/operation/operation~Operation} opB
    // @returns {String|null}
    _getRelation(opA, opB) {
        // Get the original operation. Similarly as in `wasUndone()` it is used as an universal identifier for stored data.
        const origB = this.originalOperations.get(opB);
        const undoneB = this._history.getUndoneOperation(origB);
        // If `opB` is not undoing any operation, there is no relation.
        if (!undoneB) {
            return null;
        }
        const origA = this.originalOperations.get(opA);
        const relationsA = this._relations.get(origA);
        // Get all relations for `opA`, and check if there is a relation with `opB`-undone-counterpart. If so, return it.
        if (relationsA) {
            return relationsA.get(undoneB) || null;
        }
        return null;
    }
    // Helper function for `ContextFactory#updateRelations`.
    //
    // @private
    // @param {module:engine/model/operation/operation~Operation} opA
    // @param {module:engine/model/operation/operation~Operation} opB
    // @param {String} relation
    _setRelation(opA, opB, relation) {
        // As always, setting is for original operations, not the clones/transformed operations.
        const origA = this.originalOperations.get(opA);
        const origB = this.originalOperations.get(opB);
        let relationsA = this._relations.get(origA);
        if (!relationsA) {
            relationsA = new Map();
            this._relations.set(origA, relationsA);
        }
        relationsA.set(origB, relation);
    }
}
/**
 * An utility function that updates {@link module:engine/model/operation/operation~Operation#baseVersion base versions}
 * of passed operations.
 *
 * The function simply sets `baseVersion` as a base version of the first passed operation and then increments it for
 * each following operation in `operations`.
 *
 * @private
 * @param {Array.<module:engine/model/operation/operation~Operation>} operations Operations to update.
 * @param {Number} baseVersion Base version to set for the first operation in `operations`.
 */
function updateBaseVersions(operations, baseVersion) {
    for (const operation of operations) {
        operation.baseVersion = baseVersion++;
    }
}
/**
 * Adds `howMany` instances of {@link module:engine/model/operation/nooperation~NoOperation} to `operations` set.
 *
 * @private
 * @param {Array.<module:engine/model/operation/operation~Operation>} operations
 * @param {Number} howMany
 */
function padWithNoOps(operations, howMany) {
    for (let i = 0; i < howMany; i++) {
        operations.push(new NoOperation(0));
    }
}
// -----------------------
setTransformation(AttributeOperation, AttributeOperation, (a, b, context) => {
    // If operations in conflict, check if their ranges intersect and manage them properly.
    //
    // Operations can be in conflict only if:
    //
    // * their key is the same (they change the same attribute), and
    // * they are in the same parent (operations for ranges [ 1 ] - [ 3 ] and [ 2, 0 ] - [ 2, 5 ] change different
    // elements and can't be in conflict).
    if (a.key === b.key && a.range.start.hasSameParentAs(b.range.start)) {
        // First, we want to apply change to the part of a range that has not been changed by the other operation.
        const operations = a.range.getDifference(b.range).map(range => {
            return new AttributeOperation(range, a.key, a.oldValue, a.newValue, 0);
        });
        // Then we take care of the common part of ranges.
        const common = a.range.getIntersection(b.range);
        if (common) {
            // If this operation is more important, we also want to apply change to the part of the
            // original range that has already been changed by the other operation. Since that range
            // got changed we also have to update `oldValue`.
            if (context.aIsStrong) {
                operations.push(new AttributeOperation(common, b.key, b.newValue, a.newValue, 0));
            }
        }
        if (operations.length == 0) {
            return [new NoOperation(0)];
        }
        return operations;
    }
    else {
        // If operations don't conflict, simply return an array containing just a clone of this operation.
        return [a];
    }
});
setTransformation(AttributeOperation, InsertOperation, (a, b) => {
    // Case 1:
    //
    // The attribute operation range includes the position where nodes were inserted.
    // There are two possible scenarios: the inserted nodes were text and they should receive attributes or
    // the inserted nodes were elements and they should not receive attributes.
    //
    if (a.range.start.hasSameParentAs(b.position) && a.range.containsPosition(b.position)) {
        // If new nodes should not receive attributes, two separated ranges will be returned.
        // Otherwise, one expanded range will be returned.
        const range = a.range._getTransformedByInsertion(b.position, b.howMany, !b.shouldReceiveAttributes);
        const result = range.map(r => {
            return new AttributeOperation(r, a.key, a.oldValue, a.newValue, a.baseVersion);
        });
        if (b.shouldReceiveAttributes) {
            // `AttributeOperation#range` includes some newly inserted text.
            // The operation should also change the attribute of that text. An example:
            //
            // Bold should be applied on the following range:
            // <p>Fo[zb]ar</p>
            //
            // In meantime, new text is typed:
            // <p>Fozxxbar</p>
            //
            // Bold should be applied also on the new text:
            // <p>Fo[zxxb]ar</p>
            // <p>Fo<$text bold="true">zxxb</$text>ar</p>
            //
            // There is a special case to consider here to consider.
            //
            // Consider setting an attribute with multiple possible values, for example `highlight`. The inserted text might
            // have already an attribute value applied and the `oldValue` property of the attribute operation might be wrong:
            //
            // Attribute `highlight="yellow"` should be applied on the following range:
            // <p>Fo[zb]ar<p>
            //
            // In meantime, character `x` with `highlight="red"` is typed:
            // <p>Fo[z<$text highlight="red">x</$text>b]ar</p>
            //
            // In this case we cannot simply apply operation changing the attribute value from `null` to `"yellow"` for the whole range
            // because that would lead to an exception (`oldValue` is incorrect for `x`).
            //
            // We also cannot break the original range as this would mess up a scenario when there are multiple following
            // insert operations, because then only the first inserted character is included in those ranges:
            // <p>Fo[z][x][b]ar</p>   -->   <p>Fo[z][x]x[b]ar</p>   -->   <p>Fo[z][x]xx[b]ar</p>
            //
            // So, the attribute range needs be expanded, no matter what attributes are set on the inserted nodes:
            //
            // <p>Fo[z<$text highlight="red">x</$text>b]ar</p>      <--- Change from `null` to `yellow`, throwing an exception.
            //
            // But before that operation would be applied, we will add an additional attribute operation that will change
            // attributes on the inserted nodes in a way which would make the original operation correct:
            //
            // <p>Fo[z{<$text highlight="red">}x</$text>b]ar</p>    <--- Change range `{}` from `red` to `null`.
            // <p>Fo[zxb]ar</p>                                     <--- Now change from `null` to `yellow` is completely fine.
            //
            // Generate complementary attribute operation. Be sure to add it before the original operation.
            const op = _getComplementaryAttributeOperations(b, a.key, a.oldValue);
            if (op) {
                result.unshift(op);
            }
        }
        // If nodes should not receive new attribute, we are done here.
        return result;
    }
    // If insert operation is not expanding the attribute operation range, simply transform the range.
    a.range = a.range._getTransformedByInsertion(b.position, b.howMany, false)[0];
    return [a];
});
/**
 * Helper function for `AttributeOperation` x `InsertOperation` (and reverse) transformation.
 *
 * For given `insertOperation` it checks the inserted node if it has an attribute `key` set to a value different
 * than `newValue`. If so, it generates an `AttributeOperation` which changes the value of `key` attribute to `newValue`.
 *
 * @private
 * @param {module:engine/model/operation/insertoperation~InsertOperation} insertOperation
 * @param {String} key
 * @param {*} newValue
 * @returns {module:engine/model/operation/attributeoperation~AttributeOperation|null}
 */
function _getComplementaryAttributeOperations(insertOperation, key, newValue) {
    const nodes = insertOperation.nodes;
    // At the beginning we store the attribute value from the first node.
    const insertValue = nodes.getNode(0).getAttribute(key);
    if (insertValue == newValue) {
        return null;
    }
    const range = new range_Range(insertOperation.position, insertOperation.position.getShiftedBy(insertOperation.howMany));
    return new AttributeOperation(range, key, insertValue, newValue, 0);
}
setTransformation(AttributeOperation, MergeOperation, (a, b) => {
    const ranges = [];
    // Case 1:
    //
    // Attribute change on the merged element. In this case, the merged element was moved to the graveyard.
    // An additional attribute operation that will change the (re)moved element needs to be generated.
    //
    if (a.range.start.hasSameParentAs(b.deletionPosition)) {
        if (a.range.containsPosition(b.deletionPosition) || a.range.start.isEqual(b.deletionPosition)) {
            ranges.push(range_Range._createFromPositionAndShift(b.graveyardPosition, 1));
        }
    }
    const range = a.range._getTransformedByMergeOperation(b);
    // Do not add empty (collapsed) ranges to the result. `range` may be collapsed if it contained only the merged element.
    if (!range.isCollapsed) {
        ranges.push(range);
    }
    // Create `AttributeOperation`s out of the ranges.
    return ranges.map(range => {
        return new AttributeOperation(range, a.key, a.oldValue, a.newValue, a.baseVersion);
    });
});
setTransformation(AttributeOperation, MoveOperation, (a, b) => {
    const ranges = _breakRangeByMoveOperation(a.range, b);
    // Create `AttributeOperation`s out of the ranges.
    return ranges.map(range => new AttributeOperation(range, a.key, a.oldValue, a.newValue, a.baseVersion));
});
// Helper function for `AttributeOperation` x `MoveOperation` transformation.
//
// Takes the passed `range` and transforms it by move operation `moveOp` in a specific way. Only top-level nodes of `range`
// are considered to be in the range. If move operation moves nodes deep from inside of the range, those nodes won't
// be included in the result. In other words, top-level nodes of the ranges from the result are exactly the same as
// top-level nodes of the original `range`.
//
// This is important for `AttributeOperation` because, for its range, it changes only the top-level nodes. So we need to
// track only how those nodes have been affected by `MoveOperation`.
//
// @private
// @param {module:engine/model/range~Range} range
// @param {module:engine/model/operation/moveoperation~MoveOperation} moveOp
// @returns {Array.<module:engine/model/range~Range>}
function _breakRangeByMoveOperation(range, moveOp) {
    const moveRange = range_Range._createFromPositionAndShift(moveOp.sourcePosition, moveOp.howMany);
    // We are transforming `range` (original range) by `moveRange` (range moved by move operation). As usual when it comes to
    // transforming a ranges, we may have a common part of the ranges and we may have a difference part (zero to two ranges).
    let common = null;
    let difference = [];
    // Let's compare the ranges.
    if (moveRange.containsRange(range, true)) {
        // If the whole original range is moved, treat it whole as a common part. There's also no difference part.
        common = range;
    }
    else if (range.start.hasSameParentAs(moveRange.start)) {
        // If the ranges are "on the same level" (in the same parent) then move operation may move exactly those nodes
        // that are changed by the attribute operation. In this case we get common part and difference part in the usual way.
        difference = range.getDifference(moveRange);
        common = range.getIntersection(moveRange);
    }
    else {
        // In any other situation we assume that original range is different than move range, that is that move operation
        // moves other nodes that attribute operation change. Even if the moved range is deep inside in the original range.
        //
        // Note that this is different than in `.getIntersection` (we would get a common part in that case) and different
        // than `.getDifference` (we would get two ranges).
        difference = [range];
    }
    const result = [];
    // The default behaviour of `_getTransformedByMove` might get wrong results for difference part, though, so
    // we do it by hand.
    for (let diff of difference) {
        // First, transform the range by removing moved nodes. Since this is a difference, this is safe, `null` won't be returned
        // as the range is different than the moved range.
        diff = diff._getTransformedByDeletion(moveOp.sourcePosition, moveOp.howMany);
        // Transform also `targetPosition`.
        const targetPosition = moveOp.getMovedRangeStart();
        // Spread the range only if moved nodes are inserted only between the top-level nodes of the `diff` range.
        const spread = diff.start.hasSameParentAs(targetPosition);
        // Transform by insertion of moved nodes.
        const diffs = diff._getTransformedByInsertion(targetPosition, moveOp.howMany, spread);
        result.push(...diffs);
    }
    // Common part can be simply transformed by the move operation. This is because move operation will not target to
    // that common part (the operation would have to target inside its own moved range).
    if (common) {
        result.push(common._getTransformedByMove(moveOp.sourcePosition, moveOp.targetPosition, moveOp.howMany, false)[0]);
    }
    return result;
}
setTransformation(AttributeOperation, SplitOperation, (a, b) => {
    // Case 1:
    //
    // Split node is the last node in `AttributeOperation#range`.
    // `AttributeOperation#range` needs to be expanded to include the new (split) node.
    //
    // Attribute `type` to be changed to `numbered` but the `listItem` is split.
    // <listItem type="bulleted">foobar</listItem>
    //
    // After split:
    // <listItem type="bulleted">foo</listItem><listItem type="bulleted">bar</listItem>
    //
    // After attribute change:
    // <listItem type="numbered">foo</listItem><listItem type="numbered">foo</listItem>
    //
    if (a.range.end.isEqual(b.insertionPosition)) {
        if (!b.graveyardPosition) {
            a.range.end.offset++;
        }
        return [a];
    }
    // Case 2:
    //
    // Split position is inside `AttributeOperation#range`, at the same level, so the nodes to change are
    // not going to make a flat range.
    //
    // Content with range-to-change and split position:
    // <p>Fo[zb^a]r</p>
    //
    // After split:
    // <p>Fozb</p><p>ar</p>
    //
    // Make two separate ranges containing all nodes to change:
    // <p>Fo[zb]</p><p>[a]r</p>
    //
    if (a.range.start.hasSameParentAs(b.splitPosition) && a.range.containsPosition(b.splitPosition)) {
        const secondPart = a.clone();
        secondPart.range = new range_Range(b.moveTargetPosition.clone(), a.range.end._getCombined(b.splitPosition, b.moveTargetPosition));
        a.range.end = b.splitPosition.clone();
        a.range.end.stickiness = 'toPrevious';
        return [a, secondPart];
    }
    // The default case.
    //
    a.range = a.range._getTransformedBySplitOperation(b);
    return [a];
});
setTransformation(InsertOperation, AttributeOperation, (a, b) => {
    const result = [a];
    // Case 1:
    //
    // The attribute operation range includes the position where nodes were inserted.
    // There are two possible scenarios: the inserted nodes were text and they should receive attributes or
    // the inserted nodes were elements and they should not receive attributes.
    //
    // This is a mirror scenario to the one described in `AttributeOperation` x `InsertOperation` transformation,
    // although this case is a little less complicated. In this case we simply need to change attributes of the
    // inserted nodes and that's it.
    //
    if (a.shouldReceiveAttributes && a.position.hasSameParentAs(b.range.start) && b.range.containsPosition(a.position)) {
        const op = _getComplementaryAttributeOperations(a, b.key, b.newValue);
        if (op) {
            result.push(op);
        }
    }
    // The default case is: do nothing.
    // `AttributeOperation` does not change the model tree structure so `InsertOperation` does not need to be changed.
    //
    return result;
});
setTransformation(InsertOperation, InsertOperation, (a, b, context) => {
    // Case 1:
    //
    // Two insert operations insert nodes at the same position. Since they are the same, it needs to be decided
    // what will be the order of inserted nodes. However, there is no additional information to help in that
    // decision. Also, when `b` will be transformed by `a`, the same order must be maintained.
    //
    // To achieve that, we will check if the operation is strong.
    // If it is, it won't get transformed. If it is not, it will be moved.
    //
    if (a.position.isEqual(b.position) && context.aIsStrong) {
        return [a];
    }
    // The default case.
    //
    a.position = a.position._getTransformedByInsertOperation(b);
    return [a];
});
setTransformation(InsertOperation, MoveOperation, (a, b) => {
    // The default case.
    //
    a.position = a.position._getTransformedByMoveOperation(b);
    return [a];
});
setTransformation(InsertOperation, SplitOperation, (a, b) => {
    // The default case.
    //
    a.position = a.position._getTransformedBySplitOperation(b);
    return [a];
});
setTransformation(InsertOperation, MergeOperation, (a, b) => {
    a.position = a.position._getTransformedByMergeOperation(b);
    return [a];
});
// -----------------------
setTransformation(MarkerOperation, InsertOperation, (a, b) => {
    if (a.oldRange) {
        a.oldRange = a.oldRange._getTransformedByInsertOperation(b)[0];
    }
    if (a.newRange) {
        a.newRange = a.newRange._getTransformedByInsertOperation(b)[0];
    }
    return [a];
});
setTransformation(MarkerOperation, MarkerOperation, (a, b, context) => {
    if (a.name == b.name) {
        if (context.aIsStrong) {
            a.oldRange = b.newRange ? b.newRange.clone() : null;
        }
        else {
            return [new NoOperation(0)];
        }
    }
    return [a];
});
setTransformation(MarkerOperation, MergeOperation, (a, b) => {
    if (a.oldRange) {
        a.oldRange = a.oldRange._getTransformedByMergeOperation(b);
    }
    if (a.newRange) {
        a.newRange = a.newRange._getTransformedByMergeOperation(b);
    }
    return [a];
});
setTransformation(MarkerOperation, MoveOperation, (a, b, context) => {
    if (a.oldRange) {
        a.oldRange = range_Range._createFromRanges(a.oldRange._getTransformedByMoveOperation(b));
    }
    if (a.newRange) {
        if (context.abRelation) {
            const aNewRange = range_Range._createFromRanges(a.newRange._getTransformedByMoveOperation(b));
            if (context.abRelation.side == 'left' && b.targetPosition.isEqual(a.newRange.start)) {
                a.newRange.end = aNewRange.end;
                a.newRange.start.path = context.abRelation.path;
                return [a];
            }
            else if (context.abRelation.side == 'right' && b.targetPosition.isEqual(a.newRange.end)) {
                a.newRange.start = aNewRange.start;
                a.newRange.end.path = context.abRelation.path;
                return [a];
            }
        }
        a.newRange = range_Range._createFromRanges(a.newRange._getTransformedByMoveOperation(b));
    }
    return [a];
});
setTransformation(MarkerOperation, SplitOperation, (a, b, context) => {
    if (a.oldRange) {
        a.oldRange = a.oldRange._getTransformedBySplitOperation(b);
    }
    if (a.newRange) {
        if (context.abRelation) {
            const aNewRange = a.newRange._getTransformedBySplitOperation(b);
            if (a.newRange.start.isEqual(b.splitPosition) && context.abRelation.wasStartBeforeMergedElement) {
                a.newRange.start = position_Position._createAt(b.insertionPosition);
            }
            else if (a.newRange.start.isEqual(b.splitPosition) && !context.abRelation.wasInLeftElement) {
                a.newRange.start = position_Position._createAt(b.moveTargetPosition);
            }
            if (a.newRange.end.isEqual(b.splitPosition) && context.abRelation.wasInRightElement) {
                a.newRange.end = position_Position._createAt(b.moveTargetPosition);
            }
            else if (a.newRange.end.isEqual(b.splitPosition) && context.abRelation.wasEndBeforeMergedElement) {
                a.newRange.end = position_Position._createAt(b.insertionPosition);
            }
            else {
                a.newRange.end = aNewRange.end;
            }
            return [a];
        }
        a.newRange = a.newRange._getTransformedBySplitOperation(b);
    }
    return [a];
});
// -----------------------
setTransformation(MergeOperation, InsertOperation, (a, b) => {
    if (a.sourcePosition.hasSameParentAs(b.position)) {
        a.howMany += b.howMany;
    }
    a.sourcePosition = a.sourcePosition._getTransformedByInsertOperation(b);
    a.targetPosition = a.targetPosition._getTransformedByInsertOperation(b);
    return [a];
});
setTransformation(MergeOperation, MergeOperation, (a, b, context) => {
    // Case 1:
    //
    // Same merge operations.
    //
    // Both operations have same source and target positions. So the element already got merged and there is
    // theoretically nothing to do.
    //
    if (a.sourcePosition.isEqual(b.sourcePosition) && a.targetPosition.isEqual(b.targetPosition)) {
        // There are two ways that we can provide a do-nothing operation.
        //
        // First is simply a NoOperation instance. We will use it if `b` operation was not undone.
        //
        // Second is a merge operation that has the source operation in the merged element - in the graveyard -
        // same target position and `howMany` equal to `0`. So it is basically merging an empty element from graveyard
        // which is almost the same as NoOperation.
        //
        // This way the merge operation can be later transformed by split operation
        // to provide correct undo. This will be used if `b` operation was undone (only then it is correct).
        //
        if (!context.bWasUndone) {
            return [new NoOperation(0)];
        }
        else {
            const path = b.graveyardPosition.path.slice();
            path.push(0);
            a.sourcePosition = new position_Position(b.graveyardPosition.root, path);
            a.howMany = 0;
            return [a];
        }
    }
    // Case 2:
    //
    // Same merge source position but different target position.
    //
    // This can happen during collaboration. For example, if one client merged a paragraph to the previous paragraph
    // and the other person removed that paragraph and merged the same paragraph to something before:
    //
    // Client A:
    // <p>Foo</p><p>Bar</p><p>[]Xyz</p>
    // <p>Foo</p><p>BarXyz</p>
    //
    // Client B:
    // <p>Foo</p>[<p>Bar</p>]<p>Xyz</p>
    // <p>Foo</p><p>[]Xyz</p>
    // <p>FooXyz</p>
    //
    // In this case we need to decide where finally "Xyz" will land:
    //
    // <p>FooXyz</p>               graveyard: <p>Bar</p>
    // <p>Foo</p>                  graveyard: <p>BarXyz</p>
    //
    // Let's move it in a way so that a merge operation that does not target to graveyard is more important so that
    // nodes does not end up in the graveyard. It makes sense. Both for Client A and for Client B "Xyz" finally did not
    // end up in the graveyard (see above).
    //
    // If neither or both operations point to graveyard, then let `aIsStrong` decide.
    //
    if (a.sourcePosition.isEqual(b.sourcePosition) && !a.targetPosition.isEqual(b.targetPosition) &&
        !context.bWasUndone && context.abRelation != 'splitAtSource') {
        const aToGraveyard = a.targetPosition.root.rootName == '$graveyard';
        const bToGraveyard = b.targetPosition.root.rootName == '$graveyard';
        // If `aIsWeak` it means that `a` points to graveyard while `b` doesn't. Don't move nodes then.
        const aIsWeak = aToGraveyard && !bToGraveyard;
        // If `bIsWeak` it means that `b` points to graveyard while `a` doesn't. Force moving nodes then.
        const bIsWeak = bToGraveyard && !aToGraveyard;
        // Force move if `b` is weak or neither operation is weak but `a` is stronger through `context.aIsStrong`.
        const forceMove = bIsWeak || (!aIsWeak && context.aIsStrong);
        if (forceMove) {
            const sourcePosition = b.targetPosition._getTransformedByMergeOperation(b);
            const targetPosition = a.targetPosition._getTransformedByMergeOperation(b);
            return [new MoveOperation(sourcePosition, a.howMany, targetPosition, 0)];
        }
        else {
            return [new NoOperation(0)];
        }
    }
    // The default case.
    //
    if (a.sourcePosition.hasSameParentAs(b.targetPosition)) {
        a.howMany += b.howMany;
    }
    a.sourcePosition = a.sourcePosition._getTransformedByMergeOperation(b);
    a.targetPosition = a.targetPosition._getTransformedByMergeOperation(b);
    // Handle positions in graveyard.
    // If graveyard positions are same and `a` operation is strong - do not transform.
    if (!a.graveyardPosition.isEqual(b.graveyardPosition) || !context.aIsStrong) {
        a.graveyardPosition = a.graveyardPosition._getTransformedByMergeOperation(b);
    }
    return [a];
});
setTransformation(MergeOperation, MoveOperation, (a, b, context) => {
    // Case 1:
    //
    // The element to merge got removed.
    //
    // Merge operation does support merging elements which are not siblings. So it would not be a problem
    // from technical point of view. However, if the element was removed, the intention of the user deleting it
    // was to have it all deleted, together with its children. From user experience point of view, moving back the
    // removed nodes might be unexpected. This means that in this scenario we will block the merging.
    //
    // The exception of this rule would be if the remove operation was later undone.
    //
    const removedRange = range_Range._createFromPositionAndShift(b.sourcePosition, b.howMany);
    if (b.type == 'remove' && !context.bWasUndone && !context.forceWeakRemove) {
        if (a.deletionPosition.hasSameParentAs(b.sourcePosition) && removedRange.containsPosition(a.sourcePosition)) {
            return [new NoOperation(0)];
        }
    }
    // The default case.
    //
    if (a.sourcePosition.hasSameParentAs(b.targetPosition)) {
        a.howMany += b.howMany;
    }
    if (a.sourcePosition.hasSameParentAs(b.sourcePosition)) {
        a.howMany -= b.howMany;
    }
    a.sourcePosition = a.sourcePosition._getTransformedByMoveOperation(b);
    a.targetPosition = a.targetPosition._getTransformedByMoveOperation(b);
    // `MergeOperation` graveyard position is like `MoveOperation` target position. It is a position where element(s) will
    // be moved. Like in other similar cases, we need to consider the scenario when those positions are same.
    // Here, we will treat `MergeOperation` like it is always strong (see `InsertOperation` x `InsertOperation` for comparison).
    // This means that we won't transform graveyard position if it is equal to move operation target position.
    if (!a.graveyardPosition.isEqual(b.targetPosition)) {
        a.graveyardPosition = a.graveyardPosition._getTransformedByMoveOperation(b);
    }
    return [a];
});
setTransformation(MergeOperation, SplitOperation, (a, b, context) => {
    if (b.graveyardPosition) {
        // If `b` operation defines graveyard position, a node from graveyard will be moved. This means that we need to
        // transform `a.graveyardPosition` accordingly.
        a.graveyardPosition = a.graveyardPosition._getTransformedByDeletion(b.graveyardPosition, 1);
        // This is a scenario foreseen in `MergeOperation` x `MergeOperation`, with two identical merge operations.
        //
        // So, there was `MergeOperation` x `MergeOperation` transformation earlier. Now, `a` is a merge operation which
        // source position is in graveyard. Interestingly, split operation wants to use the node to be merged by `a`. This
        // means that `b` is undoing that merge operation from earlier, which caused `a` to be in graveyard.
        //
        // If that's the case, at this point, we will only "fix" `a.howMany`. It was earlier set to `0` in
        // `MergeOperation` x `MergeOperation` transformation. Later transformations in this function will change other
        // properties.
        //
        if (a.deletionPosition.isEqual(b.graveyardPosition)) {
            a.howMany = b.howMany;
        }
    }
    // Case 1:
    //
    // Merge operation moves nodes to the place where split happens.
    // This is a classic situation when there are two paragraphs, and there is a split (enter) after the first
    // paragraph and there is a merge (delete) at the beginning of the second paragraph:
    //
    // <p>Foo{}</p><p>[]Bar</p>.
    //
    // Split is after `Foo`, while merge is from `Bar` to the end of `Foo`.
    //
    // State after split:
    // <p>Foo</p><p></p><p>Bar</p>
    //
    // Now, `Bar` should be merged to the new paragraph:
    // <p>Foo</p><p>Bar</p>
    //
    // Instead of merging it to the original paragraph:
    // <p>FooBar</p><p></p>
    //
    // This means that `targetPosition` needs to be transformed. This is the default case though.
    // For example, if the split would be after `F`, `targetPosition` should also be transformed.
    //
    // There are three exceptions, though, when we want to keep `targetPosition` as it was.
    //
    // First exception is when the merge target position is inside an element (not at the end, as usual). This
    // happens when the merge operation earlier was transformed by "the same" merge operation. If merge operation
    // targets inside the element we want to keep the original target position (and not transform it) because
    // we have additional context telling us that we want to merge to the original element. We can check if the
    // merge operation points inside element by checking what is `SplitOperation#howMany`. Since merge target position
    // is same as split position, if `howMany` is non-zero, it means that the merge target position is inside an element.
    //
    // Second exception is when the element to merge is in the graveyard and split operation uses it. In that case
    // if target position would be transformed, the merge operation would target at the source position:
    //
    // root: <p>Foo</p>				graveyard: <p></p>
    //
    // SplitOperation: root [ 0, 3 ] using graveyard [ 0 ] (howMany = 0)
    // MergeOperation: graveyard [ 0, 0 ] -> root [ 0, 3 ] (howMany = 0)
    //
    // Since split operation moves the graveyard node back to the root, the merge operation source position changes.
    // We would like to merge from the empty <p> to the "Foo" <p>:
    //
    // root: <p>Foo</p><p></p>			graveyard:
    //
    // MergeOperation#sourcePosition = root [ 1, 0 ]
    //
    // If `targetPosition` is transformed, it would become root [ 1, 0 ] as well. It has to be kept as it was.
    //
    // Third exception is connected with relations. If this happens during undo and we have explicit information
    // that target position has not been affected by the operation which is undone by this split then this split should
    // not move the target position either.
    //
    if (a.targetPosition.isEqual(b.splitPosition)) {
        const mergeInside = b.howMany != 0;
        const mergeSplittingElement = b.graveyardPosition && a.deletionPosition.isEqual(b.graveyardPosition);
        if (mergeInside || mergeSplittingElement || context.abRelation == 'mergeTargetNotMoved') {
            a.sourcePosition = a.sourcePosition._getTransformedBySplitOperation(b);
            return [a];
        }
    }
    // Case 2:
    //
    // Merge source is at the same position as split position. This sometimes happen, mostly during undo.
    // The decision here is mostly to choose whether merge source position should stay where it is (so it will be at the end of the
    // split element) or should be move to the beginning of the new element.
    //
    if (a.sourcePosition.isEqual(b.splitPosition)) {
        // Use context to check if `SplitOperation` is not undoing a merge operation, that didn't change the `a` operation.
        // This scenario happens the undone merge operation moved nodes at the source position of `a` operation.
        // In that case `a` operation source position should stay where it is.
        if (context.abRelation == 'mergeSourceNotMoved') {
            a.howMany = 0;
            a.targetPosition = a.targetPosition._getTransformedBySplitOperation(b);
            return [a];
        }
        // This merge operation might have been earlier transformed by a merge operation which both merged the same element.
        // See that case in `MergeOperation` x `MergeOperation` transformation. In that scenario, if the merge operation has been undone,
        // the special case is not applied.
        //
        // Now, the merge operation is transformed by the split which has undone that previous merge operation.
        // So now we are fixing situation which was skipped in `MergeOperation` x `MergeOperation` case.
        //
        if (context.abRelation == 'mergeSameElement' || a.sourcePosition.offset > 0) {
            a.sourcePosition = b.moveTargetPosition.clone();
            a.targetPosition = a.targetPosition._getTransformedBySplitOperation(b);
            return [a];
        }
    }
    // The default case.
    //
    if (a.sourcePosition.hasSameParentAs(b.splitPosition)) {
        a.howMany = b.splitPosition.offset;
    }
    a.sourcePosition = a.sourcePosition._getTransformedBySplitOperation(b);
    a.targetPosition = a.targetPosition._getTransformedBySplitOperation(b);
    return [a];
});
// -----------------------
setTransformation(MoveOperation, InsertOperation, (a, b) => {
    const moveRange = range_Range._createFromPositionAndShift(a.sourcePosition, a.howMany);
    const transformed = moveRange._getTransformedByInsertOperation(b, false)[0];
    a.sourcePosition = transformed.start;
    a.howMany = transformed.end.offset - transformed.start.offset;
    // See `InsertOperation` x `MoveOperation` transformation for details on this case.
    //
    // In summary, both operations point to the same place, so the order of nodes needs to be decided.
    // `MoveOperation` is considered weaker, so it is always transformed, unless there was a certain relation
    // between operations.
    //
    if (!a.targetPosition.isEqual(b.position)) {
        a.targetPosition = a.targetPosition._getTransformedByInsertOperation(b);
    }
    return [a];
});
setTransformation(MoveOperation, MoveOperation, (a, b, context) => {
    //
    // Setting and evaluating some variables that will be used in special cases and default algorithm.
    //
    // Create ranges from `MoveOperations` properties.
    const rangeA = range_Range._createFromPositionAndShift(a.sourcePosition, a.howMany);
    const rangeB = range_Range._createFromPositionAndShift(b.sourcePosition, b.howMany);
    // Assign `context.aIsStrong` to a different variable, because the value may change during execution of
    // this algorithm and we do not want to override original `context.aIsStrong` that will be used in later transformations.
    let aIsStrong = context.aIsStrong;
    // This will be used to decide the order of nodes if both operations target at the same position.
    // By default, use strong/weak operation mechanism.
    let insertBefore = !context.aIsStrong;
    // If the relation is set, then use it to decide nodes order.
    if (context.abRelation == 'insertBefore' || context.baRelation == 'insertAfter') {
        insertBefore = true;
    }
    else if (context.abRelation == 'insertAfter' || context.baRelation == 'insertBefore') {
        insertBefore = false;
    }
    // `a.targetPosition` could be affected by the `b` operation. We will transform it.
    let newTargetPosition;
    if (a.targetPosition.isEqual(b.targetPosition) && insertBefore) {
        newTargetPosition = a.targetPosition._getTransformedByDeletion(b.sourcePosition, b.howMany);
    }
    else {
        newTargetPosition = a.targetPosition._getTransformedByMove(b.sourcePosition, b.targetPosition, b.howMany);
    }
    //
    // Special case #1 + mirror.
    //
    // Special case when both move operations' target positions are inside nodes that are
    // being moved by the other move operation. So in other words, we move ranges into inside of each other.
    // This case can't be solved reasonably (on the other hand, it should not happen often).
    if (_moveTargetIntoMovedRange(a, b) && _moveTargetIntoMovedRange(b, a)) {
        // Instead of transforming operation, we return a reverse of the operation that we transform by.
        // So when the results of this "transformation" will be applied, `b` MoveOperation will get reversed.
        return [b.getReversed()];
    }
    //
    // End of special case #1.
    //
    //
    // Special case #2.
    //
    // Check if `b` operation targets inside `rangeA`.
    const bTargetsToA = rangeA.containsPosition(b.targetPosition);
    // If `b` targets to `rangeA` and `rangeA` contains `rangeB`, `b` operation has no influence on `a` operation.
    // You might say that operation `b` is captured inside operation `a`.
    if (bTargetsToA && rangeA.containsRange(rangeB, true)) {
        // There is a mini-special case here, where `rangeB` is on other level than `rangeA`. That's why
        // we need to transform `a` operation anyway.
        rangeA.start = rangeA.start._getTransformedByMove(b.sourcePosition, b.targetPosition, b.howMany);
        rangeA.end = rangeA.end._getTransformedByMove(b.sourcePosition, b.targetPosition, b.howMany);
        return _makeMoveOperationsFromRanges([rangeA], newTargetPosition);
    }
    //
    // Special case #2 mirror.
    //
    const aTargetsToB = rangeB.containsPosition(a.targetPosition);
    if (aTargetsToB && rangeB.containsRange(rangeA, true)) {
        // `a` operation is "moved together" with `b` operation.
        // Here, just move `rangeA` "inside" `rangeB`.
        rangeA.start = rangeA.start._getCombined(b.sourcePosition, b.getMovedRangeStart());
        rangeA.end = rangeA.end._getCombined(b.sourcePosition, b.getMovedRangeStart());
        return _makeMoveOperationsFromRanges([rangeA], newTargetPosition);
    }
    //
    // End of special case #2.
    //
    //
    // Special case #3 + mirror.
    //
    // `rangeA` has a node which is an ancestor of `rangeB`. In other words, `rangeB` is inside `rangeA`
    // but not on the same tree level. In such case ranges have common part but we have to treat it
    // differently, because in such case those ranges are not really conflicting and should be treated like
    // two separate ranges. Also we have to discard two difference parts.
    const aCompB = compareArrays(a.sourcePosition.getParentPath(), b.sourcePosition.getParentPath());
    if (aCompB == 'prefix' || aCompB == 'extension') {
        // Transform `rangeA` by `b` operation and make operation out of it, and that's all.
        // Note that this is a simplified version of default case, but here we treat the common part (whole `rangeA`)
        // like a one difference part.
        rangeA.start = rangeA.start._getTransformedByMove(b.sourcePosition, b.targetPosition, b.howMany);
        rangeA.end = rangeA.end._getTransformedByMove(b.sourcePosition, b.targetPosition, b.howMany);
        return _makeMoveOperationsFromRanges([rangeA], newTargetPosition);
    }
    //
    // End of special case #3.
    //
    //
    // Default case - ranges are on the same level or are not connected with each other.
    //
    // Modifier for default case.
    // Modifies `aIsStrong` flag in certain conditions.
    //
    // If only one of operations is a remove operation, we force remove operation to be the "stronger" one
    // to provide more expected results.
    if (a.type == 'remove' && b.type != 'remove' && !context.aWasUndone && !context.forceWeakRemove) {
        aIsStrong = true;
    }
    else if (a.type != 'remove' && b.type == 'remove' && !context.bWasUndone && !context.forceWeakRemove) {
        aIsStrong = false;
    }
    // Handle operation's source ranges - check how `rangeA` is affected by `b` operation.
    // This will aggregate transformed ranges.
    const ranges = [];
    // Get the "difference part" of `a` operation source range.
    // This is an array with one or two ranges. Two ranges if `rangeB` is inside `rangeA`.
    const difference = rangeA.getDifference(rangeB);
    for (const range of difference) {
        // Transform those ranges by `b` operation. For example if `b` moved range from before those ranges, fix those ranges.
        range.start = range.start._getTransformedByDeletion(b.sourcePosition, b.howMany);
        range.end = range.end._getTransformedByDeletion(b.sourcePosition, b.howMany);
        // If `b` operation targets into `rangeA` on the same level, spread `rangeA` into two ranges.
        const shouldSpread = compareArrays(range.start.getParentPath(), b.getMovedRangeStart().getParentPath()) == 'same';
        const newRanges = range._getTransformedByInsertion(b.getMovedRangeStart(), b.howMany, shouldSpread);
        ranges.push(...newRanges);
    }
    // Then, we have to manage the "common part" of both move ranges.
    const common = rangeA.getIntersection(rangeB);
    if (common !== null && aIsStrong) {
        // Calculate the new position of that part of original range.
        common.start = common.start._getCombined(b.sourcePosition, b.getMovedRangeStart());
        common.end = common.end._getCombined(b.sourcePosition, b.getMovedRangeStart());
        // Take care of proper range order.
        //
        // Put `common` at appropriate place. Keep in mind that we are interested in original order.
        // Basically there are only three cases: there is zero, one or two difference ranges.
        //
        // If there is zero difference ranges, just push `common` in the array.
        if (ranges.length === 0) {
            ranges.push(common);
        }
        // If there is one difference range, we need to check whether common part was before it or after it.
        else if (ranges.length == 1) {
            if (rangeB.start.isBefore(rangeA.start) || rangeB.start.isEqual(rangeA.start)) {
                ranges.unshift(common);
            }
            else {
                ranges.push(common);
            }
        }
        // If there are more ranges (which means two), put common part between them. This is the only scenario
        // where there could be two difference ranges so we don't have to make any comparisons.
        else {
            ranges.splice(1, 0, common);
        }
    }
    if (ranges.length === 0) {
        // If there are no "source ranges", nothing should be changed.
        // Note that this can happen only if `aIsStrong == false` and `rangeA.isEqual( rangeB )`.
        return [new NoOperation(a.baseVersion)];
    }
    return _makeMoveOperationsFromRanges(ranges, newTargetPosition);
});
setTransformation(MoveOperation, SplitOperation, (a, b, context) => {
    let newTargetPosition = a.targetPosition.clone();
    // Do not transform if target position is same as split insertion position and this split comes from undo.
    // This should be done on relations but it is too much work for now as it would require relations working in collaboration.
    // We need to make a decision how we will resolve such conflict and this is less harmful way.
    if (!a.targetPosition.isEqual(b.insertionPosition) || !b.graveyardPosition || context.abRelation == 'moveTargetAfter') {
        newTargetPosition = a.targetPosition._getTransformedBySplitOperation(b);
    }
    // Case 1:
    //
    // Last element in the moved range got split.
    //
    // In this case the default range transformation will not work correctly as the element created by
    // split operation would be outside the range. The range to move needs to be fixed manually.
    //
    const moveRange = range_Range._createFromPositionAndShift(a.sourcePosition, a.howMany);
    if (moveRange.end.isEqual(b.insertionPosition)) {
        // Do it only if this is a "natural" split, not a one that comes from undo.
        // If this is undo split, only `targetPosition` needs to be changed (if the move is a remove).
        if (!b.graveyardPosition) {
            a.howMany++;
        }
        a.targetPosition = newTargetPosition;
        return [a];
    }
    // Case 2:
    //
    // Split happened between the moved nodes. In this case two ranges to move need to be generated.
    //
    // Characters `ozba` are moved to the end of paragraph `Xyz` but split happened.
    // <p>F[oz|ba]r</p><p>Xyz</p>
    //
    // After split:
    // <p>F[oz</p><p>ba]r</p><p>Xyz</p>
    //
    // Correct ranges:
    // <p>F[oz]</p><p>[ba]r</p><p>Xyz</p>
    //
    // After move:
    // <p>F</p><p>r</p><p>Xyzozba</p>
    //
    if (moveRange.start.hasSameParentAs(b.splitPosition) && moveRange.containsPosition(b.splitPosition)) {
        let rightRange = new range_Range(b.splitPosition, moveRange.end);
        rightRange = rightRange._getTransformedBySplitOperation(b);
        const ranges = [
            new range_Range(moveRange.start, b.splitPosition),
            rightRange
        ];
        return _makeMoveOperationsFromRanges(ranges, newTargetPosition);
    }
    // Case 3:
    //
    // Move operation targets at the split position. We need to decide if the nodes should be inserted
    // at the end of the split element or at the beginning of the new element.
    //
    if (a.targetPosition.isEqual(b.splitPosition) && context.abRelation == 'insertAtSource') {
        newTargetPosition = b.moveTargetPosition;
    }
    // Case 4:
    //
    // Move operation targets just after the split element. We need to decide if the nodes should be inserted
    // between two parts of split element, or after the new element.
    //
    // Split at `|`, while move operation moves `<p>Xyz</p>` and targets at `^`:
    // <p>Foo|bar</p>^<p>baz</p>
    // <p>Foo</p>^<p>bar</p><p>baz</p> or <p>Foo</p><p>bar</p>^<p>baz</p>?
    //
    // If there is no contextual information between operations (for example, they come from collaborative
    // editing), we don't want to put some unrelated content (move) between parts of related content (split parts).
    // However, if the split is from undo, in the past, the moved content might be targeting between the
    // split parts, meaning that was exactly user's intention:
    //
    // <p>Foo</p>^<p>bar</p>		<--- original situation, in "past".
    // <p>Foobar</p>^				<--- after merge target position is transformed.
    // <p>Foo|bar</p>^				<--- then the merge is undone, and split happens, which leads us to current situation.
    //
    // In this case it is pretty clear that the intention was to put new paragraph between those nodes,
    // so we need to transform accordingly. We can detect this scenario thanks to relations.
    //
    if (a.targetPosition.isEqual(b.insertionPosition) && context.abRelation == 'insertBetween') {
        newTargetPosition = a.targetPosition;
    }
    // The default case.
    //
    const transformed = moveRange._getTransformedBySplitOperation(b);
    const ranges = [transformed];
    // Case 5:
    //
    // Moved range contains graveyard element used by split operation. Add extra move operation to the result.
    //
    if (b.graveyardPosition) {
        const movesGraveyardElement = moveRange.start.isEqual(b.graveyardPosition) || moveRange.containsPosition(b.graveyardPosition);
        if (a.howMany > 1 && movesGraveyardElement && !context.aWasUndone) {
            ranges.push(range_Range._createFromPositionAndShift(b.insertionPosition, 1));
        }
    }
    return _makeMoveOperationsFromRanges(ranges, newTargetPosition);
});
setTransformation(MoveOperation, MergeOperation, (a, b, context) => {
    const movedRange = range_Range._createFromPositionAndShift(a.sourcePosition, a.howMany);
    if (b.deletionPosition.hasSameParentAs(a.sourcePosition) && movedRange.containsPosition(b.sourcePosition)) {
        if (a.type == 'remove' && !context.forceWeakRemove) {
            // Case 1:
            //
            // The element to remove got merged.
            //
            // Merge operation does support merging elements which are not siblings. So it would not be a problem
            // from technical point of view. However, if the element was removed, the intention of the user
            // deleting it was to have it all deleted. From user experience point of view, moving back the
            // removed nodes might be unexpected. This means that in this scenario we will reverse merging and remove the element.
            //
            if (!context.aWasUndone) {
                const results = [];
                let gyMoveSource = b.graveyardPosition.clone();
                let splitNodesMoveSource = b.targetPosition._getTransformedByMergeOperation(b);
                if (a.howMany > 1) {
                    results.push(new MoveOperation(a.sourcePosition, a.howMany - 1, a.targetPosition, 0));
                    gyMoveSource = gyMoveSource._getTransformedByMove(a.sourcePosition, a.targetPosition, a.howMany - 1);
                    splitNodesMoveSource = splitNodesMoveSource._getTransformedByMove(a.sourcePosition, a.targetPosition, a.howMany - 1);
                }
                const gyMoveTarget = b.deletionPosition._getCombined(a.sourcePosition, a.targetPosition);
                const gyMove = new MoveOperation(gyMoveSource, 1, gyMoveTarget, 0);
                const splitNodesMoveTargetPath = gyMove.getMovedRangeStart().path.slice();
                splitNodesMoveTargetPath.push(0);
                const splitNodesMoveTarget = new position_Position(gyMove.targetPosition.root, splitNodesMoveTargetPath);
                splitNodesMoveSource = splitNodesMoveSource._getTransformedByMove(gyMoveSource, gyMoveTarget, 1);
                const splitNodesMove = new MoveOperation(splitNodesMoveSource, b.howMany, splitNodesMoveTarget, 0);
                results.push(gyMove);
                results.push(splitNodesMove);
                return results;
            }
        }
        else {
            // Case 2:
            //
            // The element to move got merged and it was the only element to move.
            // In this case just don't do anything, leave the node in the graveyard. Without special case
            // it would be a move operation that moves 0 nodes, so maybe it is better just to return no-op.
            //
            if (a.howMany == 1) {
                if (!context.bWasUndone) {
                    return [new NoOperation(0)];
                }
                else {
                    a.sourcePosition = b.graveyardPosition.clone();
                    a.targetPosition = a.targetPosition._getTransformedByMergeOperation(b);
                    return [a];
                }
            }
        }
    }
    // The default case.
    //
    const moveRange = range_Range._createFromPositionAndShift(a.sourcePosition, a.howMany);
    const transformed = moveRange._getTransformedByMergeOperation(b);
    a.sourcePosition = transformed.start;
    a.howMany = transformed.end.offset - transformed.start.offset;
    a.targetPosition = a.targetPosition._getTransformedByMergeOperation(b);
    return [a];
});
// -----------------------
setTransformation(RenameOperation, InsertOperation, (a, b) => {
    a.position = a.position._getTransformedByInsertOperation(b);
    return [a];
});
setTransformation(RenameOperation, MergeOperation, (a, b) => {
    // Case 1:
    //
    // Element to rename got merged, so it was moved to `b.graveyardPosition`.
    //
    if (a.position.isEqual(b.deletionPosition)) {
        a.position = b.graveyardPosition.clone();
        a.position.stickiness = 'toNext';
        return [a];
    }
    a.position = a.position._getTransformedByMergeOperation(b);
    return [a];
});
setTransformation(RenameOperation, MoveOperation, (a, b) => {
    a.position = a.position._getTransformedByMoveOperation(b);
    return [a];
});
setTransformation(RenameOperation, RenameOperation, (a, b, context) => {
    if (a.position.isEqual(b.position)) {
        if (context.aIsStrong) {
            a.oldName = b.newName;
        }
        else {
            return [new NoOperation(0)];
        }
    }
    return [a];
});
setTransformation(RenameOperation, SplitOperation, (a, b) => {
    // Case 1:
    //
    // The element to rename has been split. In this case, the new element should be also renamed.
    //
    // User decides to change the paragraph to a list item:
    // <paragraph>Foobar</paragraph>
    //
    // However, in meantime, split happens:
    // <paragraph>Foo</paragraph><paragraph>bar</paragraph>
    //
    // As a result, rename both elements:
    // <listItem>Foo</listItem><listItem>bar</listItem>
    //
    const renamePath = a.position.path;
    const splitPath = b.splitPosition.getParentPath();
    if (compareArrays(renamePath, splitPath) == 'same' && !b.graveyardPosition) {
        const extraRename = new RenameOperation(a.position.getShiftedBy(1), a.oldName, a.newName, 0);
        return [a, extraRename];
    }
    // The default case.
    //
    a.position = a.position._getTransformedBySplitOperation(b);
    return [a];
});
// -----------------------
setTransformation(RootAttributeOperation, RootAttributeOperation, (a, b, context) => {
    if (a.root === b.root && a.key === b.key) {
        if (!context.aIsStrong || a.newValue === b.newValue) {
            return [new NoOperation(0)];
        }
        else {
            a.oldValue = b.newValue;
        }
    }
    return [a];
});
// -----------------------
setTransformation(SplitOperation, InsertOperation, (a, b) => {
    // The default case.
    //
    if (a.splitPosition.hasSameParentAs(b.position) && a.splitPosition.offset < b.position.offset) {
        a.howMany += b.howMany;
    }
    a.splitPosition = a.splitPosition._getTransformedByInsertOperation(b);
    a.insertionPosition = a.insertionPosition._getTransformedByInsertOperation(b);
    return [a];
});
setTransformation(SplitOperation, MergeOperation, (a, b, context) => {
    // Case 1:
    //
    // Split element got merged. If two different elements were merged, clients will have different content.
    //
    // Example. Merge at `{}`, split at `[]`:
    // <heading>Foo</heading>{}<paragraph>B[]ar</paragraph>
    //
    // On merge side it will look like this:
    // <heading>FooB[]ar</heading>
    // <heading>FooB</heading><heading>ar</heading>
    //
    // On split side it will look like this:
    // <heading>Foo</heading>{}<paragraph>B</paragraph><paragraph>ar</paragraph>
    // <heading>FooB</heading><paragraph>ar</paragraph>
    //
    // Clearly, the second element is different for both clients.
    //
    // We could use the removed merge element from graveyard as a split element but then clients would have a different
    // model state (in graveyard), because the split side client would still have an element in graveyard (removed by merge).
    //
    // To overcome this, in `SplitOperation` x `MergeOperation` transformation we will add additional `SplitOperation`
    // in the graveyard, which will actually clone the merged-and-deleted element. Then, that cloned element will be
    // used for splitting. Example below.
    //
    // Original state:
    // <heading>Foo</heading>{}<paragraph>B[]ar</paragraph>
    //
    // Merge side client:
    //
    // After merge:
    // <heading>FooB[]ar</heading>                                 graveyard: <paragraph></paragraph>
    //
    // Extra split:
    // <heading>FooB[]ar</heading>                                 graveyard: <paragraph></paragraph><paragraph></paragraph>
    //
    // Use the "cloned" element from graveyard:
    // <heading>FooB</heading><paragraph>ar</paragraph>            graveyard: <paragraph></paragraph>
    //
    // Split side client:
    //
    // After split:
    // <heading>Foo</heading>{}<paragraph>B</paragraph><paragraph>ar</paragraph>
    //
    // After merge:
    // <heading>FooB</heading><paragraph>ar</paragraph>            graveyard: <paragraph></paragraph>
    //
    // This special case scenario only applies if the original split operation clones the split element.
    // If the original split operation has `graveyardPosition` set, it all doesn't have sense because split operation
    // knows exactly which element it should use. So there would be no original problem with different contents.
    //
    // Additionally, the special case applies only if the merge wasn't already undone.
    //
    if (!a.graveyardPosition && !context.bWasUndone && a.splitPosition.hasSameParentAs(b.sourcePosition)) {
        const splitPath = b.graveyardPosition.path.slice();
        splitPath.push(0);
        const splitPosition = new position_Position(b.graveyardPosition.root, splitPath);
        const insertionPosition = SplitOperation.getInsertionPosition(new position_Position(b.graveyardPosition.root, splitPath));
        const additionalSplit = new SplitOperation(splitPosition, 0, insertionPosition, null, 0);
        a.splitPosition = a.splitPosition._getTransformedByMergeOperation(b);
        a.insertionPosition = SplitOperation.getInsertionPosition(a.splitPosition);
        a.graveyardPosition = additionalSplit.insertionPosition.clone();
        a.graveyardPosition.stickiness = 'toNext';
        return [additionalSplit, a];
    }
    // The default case.
    //
    if (a.splitPosition.hasSameParentAs(b.deletionPosition) && !a.splitPosition.isAfter(b.deletionPosition)) {
        a.howMany--;
    }
    if (a.splitPosition.hasSameParentAs(b.targetPosition)) {
        a.howMany += b.howMany;
    }
    a.splitPosition = a.splitPosition._getTransformedByMergeOperation(b);
    a.insertionPosition = SplitOperation.getInsertionPosition(a.splitPosition);
    if (a.graveyardPosition) {
        a.graveyardPosition = a.graveyardPosition._getTransformedByMergeOperation(b);
    }
    return [a];
});
setTransformation(SplitOperation, MoveOperation, (a, b, context) => {
    const rangeToMove = range_Range._createFromPositionAndShift(b.sourcePosition, b.howMany);
    if (a.graveyardPosition) {
        // Case 1:
        //
        // Split operation graveyard node was moved. In this case move operation is stronger. Since graveyard element
        // is already moved to the correct position, we need to only move the nodes after the split position.
        // This will be done by `MoveOperation` instead of `SplitOperation`.
        //
        const gyElementMoved = rangeToMove.start.isEqual(a.graveyardPosition) || rangeToMove.containsPosition(a.graveyardPosition);
        if (!context.bWasUndone && gyElementMoved) {
            const sourcePosition = a.splitPosition._getTransformedByMoveOperation(b);
            const newParentPosition = a.graveyardPosition._getTransformedByMoveOperation(b);
            const newTargetPath = newParentPosition.path.slice();
            newTargetPath.push(0);
            const newTargetPosition = new position_Position(newParentPosition.root, newTargetPath);
            const moveOp = new MoveOperation(sourcePosition, a.howMany, newTargetPosition, 0);
            return [moveOp];
        }
        a.graveyardPosition = a.graveyardPosition._getTransformedByMoveOperation(b);
    }
    // Case 2:
    //
    // Split is at a position where nodes were moved.
    //
    // This is a scenario described in `MoveOperation` x `SplitOperation` transformation but from the
    // "split operation point of view".
    //
    const splitAtTarget = a.splitPosition.isEqual(b.targetPosition);
    if (splitAtTarget && (context.baRelation == 'insertAtSource' || context.abRelation == 'splitBefore')) {
        a.howMany += b.howMany;
        a.splitPosition = a.splitPosition._getTransformedByDeletion(b.sourcePosition, b.howMany);
        a.insertionPosition = SplitOperation.getInsertionPosition(a.splitPosition);
        return [a];
    }
    if (splitAtTarget && context.abRelation && context.abRelation.howMany) {
        const { howMany, offset } = context.abRelation;
        a.howMany += howMany;
        a.splitPosition = a.splitPosition.getShiftedBy(offset);
        return [a];
    }
    // Case 3:
    //
    // If the split position is inside the moved range, we need to shift the split position to a proper place.
    // The position cannot be moved together with moved range because that would result in splitting of an incorrect element.
    //
    // Characters `bc` should be moved to the second paragraph while split position is between them:
    // <paragraph>A[b|c]d</paragraph><paragraph>Xyz</paragraph>
    //
    // After move, new split position is incorrect:
    // <paragraph>Ad</paragraph><paragraph>Xb|cyz</paragraph>
    //
    // Correct split position:
    // <paragraph>A|d</paragraph><paragraph>Xbcyz</paragraph>
    //
    // After split:
    // <paragraph>A</paragraph><paragraph>d</paragraph><paragraph>Xbcyz</paragraph>
    //
    if (a.splitPosition.hasSameParentAs(b.sourcePosition) && rangeToMove.containsPosition(a.splitPosition)) {
        const howManyRemoved = b.howMany - (a.splitPosition.offset - b.sourcePosition.offset);
        a.howMany -= howManyRemoved;
        if (a.splitPosition.hasSameParentAs(b.targetPosition) && a.splitPosition.offset < b.targetPosition.offset) {
            a.howMany += b.howMany;
        }
        a.splitPosition = b.sourcePosition.clone();
        a.insertionPosition = SplitOperation.getInsertionPosition(a.splitPosition);
        return [a];
    }
    // The default case.
    // Don't change `howMany` if move operation does not really move anything.
    //
    if (!b.sourcePosition.isEqual(b.targetPosition)) {
        if (a.splitPosition.hasSameParentAs(b.sourcePosition) && a.splitPosition.offset <= b.sourcePosition.offset) {
            a.howMany -= b.howMany;
        }
        if (a.splitPosition.hasSameParentAs(b.targetPosition) && a.splitPosition.offset < b.targetPosition.offset) {
            a.howMany += b.howMany;
        }
    }
    // Change position stickiness to force a correct transformation.
    a.splitPosition.stickiness = 'toNone';
    a.splitPosition = a.splitPosition._getTransformedByMoveOperation(b);
    a.splitPosition.stickiness = 'toNext';
    if (a.graveyardPosition) {
        a.insertionPosition = a.insertionPosition._getTransformedByMoveOperation(b);
    }
    else {
        a.insertionPosition = SplitOperation.getInsertionPosition(a.splitPosition);
    }
    return [a];
});
setTransformation(SplitOperation, SplitOperation, (a, b, context) => {
    // Case 1:
    //
    // Split at the same position.
    //
    // If there already was a split at the same position as in `a` operation, it means that the intention
    // conveyed by `a` operation has already been fulfilled and `a` should not do anything (to avoid double split).
    //
    // However, there is a difference if these are new splits or splits created by undo. These have different
    // intentions. Also splits moving back different elements from graveyard have different intentions. They
    // are just different operations.
    //
    // So we cancel split operation only if it was really identical.
    //
    // Also, there is additional case, where split operations aren't identical and should not be cancelled, however the
    // default transformation is incorrect too.
    //
    if (a.splitPosition.isEqual(b.splitPosition)) {
        if (!a.graveyardPosition && !b.graveyardPosition) {
            return [new NoOperation(0)];
        }
        if (a.graveyardPosition && b.graveyardPosition && a.graveyardPosition.isEqual(b.graveyardPosition)) {
            return [new NoOperation(0)];
        }
        // Use context to know that the `a.splitPosition` should stay where it is.
        // This happens during undo when first a merge operation moved nodes to `a.splitPosition` and now `b` operation undoes that merge.
        if (context.abRelation == 'splitBefore') {
            // Since split is at the same position, there are no nodes left to split.
            a.howMany = 0;
            // Note: there was `if ( a.graveyardPosition )` here but it was uncovered in tests and I couldn't find any scenarios for now.
            // That would have to be a `SplitOperation` that didn't come from undo but is transformed by operations that were undone.
            // It could happen if `context` is enabled in collaboration.
            a.graveyardPosition = a.graveyardPosition._getTransformedBySplitOperation(b);
            return [a];
        }
    }
    // Case 2:
    //
    // Same node is using to split different elements. This happens in undo when previously same element was merged to
    // two different elements. This is described in `MergeOperation` x `MergeOperation` transformation.
    //
    // In this case we will follow the same logic. We will assume that `insertionPosition` is same for both
    // split operations. This might not always be true but in the real cases that were experienced it was. After all,
    // if these splits are reverses of merge operations that were merging the same element, then the `insertionPosition`
    // should be same for both of those splits.
    //
    // Again, we will decide which operation is stronger by checking if split happens in graveyard or in non-graveyard root.
    //
    if (a.graveyardPosition && b.graveyardPosition && a.graveyardPosition.isEqual(b.graveyardPosition)) {
        const aInGraveyard = a.splitPosition.root.rootName == '$graveyard';
        const bInGraveyard = b.splitPosition.root.rootName == '$graveyard';
        // If `aIsWeak` it means that `a` points to graveyard while `b` doesn't. Don't move nodes then.
        const aIsWeak = aInGraveyard && !bInGraveyard;
        // If `bIsWeak` it means that `b` points to graveyard while `a` doesn't. Force moving nodes then.
        const bIsWeak = bInGraveyard && !aInGraveyard;
        // Force move if `b` is weak or neither operation is weak but `a` is stronger through `context.aIsStrong`.
        const forceMove = bIsWeak || (!aIsWeak && context.aIsStrong);
        if (forceMove) {
            const result = [];
            // First we need to move any nodes split by `b` back to where they were.
            // Do it only if `b` actually moved something.
            if (b.howMany) {
                result.push(new MoveOperation(b.moveTargetPosition, b.howMany, b.splitPosition, 0));
            }
            // Then we need to move nodes from `a` split position to their new element.
            // Do it only if `a` actually should move something.
            if (a.howMany) {
                result.push(new MoveOperation(a.splitPosition, a.howMany, a.moveTargetPosition, 0));
            }
            return result;
        }
        else {
            return [new NoOperation(0)];
        }
    }
    if (a.graveyardPosition) {
        a.graveyardPosition = a.graveyardPosition._getTransformedBySplitOperation(b);
    }
    // Case 3:
    //
    // Position where operation `b` inserted a new node after split is the same as the operation `a` split position.
    // As in similar cases, there is ambiguity if the split should be before the new node (created by `b`) or after.
    //
    if (a.splitPosition.isEqual(b.insertionPosition) && context.abRelation == 'splitBefore') {
        a.howMany++;
        return [a];
    }
    // Case 4:
    //
    // This is a mirror to the case 2. above.
    //
    if (b.splitPosition.isEqual(a.insertionPosition) && context.baRelation == 'splitBefore') {
        const newPositionPath = b.insertionPosition.path.slice();
        newPositionPath.push(0);
        const newPosition = new position_Position(b.insertionPosition.root, newPositionPath);
        const moveOp = new MoveOperation(a.insertionPosition, 1, newPosition, 0);
        return [a, moveOp];
    }
    // The default case.
    //
    if (a.splitPosition.hasSameParentAs(b.splitPosition) && a.splitPosition.offset < b.splitPosition.offset) {
        a.howMany -= b.howMany;
    }
    a.splitPosition = a.splitPosition._getTransformedBySplitOperation(b);
    a.insertionPosition = SplitOperation.getInsertionPosition(a.splitPosition);
    return [a];
});
// Checks whether `MoveOperation` `targetPosition` is inside a node from the moved range of the other `MoveOperation`.
//
// @private
// @param {module:engine/model/operation/moveoperation~MoveOperation} a
// @param {module:engine/model/operation/moveoperation~MoveOperation} b
// @returns {Boolean}
function _moveTargetIntoMovedRange(a, b) {
    return a.targetPosition._getTransformedByDeletion(b.sourcePosition, b.howMany) === null;
}
// Helper function for `MoveOperation` x `MoveOperation` transformation. Converts given ranges and target position to
// move operations and returns them.
//
// Ranges and target position will be transformed on-the-fly when generating operations.
//
// Given `ranges` should be in the order of how they were in the original transformed operation.
//
// Given `targetPosition` is the target position of the first range from `ranges`.
//
// @private
// @param {Array.<module:engine/model/range~Range>} ranges
// @param {module:engine/model/position~Position} targetPosition
// @returns {Array.<module:engine/model/operation/moveoperation~MoveOperation>}
function _makeMoveOperationsFromRanges(ranges, targetPosition) {
    // At this moment we have some ranges and a target position, to which those ranges should be moved.
    // Order in `ranges` array is the go-to order of after transformation.
    //
    // We are almost done. We have `ranges` and `targetPosition` to make operations from.
    // Unfortunately, those operations may affect each other. Precisely, first operation after move
    // may affect source range and target position of second and third operation. Same with second
    // operation affecting third.
    //
    // We need to fix those source ranges and target positions once again, before converting `ranges` to operations.
    const operations = [];
    // Keep in mind that nothing will be transformed if there is just one range in `ranges`.
    for (let i = 0; i < ranges.length; i++) {
        // Create new operation out of a range and target position.
        const range = ranges[i];
        const op = new MoveOperation(range.start, range.end.offset - range.start.offset, targetPosition, 0);
        operations.push(op);
        // Transform other ranges by the generated operation.
        for (let j = i + 1; j < ranges.length; j++) {
            // All ranges in `ranges` array should be:
            //
            // * non-intersecting (these are part of original operation source range), and
            // * `targetPosition` does not target into them (opposite would mean that transformed operation targets "inside itself").
            //
            // This means that the transformation will be "clean" and always return one result.
            ranges[j] = ranges[j]._getTransformedByMove(op.sourcePosition, op.targetPosition, op.howMany)[0];
        }
        targetPosition = targetPosition._getTransformedByMove(op.sourcePosition, op.targetPosition, op.howMany);
    }
    return operations;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/clickobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/clickobserver
 */

/**
 * {@link module:engine/view/document~Document#event:click Click} event observer.
 *
 * Note that this observer is not available by default. To make it available it needs to be added to
 * {@link module:engine/view/view~View view controller}
 * by a {@link module:engine/view/view~View#addObserver} method.
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class ClickObserver extends DomEventObserver {
    constructor(view) {
        super(view);
        this.domEventType = 'click';
    }
    onDomEvent(domEvent) {
        this.fire(domEvent.type, domEvent);
    }
}
/**
 * Fired when one of the editables has been clicked.
 *
 * Introduced by {@link module:engine/view/observer/clickobserver~ClickObserver}.
 *
 * Note that this event is not available by default. To make it available
 * {@link module:engine/view/observer/clickobserver~ClickObserver} needs to be added
 * to {@link module:engine/view/view~View} by a {@link module:engine/view/view~View#addObserver} method.
 *
 * @see module:engine/view/observer/clickobserver~ClickObserver
 * @event module:engine/view/document~Document#event:click
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/observer/mouseobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/view/observer/mouseobserver
 */

/**
 * Mouse events observer.
 *
 * Note that this observer is not available by default. To make it available it needs to be added to
 * {@link module:engine/view/view~View} by {@link module:engine/view/view~View#addObserver} method.
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class MouseObserver extends DomEventObserver {
    constructor(view) {
        super(view);
        this.domEventType = ['mousedown', 'mouseup', 'mouseover', 'mouseout'];
    }
    onDomEvent(domEvent) {
        this.fire(domEvent.type, domEvent);
    }
}
/**
 * Fired when the mouse button is pressed down on one of the editing roots of the editor.
 *
 * Introduced by {@link module:engine/view/observer/mouseobserver~MouseObserver}.
 *
 * Note that this event is not available by default. To make it available, {@link module:engine/view/observer/mouseobserver~MouseObserver}
 * needs to be added to {@link module:engine/view/view~View} by the {@link module:engine/view/view~View#addObserver} method.
 *
 * @see module:engine/view/observer/mouseobserver~MouseObserver
 * @event module:engine/view/document~Document#event:mousedown
 * @param {module:engine/view/observer/domeventdata~DomEventData} data The event data.
 */
/**
 * Fired when the mouse button is released over one of the editing roots of the editor.
 *
 * Introduced by {@link module:engine/view/observer/mouseobserver~MouseObserver}.
 *
 * Note that this event is not available by default. To make it available, {@link module:engine/view/observer/mouseobserver~MouseObserver}
 * needs to be added to {@link module:engine/view/view~View} by the {@link module:engine/view/view~View#addObserver} method.
 *
 * @see module:engine/view/observer/mouseobserver~MouseObserver
 * @event module:engine/view/document~Document#event:mouseup
 * @param {module:engine/view/observer/domeventdata~DomEventData} data The event data.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/upcastwriter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module module:engine/view/upcastwriter
 */







/**
 * View upcast writer. It provides a set of methods used to manipulate non-semantic view trees.
 *
 * It should be used only while working on a non-semantic view
 * (e.g. a view created from HTML string on paste).
 * To manipulate a view which was or is being downcasted from the the model use the
 * {@link module:engine/view/downcastwriter~DowncastWriter downcast writer}.
 *
 * Read more about changing the view in the {@glink framework/guides/architecture/editing-engine#changing-the-view Changing the view}
 * section of the {@glink framework/guides/architecture/editing-engine Editing engine architecture} guide.
 *
 * Unlike `DowncastWriter`, which is available in the {@link module:engine/view/view~View#change `View#change()`} block,
 * `UpcastWriter` can be created wherever you need it:
 *
 *		const writer = new UpcastWriter( viewDocument );
 *		const text = writer.createText( 'foo!' );
 *
 *		writer.appendChild( text, someViewElement );
 */
class UpcastWriter {
    /**
     * @param {module:engine/view/document~Document} document The view document instance in which this upcast writer operates.
     */
    constructor(document) {
        /**
         * The view document instance in which this upcast writer operates.
         *
         * @readonly
         * @type {module:engine/view/document~Document}
         */
        this.document = document;
    }
    /**
     * Creates a new {@link module:engine/view/documentfragment~DocumentFragment} instance.
     *
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into the created document fragment.
     * @returns {module:engine/view/documentfragment~DocumentFragment} The created document fragment.
     */
    createDocumentFragment(children) {
        return new DocumentFragment(this.document, children);
    }
    /**
     * Creates a new {@link module:engine/view/element~Element} instance.
     *
     * Attributes can be passed in various formats:
     *
     *		upcastWriter.createElement( 'div', { class: 'editor', contentEditable: 'true' } ); // object
     *		upcastWriter.createElement( 'div', [ [ 'class', 'editor' ], [ 'contentEditable', 'true' ] ] ); // map-like iterator
     *		upcastWriter.createElement( 'div', mapOfAttributes ); // map
     *
     * @param {String} name Node name.
     * @param {Object|Iterable} [attrs] Collection of attributes.
     * @param {module:engine/view/node~Node|Iterable.<module:engine/view/node~Node>} [children]
     * A list of nodes to be inserted into created element.
     * @returns {module:engine/view/element~Element} Created element.
     */
    createElement(name, attrs, children) {
        return new Element(this.document, name, attrs, children);
    }
    /**
     * Creates a new {@link module:engine/view/text~Text} instance.
     *
     * @param {String} data The text's data.
     * @returns {module:engine/view/text~Text} The created text node.
     */
    createText(data) {
        return new text_Text(this.document, data);
    }
    /**
     * Clones the provided element.
     *
     * @see module:engine/view/element~Element#_clone
     * @param {module:engine/view/element~Element} element Element to be cloned.
     * @param {Boolean} [deep=false] If set to `true` clones element and all its children recursively. When set to `false`,
     * element will be cloned without any children.
     * @returns {module:engine/view/element~Element} Clone of this element.
     */
    clone(element, deep = false) {
        return element._clone(deep);
    }
    /**
     * Appends a child node or a list of child nodes at the end of this node
     * and sets the parent of these nodes to this element.
     *
     * @see module:engine/view/element~Element#_appendChild
     * @param {module:engine/view/item~Item|Iterable.<module:engine/view/item~Item>} items Items to be inserted.
     * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} element Element
     * to which items will be appended.
     * @fires module:engine/view/node~Node#event:change
     * @returns {Number} Number of appended nodes.
     */
    appendChild(items, element) {
        return element._appendChild(items);
    }
    /**
     * Inserts a child node or a list of child nodes on the given index and sets the parent of these nodes to
     * this element.
     *
     * @see module:engine/view/element~Element#_insertChild
     * @param {Number} index Offset at which nodes should be inserted.
     * @param {module:engine/view/item~Item|Iterable.<module:engine/view/item~Item>} items Items to be inserted.
     * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} element Element
     * to which items will be inserted.
     * @fires module:engine/view/node~Node#event:change
     * @returns {Number} Number of inserted nodes.
     */
    insertChild(index, items, element) {
        return element._insertChild(index, items);
    }
    /**
     * Removes the given number of child nodes starting at the given index and set the parent of these nodes to `null`.
     *
     * @see module:engine/view/element~Element#_removeChildren
     * @param {Number} index Offset from which nodes will be removed.
     * @param {Number} howMany Number of nodes to remove.
     * @param {module:engine/view/element~Element|module:engine/view/documentfragment~DocumentFragment} element Element
     * which children will be removed.
     * @fires module:engine/view/node~Node#event:change
     * @returns {Array.<module:engine/view/node~Node>} The array containing removed nodes.
     */
    removeChildren(index, howMany, element) {
        return element._removeChildren(index, howMany);
    }
    /**
     * Removes given element from the view structure. Will not have effect on detached elements.
     *
     * @param {module:engine/view/element~Element} element Element which will be removed.
     * @returns {Array.<module:engine/view/node~Node>} The array containing removed nodes.
     */
    remove(element) {
        const parent = element.parent;
        if (parent) {
            return this.removeChildren(parent.getChildIndex(element), 1, parent);
        }
        return [];
    }
    /**
     * Replaces given element with the new one in the view structure. Will not have effect on detached elements.
     *
     * @param {module:engine/view/element~Element} oldElement Element which will be replaced.
     * @param {module:engine/view/element~Element} newElement Element which will be inserted in the place of the old element.
     * @returns {Boolean} Whether old element was successfully replaced.
     */
    replace(oldElement, newElement) {
        const parent = oldElement.parent;
        if (parent) {
            const index = parent.getChildIndex(oldElement);
            this.removeChildren(index, 1, parent);
            this.insertChild(index, newElement, parent);
            return true;
        }
        return false;
    }
    /**
     * Removes given element from view structure and places its children in its position.
     * It does nothing if element has no parent.
     *
     * @param {module:engine/view/element~Element} element Element to unwrap.
     */
    unwrapElement(element) {
        const parent = element.parent;
        if (parent) {
            const index = parent.getChildIndex(element);
            this.remove(element);
            this.insertChild(index, element.getChildren(), parent);
        }
    }
    /**
     * Renames element by creating a copy of a given element but with its name changed and then moving contents of the
     * old element to the new one.
     *
     * Since this function creates a new element and removes the given one, the new element is returned to keep reference.
     *
     * @param {String} newName New element name.
     * @param {module:engine/view/element~Element} element Element to be renamed.
     * @returns {module:engine/view/element~Element|null} New element or null if the old element
     * was not replaced (happens for detached elements).
     */
    rename(newName, element) {
        const newElement = new Element(this.document, newName, element.getAttributes(), element.getChildren());
        return this.replace(element, newElement) ? newElement : null;
    }
    /**
     * Adds or overwrites element's attribute with a specified key and value.
     *
     *		writer.setAttribute( 'href', 'http://ckeditor.com', linkElement );
     *
     * @see module:engine/view/element~Element#_setAttribute
     * @param {String} key Attribute key.
     * @param {String} value Attribute value.
     * @param {module:engine/view/element~Element} element Element for which attribute will be set.
     */
    setAttribute(key, value, element) {
        element._setAttribute(key, value);
    }
    /**
     * Removes attribute from the element.
     *
     *		writer.removeAttribute( 'href', linkElement );
     *
     * @see module:engine/view/element~Element#_removeAttribute
     * @param {String} key Attribute key.
     * @param {module:engine/view/element~Element} element Element from which attribute will be removed.
     */
    removeAttribute(key, element) {
        element._removeAttribute(key);
    }
    /**
     * Adds specified class to the element.
     *
     *		writer.addClass( 'foo', linkElement );
     *		writer.addClass( [ 'foo', 'bar' ], linkElement );
     *
     * @see module:engine/view/element~Element#_addClass
     * @param {Array.<String>|String} className Single class name or array of class names which will be added.
     * @param {module:engine/view/element~Element} element Element for which class will be added.
     */
    addClass(className, element) {
        element._addClass(className);
    }
    /**
     * Removes specified class from the element.
     *
     *		writer.removeClass( 'foo', linkElement );
     *		writer.removeClass( [ 'foo', 'bar' ], linkElement );
     *
     * @see module:engine/view/element~Element#_removeClass
     * @param {Array.<String>|String} className Single class name or array of class names which will be removed.
     * @param {module:engine/view/element~Element} element Element from which class will be removed.
     */
    removeClass(className, element) {
        element._removeClass(className);
    }
    setStyle(property, valueOrElement, element) {
        if (lodash_es_isPlainObject(property) && element === undefined) {
            valueOrElement._setStyle(property);
        }
        else {
            element._setStyle(property, valueOrElement);
        }
    }
    /**
     * Removes specified style from the element.
     *
     *		writer.removeStyle( 'color', element );  // Removes 'color' style.
     *		writer.removeStyle( [ 'color', 'border-top' ], element ); // Removes both 'color' and 'border-top' styles.
     *
     * **Note**: This method can work with normalized style names if
     * {@link module:engine/controller/datacontroller~DataController#addStyleProcessorRules a particular style processor rule is enabled}.
     * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
     *
     * @see module:engine/view/element~Element#_removeStyle
     * @param {Array.<String>|String} property Style property name or names to be removed.
     * @param {module:engine/view/element~Element} element Element from which style will be removed.
     */
    removeStyle(property, element) {
        element._removeStyle(property);
    }
    /**
     * Sets a custom property on element. Unlike attributes, custom properties are not rendered to the DOM,
     * so they can be used to add special data to elements.
     *
     * @see module:engine/view/element~Element#_setCustomProperty
     * @param {String|Symbol} key Custom property name/key.
     * @param {*} value Custom property value to be stored.
     * @param {module:engine/view/element~Element} element Element for which custom property will be set.
     */
    setCustomProperty(key, value, element) {
        element._setCustomProperty(key, value);
    }
    /**
     * Removes a custom property stored under the given key.
     *
     * @see module:engine/view/element~Element#_removeCustomProperty
     * @param {String|Symbol} key Name/key of the custom property to be removed.
     * @param {module:engine/view/element~Element} element Element from which the custom property will be removed.
     * @returns {Boolean} Returns true if property was removed.
     */
    removeCustomProperty(key, element) {
        return element._removeCustomProperty(key);
    }
    /**
     * Creates position at the given location. The location can be specified as:
     *
     * * a {@link module:engine/view/position~Position position},
     * * parent element and offset (offset defaults to `0`),
     * * parent element and `'end'` (sets position at the end of that element),
     * * {@link module:engine/view/item~Item view item} and `'before'` or `'after'` (sets position before or after given view item).
     *
     * This method is a shortcut to other constructors such as:
     *
     * * {@link #createPositionBefore},
     * * {@link #createPositionAfter},
     *
     * @param {module:engine/view/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/view/item~Item view item}.
     * @returns {module:engine/view/position~Position}
     */
    createPositionAt(itemOrPosition, offset) {
        return Position._createAt(itemOrPosition, offset);
    }
    /**
     * Creates a new position after given view item.
     *
     * @param {module:engine/view/item~Item} item View item after which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    createPositionAfter(item) {
        return Position._createAfter(item);
    }
    /**
     * Creates a new position before given view item.
     *
     * @param {module:engine/view/item~Item} item View item before which the position should be located.
     * @returns {module:engine/view/position~Position}
     */
    createPositionBefore(item) {
        return Position._createBefore(item);
    }
    /**
     * Creates a range spanning from `start` position to `end` position.
     *
     * **Note:** This factory method creates it's own {@link module:engine/view/position~Position} instances basing on passed values.
     *
     * @param {module:engine/view/position~Position} start Start position.
     * @param {module:engine/view/position~Position} [end] End position. If not set, range will be collapsed at `start` position.
     * @returns {module:engine/view/range~Range}
     */
    createRange(start, end) {
        return new Range(start, end);
    }
    /**
     * Creates a range that starts before given {@link module:engine/view/item~Item view item} and ends after it.
     *
     * @param {module:engine/view/item~Item} item
     * @returns {module:engine/view/range~Range}
     */
    createRangeOn(item) {
        return Range._createOn(item);
    }
    /**
     * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
     * that element and ends after the last child of that element.
     *
     * @param {module:engine/view/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/view/range~Range}
     */
    createRangeIn(element) {
        return Range._createIn(element);
    }
    /**
     * Creates a new {@link module:engine/view/selection~Selection} instance.
     *
     * 		// Creates empty selection without ranges.
     *		const selection = writer.createSelection();
     *
     *		// Creates selection at the given range.
     *		const range = writer.createRange( start, end );
     *		const selection = writer.createSelection( range );
     *
     *		// Creates selection at the given ranges
     * 		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		const selection = writer.createSelection( ranges );
     *
     *		// Creates selection from the other selection.
     *		const otherSelection = writer.createSelection();
     *		const selection = writer.createSelection( otherSelection );
     *
     *		// Creates selection from the document selection.
     *		const selection = writer.createSelection( editor.editing.view.document.selection );
     *
     * 		// Creates selection at the given position.
     *		const position = writer.createPositionFromPath( root, path );
     *		const selection = writer.createSelection( position );
     *
     *		// Creates collapsed selection at the position of given item and offset.
     *		const paragraph = writer.createContainerElement( 'paragraph' );
     *		const selection = writer.createSelection( paragraph, offset );
     *
     *		// Creates a range inside an {@link module:engine/view/element~Element element} which starts before the
     *		// first child of that element and ends after the last child of that element.
     *		const selection = writer.createSelection( paragraph, 'in' );
     *
     *		// Creates a range on an {@link module:engine/view/item~Item item} which starts before the item and ends
     *		// just after the item.
     *		const selection = writer.createSelection( paragraph, 'on' );
     *
     * `Selection`'s constructor allow passing additional options (`backward`, `fake` and `label`) as the last argument.
     *
     *		// Creates backward selection.
     *		const selection = writer.createSelection( range, { backward: true } );
     *
     * Fake selection does not render as browser native selection over selected elements and is hidden to the user.
     * This way, no native selection UI artifacts are displayed to the user and selection over elements can be
     * represented in other way, for example by applying proper CSS class.
     *
     * Additionally fake's selection label can be provided. It will be used to describe fake selection in DOM
     * (and be  properly handled by screen readers).
     *
     *		// Creates fake selection with label.
     *		const selection = writer.createSelection( range, { fake: true, label: 'foo' } );
     *
     * @param {module:engine/view/selection~Selectable} [selectable=null]
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Offset or place when selectable is an `Item`.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @param {Boolean} [options.fake] Sets this selection instance to be marked as `fake`.
     * @param {String} [options.label] Label for the fake selection.
     * @returns {module:engine/view/selection~Selection}
     */
    createSelection(...args) {
        return new Selection(...args);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/styles/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
const HEX_COLOR_REGEXP = /^#([0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
const RGB_COLOR_REGEXP = /^rgb\([ ]?([0-9]{1,3}[ %]?,[ ]?){2,3}[0-9]{1,3}[ %]?\)$/i;
const RGBA_COLOR_REGEXP = /^rgba\([ ]?([0-9]{1,3}[ %]?,[ ]?){3}(1|[0-9]+%|[0]?\.?[0-9]+)\)$/i;
const HSL_COLOR_REGEXP = /^hsl\([ ]?([0-9]{1,3}[ %]?[,]?[ ]*){3}(1|[0-9]+%|[0]?\.?[0-9]+)?\)$/i;
const HSLA_COLOR_REGEXP = /^hsla\([ ]?([0-9]{1,3}[ %]?,[ ]?){2,3}(1|[0-9]+%|[0]?\.?[0-9]+)\)$/i;
const COLOR_NAMES = new Set([
    // CSS Level 1
    'black', 'silver', 'gray', 'white', 'maroon', 'red', 'purple', 'fuchsia',
    'green', 'lime', 'olive', 'yellow', 'navy', 'blue', 'teal', 'aqua',
    // CSS Level 2 (Revision 1)
    'orange',
    // CSS Color Module Level 3
    'aliceblue', 'antiquewhite', 'aquamarine', 'azure', 'beige', 'bisque', 'blanchedalmond', 'blueviolet', 'brown',
    'burlywood', 'cadetblue', 'chartreuse', 'chocolate', 'coral', 'cornflowerblue', 'cornsilk', 'crimson', 'cyan',
    'darkblue', 'darkcyan', 'darkgoldenrod', 'darkgray', 'darkgreen', 'darkgrey', 'darkkhaki', 'darkmagenta',
    'darkolivegreen', 'darkorange', 'darkorchid', 'darkred', 'darksalmon', 'darkseagreen', 'darkslateblue',
    'darkslategray', 'darkslategrey', 'darkturquoise', 'darkviolet', 'deeppink', 'deepskyblue', 'dimgray', 'dimgrey',
    'dodgerblue', 'firebrick', 'floralwhite', 'forestgreen', 'gainsboro', 'ghostwhite', 'gold', 'goldenrod',
    'greenyellow', 'grey', 'honeydew', 'hotpink', 'indianred', 'indigo', 'ivory', 'khaki', 'lavender', 'lavenderblush',
    'lawngreen', 'lemonchiffon', 'lightblue', 'lightcoral', 'lightcyan', 'lightgoldenrodyellow', 'lightgray',
    'lightgreen', 'lightgrey', 'lightpink', 'lightsalmon', 'lightseagreen', 'lightskyblue', 'lightslategray',
    'lightslategrey', 'lightsteelblue', 'lightyellow', 'limegreen', 'linen', 'magenta', 'mediumaquamarine',
    'mediumblue', 'mediumorchid', 'mediumpurple', 'mediumseagreen', 'mediumslateblue', 'mediumspringgreen',
    'mediumturquoise', 'mediumvioletred', 'midnightblue', 'mintcream', 'mistyrose', 'moccasin', 'navajowhite',
    'oldlace', 'olivedrab', 'orangered', 'orchid', 'palegoldenrod', 'palegreen', 'paleturquoise', 'palevioletred',
    'papayawhip', 'peachpuff', 'peru', 'pink', 'plum', 'powderblue', 'rosybrown', 'royalblue', 'saddlebrown', 'salmon',
    'sandybrown', 'seagreen', 'seashell', 'sienna', 'skyblue', 'slateblue', 'slategray', 'slategrey', 'snow',
    'springgreen', 'steelblue', 'tan', 'thistle', 'tomato', 'turquoise', 'violet', 'wheat', 'whitesmoke', 'yellowgreen',
    // CSS Color Module Level 3 (System Colors)
    'activeborder', 'activecaption', 'appworkspace', 'background', 'buttonface', 'buttonhighlight', 'buttonshadow',
    'buttontext', 'captiontext', 'graytext', 'highlight', 'highlighttext', 'inactiveborder', 'inactivecaption',
    'inactivecaptiontext', 'infobackground', 'infotext', 'menu', 'menutext', 'scrollbar', 'threeddarkshadow',
    'threedface', 'threedhighlight', 'threedlightshadow', 'threedshadow', 'window', 'windowframe', 'windowtext',
    // CSS Color Module Level 4
    'rebeccapurple',
    // Keywords
    'currentcolor', 'transparent'
]);
/**
 * Checks if string contains [color](https://developer.mozilla.org/en-US/docs/Web/CSS/color) CSS value.
 *
 *		isColor( '#f00' );						// true
 *		isColor( '#AA00BB33' );					// true
 *		isColor( 'rgb(0, 0, 250)' );			// true
 *		isColor( 'hsla(240, 100%, 50%, .7)' );	// true
 *		isColor( 'deepskyblue' );				// true
 *
 * **Note**: It does not support CSS Level 4 whitespace syntax, system colors and radius values for HSL colors.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function isColor(string) {
    // As far as I was able to test checking some pre-conditions is faster than joining each test with ||.
    if (string.startsWith('#')) {
        return HEX_COLOR_REGEXP.test(string);
    }
    if (string.startsWith('rgb')) {
        return RGB_COLOR_REGEXP.test(string) || RGBA_COLOR_REGEXP.test(string);
    }
    if (string.startsWith('hsl')) {
        return HSL_COLOR_REGEXP.test(string) || HSLA_COLOR_REGEXP.test(string);
    }
    // Array check > RegExp test.
    return COLOR_NAMES.has(string.toLowerCase());
}
const lineStyleValues = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset'];
/**
 * Checks if string contains [line style](https://developer.mozilla.org/en-US/docs/Web/CSS/border-style) CSS value.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function isLineStyle(string) {
    return lineStyleValues.includes(string);
}
const lengthRegExp = /^([+-]?[0-9]*([.][0-9]+)?(px|cm|mm|in|pc|pt|ch|em|ex|rem|vh|vw|vmin|vmax)|0)$/;
/**
 * Checks if string contains [length](https://developer.mozilla.org/en-US/docs/Web/CSS/length) CSS value.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function utils_isLength(string) {
    return lengthRegExp.test(string);
}
const PERCENTAGE_VALUE_REGEXP = /^[+-]?[0-9]*([.][0-9]+)?%$/;
/**
 * Checks if string contains [percentage](https://developer.mozilla.org/en-US/docs/Web/CSS/percentage) CSS value.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function isPercentage(string) {
    return PERCENTAGE_VALUE_REGEXP.test(string);
}
const repeatValues = ['repeat-x', 'repeat-y', 'repeat', 'space', 'round', 'no-repeat'];
/**
 * Checks if string contains [background repeat](https://developer.mozilla.org/en-US/docs/Web/CSS/background-repeat) CSS value.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function isRepeat(string) {
    return repeatValues.includes(string);
}
const positionValues = ['center', 'top', 'bottom', 'left', 'right'];
/**
 * Checks if string contains [background position](https://developer.mozilla.org/en-US/docs/Web/CSS/background-position) CSS value.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function isPosition(string) {
    return positionValues.includes(string);
}
const attachmentValues = ['fixed', 'scroll', 'local'];
/**
 * Checks if string contains [background attachment](https://developer.mozilla.org/en-US/docs/Web/CSS/background-attachment) CSS value.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function isAttachment(string) {
    return attachmentValues.includes(string);
}
const urlRegExp = /^url\(/;
/**
 * Checks if string contains [URL](https://developer.mozilla.org/en-US/docs/Web/CSS/url) CSS value.
 *
 * @param {String} string
 * @returns {Boolean}
 */
function isURL(string) {
    return urlRegExp.test(string);
}
/**
 * TODO: Docs
 */
function getBoxSidesValues(value = '') {
    if (value === '') {
        return { top: undefined, right: undefined, bottom: undefined, left: undefined };
    }
    const values = getShorthandValues(value);
    const top = values[0];
    const bottom = values[2] || top;
    const right = values[1] || top;
    const left = values[3] || right;
    return { top, bottom, right, left };
}
/**
 * Default reducer for CSS properties that concerns edges of a box
 * [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) notations:
 *
 *		stylesProcessor.setReducer( 'padding', getBoxSidesValueReducer( 'padding' ) );
 *
 * @param {String} styleShorthand
 * @returns {Function}
 */
function getBoxSidesValueReducer(styleShorthand) {
    return (value) => {
        const { top, right, bottom, left } = value;
        const reduced = [];
        if (![top, right, left, bottom].every(value => !!value)) {
            if (top) {
                reduced.push([styleShorthand + '-top', top]);
            }
            if (right) {
                reduced.push([styleShorthand + '-right', right]);
            }
            if (bottom) {
                reduced.push([styleShorthand + '-bottom', bottom]);
            }
            if (left) {
                reduced.push([styleShorthand + '-left', left]);
            }
        }
        else {
            reduced.push([styleShorthand, getBoxSidesShorthandValue(value)]);
        }
        return reduced;
    };
}
/**
 * Returns a [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) notation
 * of a CSS property value.
 *
 *		getBoxSidesShorthandValue( { top: '1px', right: '1px', bottom: '2px', left: '1px' } );
 *		// will return '1px 1px 2px'
 *
 * @param {module:engine/view/stylesmap~BoxSides} styleShorthand
 * @returns {String}
 */
function getBoxSidesShorthandValue({ top, right, bottom, left }) {
    const out = [];
    if (left !== right) {
        out.push(top, right, bottom, left);
    }
    else if (bottom !== top) {
        out.push(top, right, bottom);
    }
    else if (right !== top) {
        out.push(top, right);
    }
    else {
        out.push(top);
    }
    return out.join(' ');
}
/**
 * Creates a normalizer for a [shorthand](https://developer.mozilla.org/en-US/docs/Web/CSS/Shorthand_properties) 1-to-4 value.
 *
 *		stylesProcessor.setNormalizer( 'margin', getPositionShorthandNormalizer( 'margin' ) );
 *
 * @param {String} shorthand
 * @returns {Function}
 */
function getPositionShorthandNormalizer(shorthand) {
    return (value) => {
        return {
            path: shorthand,
            value: getBoxSidesValues(value)
        };
    };
}
/**
 * Parses parts of a 1-to-4 value notation - handles some CSS values with spaces (like RGB()).
 *
 *		getShorthandValues( 'red blue RGB(0, 0, 0)');
 *		// will return [ 'red', 'blue', 'RGB(0, 0, 0)' ]
 *
 * @param {String} string
 * @returns {Array.<String>}
 */
function getShorthandValues(string) {
    return string
        .replace(/, /g, ',') // Exclude comma from spaces evaluation as values are separated by spaces.
        .split(' ')
        .map(string => string.replace(/,/g, ', ')); // Restore original notation.
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/styles/background.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * Adds a background CSS styles processing rules.
 *
 *		editor.data.addStyleProcessorRules( addBackgroundRules );
 *
 * The normalized value is stored as:
 *
 *		const styles = {
 *			background: {
 *				color,
 *				repeat,
 *				position,
 *				attachment,
 *				image
 *			}
 *		};
 *
 * **Note**: Currently only `'background-color'` longhand value is parsed besides `'background'` shorthand. The reducer also supports only
 * `'background-color'` value.
 *
 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor
 */
function addBackgroundRules(stylesProcessor) {
    stylesProcessor.setNormalizer('background', getBackgroundNormalizer());
    stylesProcessor.setNormalizer('background-color', getBackgroundColorNormalizer());
    stylesProcessor.setReducer('background', getBackgroundReducer());
    stylesProcessor.setStyleRelation('background', ['background-color']);
}
function getBackgroundNormalizer() {
    return value => {
        const background = {};
        const parts = getShorthandValues(value);
        for (const part of parts) {
            if (isRepeat(part)) {
                background.repeat = background.repeat || [];
                background.repeat.push(part);
            }
            else if (isPosition(part)) {
                background.position = background.position || [];
                background.position.push(part);
            }
            else if (isAttachment(part)) {
                background.attachment = part;
            }
            else if (isColor(part)) {
                background.color = part;
            }
            else if (isURL(part)) {
                background.image = part;
            }
        }
        return {
            path: 'background',
            value: background
        };
    };
}
function getBackgroundColorNormalizer() {
    return value => ({ path: 'background.color', value });
}
function getBackgroundReducer() {
    return value => {
        const ret = [];
        ret.push(['background-color', value.color]);
        return ret;
    };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/styles/border.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * Adds a border CSS styles processing rules.
 *
 *		editor.data.addStyleProcessorRules( addBorderRules );
 *
 * This rules merges all [border](https://developer.mozilla.org/en-US/docs/Web/CSS/border) styles notation shorthands:
 *
 * - border
 * - border-top
 * - border-right
 * - border-bottom
 * - border-left
 * - border-color
 * - border-style
 * - border-width
 *
 * and all corresponding longhand forms (like `border-top-color`, `border-top-style`, etc).
 *
 * It does not handle other shorthands (like `border-radius` or `border-image`).
 *
 * The normalized model stores border values as:
 *
 *		const styles = {
 *			border: {
 *				color: { top, right, bottom, left },
 *				style: { top, right, bottom, left },
 *				width: { top, right, bottom, left },
 *			}
 *		};
 *
 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor
 */
function addBorderRules(stylesProcessor) {
    stylesProcessor.setNormalizer('border', getBorderNormalizer());
    // Border-position shorthands.
    stylesProcessor.setNormalizer('border-top', getBorderPositionNormalizer('top'));
    stylesProcessor.setNormalizer('border-right', getBorderPositionNormalizer('right'));
    stylesProcessor.setNormalizer('border-bottom', getBorderPositionNormalizer('bottom'));
    stylesProcessor.setNormalizer('border-left', getBorderPositionNormalizer('left'));
    // Border-property shorthands.
    stylesProcessor.setNormalizer('border-color', getBorderPropertyNormalizer('color'));
    stylesProcessor.setNormalizer('border-width', getBorderPropertyNormalizer('width'));
    stylesProcessor.setNormalizer('border-style', getBorderPropertyNormalizer('style'));
    // Border longhands.
    stylesProcessor.setNormalizer('border-top-color', getBorderPropertyPositionNormalizer('color', 'top'));
    stylesProcessor.setNormalizer('border-top-style', getBorderPropertyPositionNormalizer('style', 'top'));
    stylesProcessor.setNormalizer('border-top-width', getBorderPropertyPositionNormalizer('width', 'top'));
    stylesProcessor.setNormalizer('border-right-color', getBorderPropertyPositionNormalizer('color', 'right'));
    stylesProcessor.setNormalizer('border-right-style', getBorderPropertyPositionNormalizer('style', 'right'));
    stylesProcessor.setNormalizer('border-right-width', getBorderPropertyPositionNormalizer('width', 'right'));
    stylesProcessor.setNormalizer('border-bottom-color', getBorderPropertyPositionNormalizer('color', 'bottom'));
    stylesProcessor.setNormalizer('border-bottom-style', getBorderPropertyPositionNormalizer('style', 'bottom'));
    stylesProcessor.setNormalizer('border-bottom-width', getBorderPropertyPositionNormalizer('width', 'bottom'));
    stylesProcessor.setNormalizer('border-left-color', getBorderPropertyPositionNormalizer('color', 'left'));
    stylesProcessor.setNormalizer('border-left-style', getBorderPropertyPositionNormalizer('style', 'left'));
    stylesProcessor.setNormalizer('border-left-width', getBorderPropertyPositionNormalizer('width', 'left'));
    stylesProcessor.setExtractor('border-top', getBorderPositionExtractor('top'));
    stylesProcessor.setExtractor('border-right', getBorderPositionExtractor('right'));
    stylesProcessor.setExtractor('border-bottom', getBorderPositionExtractor('bottom'));
    stylesProcessor.setExtractor('border-left', getBorderPositionExtractor('left'));
    stylesProcessor.setExtractor('border-top-color', 'border.color.top');
    stylesProcessor.setExtractor('border-right-color', 'border.color.right');
    stylesProcessor.setExtractor('border-bottom-color', 'border.color.bottom');
    stylesProcessor.setExtractor('border-left-color', 'border.color.left');
    stylesProcessor.setExtractor('border-top-width', 'border.width.top');
    stylesProcessor.setExtractor('border-right-width', 'border.width.right');
    stylesProcessor.setExtractor('border-bottom-width', 'border.width.bottom');
    stylesProcessor.setExtractor('border-left-width', 'border.width.left');
    stylesProcessor.setExtractor('border-top-style', 'border.style.top');
    stylesProcessor.setExtractor('border-right-style', 'border.style.right');
    stylesProcessor.setExtractor('border-bottom-style', 'border.style.bottom');
    stylesProcessor.setExtractor('border-left-style', 'border.style.left');
    stylesProcessor.setReducer('border-color', getBoxSidesValueReducer('border-color'));
    stylesProcessor.setReducer('border-style', getBoxSidesValueReducer('border-style'));
    stylesProcessor.setReducer('border-width', getBoxSidesValueReducer('border-width'));
    stylesProcessor.setReducer('border-top', getBorderPositionReducer('top'));
    stylesProcessor.setReducer('border-right', getBorderPositionReducer('right'));
    stylesProcessor.setReducer('border-bottom', getBorderPositionReducer('bottom'));
    stylesProcessor.setReducer('border-left', getBorderPositionReducer('left'));
    stylesProcessor.setReducer('border', getBorderReducer());
    stylesProcessor.setStyleRelation('border', [
        'border-color', 'border-style', 'border-width',
        'border-top', 'border-right', 'border-bottom', 'border-left',
        'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color',
        'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style',
        'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'
    ]);
    stylesProcessor.setStyleRelation('border-color', [
        'border-top-color', 'border-right-color', 'border-bottom-color', 'border-left-color'
    ]);
    stylesProcessor.setStyleRelation('border-style', [
        'border-top-style', 'border-right-style', 'border-bottom-style', 'border-left-style'
    ]);
    stylesProcessor.setStyleRelation('border-width', [
        'border-top-width', 'border-right-width', 'border-bottom-width', 'border-left-width'
    ]);
    stylesProcessor.setStyleRelation('border-top', ['border-top-color', 'border-top-style', 'border-top-width']);
    stylesProcessor.setStyleRelation('border-right', ['border-right-color', 'border-right-style', 'border-right-width']);
    stylesProcessor.setStyleRelation('border-bottom', ['border-bottom-color', 'border-bottom-style', 'border-bottom-width']);
    stylesProcessor.setStyleRelation('border-left', ['border-left-color', 'border-left-style', 'border-left-width']);
}
function getBorderNormalizer() {
    return value => {
        const { color, style, width } = normalizeBorderShorthand(value);
        return {
            path: 'border',
            value: {
                color: getBoxSidesValues(color),
                style: getBoxSidesValues(style),
                width: getBoxSidesValues(width)
            }
        };
    };
}
function getBorderPositionNormalizer(side) {
    return value => {
        const { color, style, width } = normalizeBorderShorthand(value);
        const border = {};
        if (color !== undefined) {
            border.color = { [side]: color };
        }
        if (style !== undefined) {
            border.style = { [side]: style };
        }
        if (width !== undefined) {
            border.width = { [side]: width };
        }
        return {
            path: 'border',
            value: border
        };
    };
}
function getBorderPropertyNormalizer(propertyName) {
    return value => {
        return {
            path: 'border',
            value: toBorderPropertyShorthand(value, propertyName)
        };
    };
}
function toBorderPropertyShorthand(value, property) {
    return {
        [property]: getBoxSidesValues(value)
    };
}
function getBorderPropertyPositionNormalizer(property, side) {
    return value => {
        return {
            path: 'border',
            value: {
                [property]: {
                    [side]: value
                }
            }
        };
    };
}
function getBorderPositionExtractor(which) {
    return (name, styles) => {
        if (styles.border) {
            return extractBorderPosition(styles.border, which);
        }
    };
}
function extractBorderPosition(border, which) {
    const value = {};
    if (border.width && border.width[which]) {
        value.width = border.width[which];
    }
    if (border.style && border.style[which]) {
        value.style = border.style[which];
    }
    if (border.color && border.color[which]) {
        value.color = border.color[which];
    }
    return value;
}
function normalizeBorderShorthand(string) {
    const result = {};
    const parts = getShorthandValues(string);
    for (const part of parts) {
        if (utils_isLength(part) || /thin|medium|thick/.test(part)) {
            result.width = part;
        }
        else if (isLineStyle(part)) {
            result.style = part;
        }
        else {
            result.color = part;
        }
    }
    return result;
}
// The border reducer factory.
//
// It tries to produce the most optimal output for the specified styles.
//
// For a border style:
//
//      style: {top: "solid", bottom: "solid", right: "solid", left: "solid"}
//
// It will produce: `border-style: solid`.
// For a border style and color:
//
//      color: {top: "#ff0", bottom: "#ff0", right: "#ff0", left: "#ff0"}
//      style: {top: "solid", bottom: "solid", right: "solid", left: "solid"}
//
// It will produce: `border-color: #ff0; border-style: solid`.
// If all border parameters are specified:
//
//      color: {top: "#ff0", bottom: "#ff0", right: "#ff0", left: "#ff0"}
//      style: {top: "solid", bottom: "solid", right: "solid", left: "solid"}
//      width: {top: "2px", bottom: "2px", right: "2px", left: "2px"}
//
// It will combine everything into a single property: `border: 2px solid #ff0`.
//
// The definitions are merged only if all border selectors have the same values.
//
// @returns {Function}
function getBorderReducer() {
    return value => {
        const topStyles = extractBorderPosition(value, 'top');
        const rightStyles = extractBorderPosition(value, 'right');
        const bottomStyles = extractBorderPosition(value, 'bottom');
        const leftStyles = extractBorderPosition(value, 'left');
        const borderStyles = [topStyles, rightStyles, bottomStyles, leftStyles];
        const borderStylesByType = {
            width: getReducedStyleValueForType(borderStyles, 'width'),
            style: getReducedStyleValueForType(borderStyles, 'style'),
            color: getReducedStyleValueForType(borderStyles, 'color')
        };
        // Try reducing to a single `border:` property.
        const reducedBorderStyle = reduceBorderPosition(borderStylesByType, 'all');
        if (reducedBorderStyle.length) {
            return reducedBorderStyle;
        }
        // Try reducing to `border-style:`, `border-width:`, `border-color:` properties.
        const reducedStyleTypes = Object.entries(borderStylesByType).reduce((reducedStyleTypes, [type, value]) => {
            if (value) {
                reducedStyleTypes.push([`border-${type}`, value]);
                // Remove it from the full set to not include it in the most specific properties later.
                borderStyles.forEach(style => delete style[type]);
            }
            return reducedStyleTypes;
        }, []);
        // The reduced properties (by type) and all that remains that could not be reduced.
        return [
            ...reducedStyleTypes,
            ...reduceBorderPosition(topStyles, 'top'),
            ...reduceBorderPosition(rightStyles, 'right'),
            ...reduceBorderPosition(bottomStyles, 'bottom'),
            ...reduceBorderPosition(leftStyles, 'left')
        ];
    };
    // @param {Array.<Object>} styles The array of objects with `style`, `color`, `width` properties.
    // @param {'width'|'style'|'color'} type
    function getReducedStyleValueForType(styles, type) {
        return styles
            .map(style => style[type])
            .reduce((result, style) => result == style ? result : null);
    }
}
function getBorderPositionReducer(which) {
    return value => reduceBorderPosition(value, which);
}
// Returns an array with reduced border styles depending on the specified values.
//
// If all border properties (width, style, color) are specified, the returned selector will be
// merged into a group: `border-*: [width] [style] [color]`.
//
// Otherwise, the specific definitions will be returned: `border-(width|style|color)-*: [value]`.
//
// @param {Object|null} value Styles if defined.
// @param {'top'|'right'|'bottom'|'left'|'all'} which The border position.
// @returns {Array}
function reduceBorderPosition(value, which) {
    const borderTypes = [];
    if (value && (value.width)) {
        borderTypes.push('width');
    }
    if (value && (value.style)) {
        borderTypes.push('style');
    }
    if (value && (value.color)) {
        borderTypes.push('color');
    }
    if (borderTypes.length == 3) {
        const borderValue = borderTypes.map(item => value[item]).join(' ');
        return [
            which == 'all' ? ['border', borderValue] : [`border-${which}`, borderValue]
        ];
    }
    // We are unable to reduce to a single `border:` property.
    if (which == 'all') {
        return [];
    }
    return borderTypes.map(type => {
        return [`border-${which}-${type}`, value[type]];
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/styles/margin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * Adds a margin CSS styles processing rules.
 *
 *		editor.data.addStyleProcessorRules( addMarginRules );
 *
 * The normalized value is stored as:
 *
 *		const styles = {
 *			margin: {
 *				top,
 *				right,
 *				bottom,
 *				left
 *			}
 *		};
 *
 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor
 */
function addMarginRules(stylesProcessor) {
    stylesProcessor.setNormalizer('margin', getPositionShorthandNormalizer('margin'));
    stylesProcessor.setNormalizer('margin-top', value => ({ path: 'margin.top', value }));
    stylesProcessor.setNormalizer('margin-right', value => ({ path: 'margin.right', value }));
    stylesProcessor.setNormalizer('margin-bottom', value => ({ path: 'margin.bottom', value }));
    stylesProcessor.setNormalizer('margin-left', value => ({ path: 'margin.left', value }));
    stylesProcessor.setReducer('margin', getBoxSidesValueReducer('margin'));
    stylesProcessor.setStyleRelation('margin', ['margin-top', 'margin-right', 'margin-bottom', 'margin-left']);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/view/styles/padding.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * Adds a margin CSS styles processing rules.
 *
 *		editor.data.addStyleProcessorRules( addPaddingRules );
 *
 * The normalized value is stored as:
 *
 *		const styles = {
 *			padding: {
 *				top,
 *				right,
 *				bottom,
 *				left
 *			}
 *		};
 *
 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor
 */
function addPaddingRules(stylesProcessor) {
    stylesProcessor.setNormalizer('padding', getPositionShorthandNormalizer('padding'));
    stylesProcessor.setNormalizer('padding-top', value => ({ path: 'padding.top', value }));
    stylesProcessor.setNormalizer('padding-right', value => ({ path: 'padding.right', value }));
    stylesProcessor.setNormalizer('padding-bottom', value => ({ path: 'padding.bottom', value }));
    stylesProcessor.setNormalizer('padding-left', value => ({ path: 'padding.left', value }));
    stylesProcessor.setReducer('padding', getBoxSidesValueReducer('padding'));
    stylesProcessor.setStyleRelation('padding', ['padding-top', 'padding-right', 'padding-bottom', 'padding-left']);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine
 */


















































;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/insertcontent.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/insertcontent
 */








/**
 * Inserts content into the editor (specified selection) as one would expect the paste functionality to work.
 *
 * It takes care of removing the selected content, splitting elements (if needed), inserting elements and merging elements appropriately.
 *
 * Some examples:
 *
 * 		<p>x^</p> + <p>y</p> => <p>x</p><p>y</p> => <p>xy[]</p>
 * 		<p>x^y</p> + <p>z</p> => <p>x</p>^<p>y</p> + <p>z</p> => <p>x</p><p>z</p><p>y</p> => <p>xz[]y</p>
 * 		<p>x^y</p> + <img /> => <p>x</p>^<p>y</p> + <img /> => <p>x</p><img /><p>y</p>
 * 		<p>x</p><p>^</p><p>z</p> + <p>y</p> => <p>x</p><p>y[]</p><p>z</p> (no merging)
 * 		<p>x</p>[<img />]<p>z</p> + <p>y</p> => <p>x</p>^<p>z</p> + <p>y</p> => <p>x</p><p>y[]</p><p>z</p>
 *
 * If an instance of {@link module:engine/model/selection~Selection} is passed as `selectable` it will be modified
 * to the insertion selection (equal to a range to be selected after insertion).
 *
 * If `selectable` is not passed, the content will be inserted using the current selection of the model document.
 *
 * **Note:** Use {@link module:engine/model/model~Model#insertContent} instead of this function.
 * This function is only exposed to be reusable in algorithms which change the {@link module:engine/model/model~Model#insertContent}
 * method's behavior.
 *
 * @param {module:engine/model/model~Model} model The model in context of which the insertion
 * should be performed.
 * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
 * @param {module:engine/model/selection~Selectable} [selectable=model.document.selection]
 * Selection into which the content should be inserted.
 * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
 * @returns {module:engine/model/range~Range} Range which contains all the performed changes. This is a range that, if removed,
 * would return the model to the state before the insertion. If no changes were preformed by `insertContent`, returns a range collapsed
 * at the insertion position.
 */
function insertContent(model, content, selectable, placeOrOffset) {
    return model.change(writer => {
        let selection;
        if (!selectable) {
            selection = model.document.selection;
        }
        else if (selectable instanceof selection_Selection || selectable instanceof documentselection_DocumentSelection) {
            selection = selectable;
        }
        else {
            selection = writer.createSelection(selectable, placeOrOffset);
        }
        if (!selection.isCollapsed) {
            model.deleteContent(selection, { doNotAutoparagraph: true });
        }
        const insertion = new Insertion(model, writer, selection.anchor);
        const fakeMarkerElements = [];
        let nodesToInsert;
        if (content.is('documentFragment')) {
            // If document fragment has any markers, these markers should be inserted into the model as well.
            if (content.markers.size) {
                const markersPosition = [];
                for (const [name, range] of content.markers) {
                    const { start, end } = range;
                    const isCollapsed = start.isEqual(end);
                    markersPosition.push({ position: start, name, isCollapsed }, { position: end, name, isCollapsed });
                }
                // Markers position is sorted backwards to ensure that the insertion of fake markers will not change
                // the position of the next markers.
                markersPosition.sort(({ position: posA }, { position: posB }) => posA.isBefore(posB) ? 1 : -1);
                for (const { position, name, isCollapsed } of markersPosition) {
                    let fakeElement = null;
                    let collapsed = null;
                    const isAtBeginning = position.parent === content && position.isAtStart;
                    const isAtEnd = position.parent === content && position.isAtEnd;
                    // We have two ways of handling markers. In general, we want to add temporary <$marker> model elements to
                    // represent marker boundaries. These elements will be inserted into content together with the rest
                    // of the document fragment. After insertion is done, positions for these elements will be read
                    // and proper, actual markers will be created in the model and fake elements will be removed.
                    //
                    // However, if the <$marker> element is at the beginning or at the end of the document fragment,
                    // it may affect how the inserted content is merged with current model, impacting the insertion
                    // result. To avoid that, we don't add <$marker> elements at these positions. Instead, we will use
                    // `Insertion#getAffectedRange()` to figure out new positions for these marker boundaries.
                    if (!isAtBeginning && !isAtEnd) {
                        fakeElement = writer.createElement('$marker');
                        writer.insert(fakeElement, position);
                    }
                    else if (isCollapsed) {
                        // Save whether the collapsed marker was at the beginning or at the end of document fragment
                        // to know where to create it after the insertion is done.
                        collapsed = isAtBeginning ? 'start' : 'end';
                    }
                    fakeMarkerElements.push({
                        name,
                        element: fakeElement,
                        collapsed
                    });
                }
            }
            nodesToInsert = content.getChildren();
        }
        else {
            nodesToInsert = [content];
        }
        insertion.handleNodes(nodesToInsert);
        let newRange = insertion.getSelectionRange();
        if (content.is('documentFragment') && fakeMarkerElements.length) {
            // After insertion was done, the selection was set but the model contains fake <$marker> elements.
            // These <$marker> elements will be now removed. Because of that, we will need to fix the selection.
            // We will create a live range that will automatically be update as <$marker> elements are removed.
            const selectionLiveRange = newRange ? LiveRange.fromRange(newRange) : null;
            // Marker name -> [ start position, end position ].
            const markersData = {};
            // Note: `fakeMarkerElements` are sorted backwards. However, now, we want to handle the markers
            // from the beginning, so that existing <$marker> elements do not affect markers positions.
            // This is why we iterate from the end to the start.
            for (let i = fakeMarkerElements.length - 1; i >= 0; i--) {
                const { name, element, collapsed } = fakeMarkerElements[i];
                const isStartBoundary = !markersData[name];
                if (isStartBoundary) {
                    markersData[name] = [];
                }
                if (element) {
                    // Read fake marker element position to learn where the marker should be created.
                    const elementPosition = writer.createPositionAt(element, 'before');
                    markersData[name].push(elementPosition);
                    writer.remove(element);
                }
                else {
                    // If the fake marker element does not exist, it means that the marker boundary was at the beginning or at the end.
                    const rangeOnInsertion = insertion.getAffectedRange();
                    if (!rangeOnInsertion) {
                        // If affected range is `null` it means that nothing was in the document fragment or all content was filtered out.
                        // Some markers that were in the filtered content may be removed (partially or totally).
                        // Let's handle only those markers that were at the beginning or at the end of the document fragment.
                        if (collapsed) {
                            markersData[name].push(insertion.position);
                        }
                        continue;
                    }
                    if (collapsed) {
                        // If the marker was collapsed at the beginning or at the end of the document fragment,
                        // put both boundaries at the beginning or at the end of inserted range (to keep the marker collapsed).
                        markersData[name].push(rangeOnInsertion[collapsed]);
                    }
                    else {
                        markersData[name].push(isStartBoundary ? rangeOnInsertion.start : rangeOnInsertion.end);
                    }
                }
            }
            for (const [name, [start, end]] of Object.entries(markersData)) {
                // For now, we ignore markers if they are included in the filtered-out content.
                // In the future implementation we will improve that case to create markers that are not filtered out completely.
                if (start && end && start.root === end.root) {
                    writer.addMarker(name, {
                        usingOperation: true,
                        affectsData: true,
                        range: new range_Range(start, end)
                    });
                }
            }
            if (selectionLiveRange) {
                newRange = selectionLiveRange.toRange();
                selectionLiveRange.detach();
            }
        }
        /* istanbul ignore else */
        if (newRange) {
            if (selection instanceof documentselection_DocumentSelection) {
                writer.setSelection(newRange);
            }
            else {
                selection.setTo(newRange);
            }
        }
        else {
            // We are not testing else because it's a safe check for unpredictable edge cases:
            // an insertion without proper range to select.
            //
            // @if CK_DEBUG // console.warn( 'Cannot determine a proper selection range after insertion.' );
        }
        const affectedRange = insertion.getAffectedRange() || model.createRange(selection.anchor);
        insertion.destroy();
        return affectedRange;
    });
}
/**
 * Utility class for performing content insertion.
 *
 * @private
 */
class Insertion {
    constructor(model, writer, position) {
        /**
         * The model in context of which the insertion should be performed.
         *
         * @member {module:engine/model~Model} #model
         */
        this.model = model;
        /**
         * Batch to which operations will be added.
         *
         * @member {module:engine/controller/writer~Batch} #writer
         */
        this.writer = writer;
        /**
         * The position at which (or near which) the next node will be inserted.
         *
         * @member {module:engine/model/position~Position} #position
         */
        this.position = position;
        /**
         * Elements with which the inserted elements can be merged.
         *
         *		<p>x^</p><p>y</p> + <p>z</p> (can merge to <p>x</p>)
         *		<p>x</p><p>^y</p> + <p>z</p> (can merge to <p>y</p>)
         *		<p>x^y</p> + <p>z</p> (can merge to <p>xy</p> which will be split during the action,
         *								so both its pieces will be added to this set)
         *
         *
         * @member {Set} #canMergeWith
         */
        this.canMergeWith = new Set([this.position.parent]);
        /**
         * Schema of the model.
         *
         * @member {module:engine/model/schema~Schema} #schema
         */
        this.schema = model.schema;
        /**
         * The temporary DocumentFragment used for grouping multiple nodes for single insert operation.
         *
         * @private
         * @type {module:engine/model/documentfragment~DocumentFragment}
         */
        this._documentFragment = writer.createDocumentFragment();
        /**
         * The current position in the temporary DocumentFragment.
         *
         * @private
         * @type {module:engine/model/position~Position}
         */
        this._documentFragmentPosition = writer.createPositionAt(this._documentFragment, 0);
        /**
         * The reference to the first inserted node.
         *
         * @private
         * @type {module:engine/model/node~Node}
         */
        this._firstNode = null;
        /**
         * The reference to the last inserted node.
         *
         * @private
         * @type {module:engine/model/node~Node}
         */
        this._lastNode = null;
        /**
         * The reference to the last auto paragraph node.
         *
         * @private
         * @type {module:engine/model/node~Node}
         */
        this._lastAutoParagraph = null;
        /**
         * The array of nodes that should be cleaned of not allowed attributes.
         *
         * @private
         * @type {Array.<module:engine/model/node~Node>}
         */
        this._filterAttributesOf = [];
        /**
         * Beginning of the affected range. See {@link module:engine/model/utils/insertcontent~Insertion#getAffectedRange}.
         *
         * @private
         * @member {module:engine/model/liveposition~LivePosition|null} #_affectedStart
         */
        this._affectedStart = null;
        /**
         * End of the affected range. See {@link module:engine/model/utils/insertcontent~Insertion#getAffectedRange}.
         *
         * @private
         * @member {module:engine/model/liveposition~LivePosition|null} #_affectedEnd
         */
        this._affectedEnd = null;
    }
    /**
     * Handles insertion of a set of nodes.
     *
     * @param {Iterable.<module:engine/model/node~Node>} nodes Nodes to insert.
     */
    handleNodes(nodes) {
        for (const node of Array.from(nodes)) {
            this._handleNode(node);
        }
        // Insert nodes collected in temporary DocumentFragment.
        this._insertPartialFragment();
        // If there was an auto paragraph then we might need to adjust the end of insertion.
        if (this._lastAutoParagraph) {
            this._updateLastNodeFromAutoParagraph(this._lastAutoParagraph);
        }
        // After the content was inserted we may try to merge it with its next sibling if the selection was in it initially.
        // Merging with the previous sibling was performed just after inserting the first node to the document.
        this._mergeOnRight();
        // TMP this will become a post-fixer.
        this.schema.removeDisallowedAttributes(this._filterAttributesOf, this.writer);
        this._filterAttributesOf = [];
    }
    /**
     * Updates the last node after the auto paragraphing.
     *
     * @private
     * @param {module:engine/model/node~Node} node The last auto paragraphing node.
     */
    _updateLastNodeFromAutoParagraph(node) {
        const positionAfterLastNode = this.writer.createPositionAfter(this._lastNode);
        const positionAfterNode = this.writer.createPositionAfter(node);
        // If the real end was after the last auto paragraph then update relevant properties.
        if (positionAfterNode.isAfter(positionAfterLastNode)) {
            this._lastNode = node;
            /* istanbul ignore if */
            if (this.position.parent != node || !this.position.isAtEnd) {
                // Algorithm's correctness check. We should never end up here but it's good to know that we did.
                // At this point the insertion position should be at the end of the last auto paragraph.
                // Note: This error is documented in other place in this file.
                throw new CKEditorError('insertcontent-invalid-insertion-position', this);
            }
            this.position = positionAfterNode;
            this._setAffectedBoundaries(this.position);
        }
    }
    /**
     * Returns range to be selected after insertion.
     * Returns `null` if there is no valid range to select after insertion.
     *
     * @returns {module:engine/model/range~Range|null}
     */
    getSelectionRange() {
        if (this._nodeToSelect) {
            return range_Range._createOn(this._nodeToSelect);
        }
        return this.model.schema.getNearestSelectionRange(this.position);
    }
    /**
     * Returns a range which contains all the performed changes. This is a range that, if removed, would return the model to the state
     * before the insertion. Returns `null` if no changes were done.
     *
     * @returns {module:engine/model/range~Range|null}
     */
    getAffectedRange() {
        if (!this._affectedStart) {
            return null;
        }
        return new range_Range(this._affectedStart, this._affectedEnd);
    }
    /**
     * Destroys `Insertion` instance.
     */
    destroy() {
        if (this._affectedStart) {
            this._affectedStart.detach();
        }
        if (this._affectedEnd) {
            this._affectedEnd.detach();
        }
    }
    /**
     * Handles insertion of a single node.
     *
     * @private
     * @param {module:engine/model/node~Node} node
     */
    _handleNode(node) {
        // Let's handle object in a special way.
        // * They should never be merged with other elements.
        // * If they are not allowed in any of the selection ancestors, they could be either autoparagraphed or totally removed.
        if (this.schema.isObject(node)) {
            this._handleObject(node);
            return;
        }
        // Try to find a place for the given node.
        // Check if a node can be inserted in the given position or it would be accepted if a paragraph would be inserted.
        // Inserts the auto paragraph if it would allow for insertion.
        let isAllowed = this._checkAndAutoParagraphToAllowedPosition(node);
        if (!isAllowed) {
            // Split the position.parent's branch up to a point where the node can be inserted.
            // If it isn't allowed in the whole branch, then of course don't split anything.
            isAllowed = this._checkAndSplitToAllowedPosition(node);
            if (!isAllowed) {
                this._handleDisallowedNode(node);
                return;
            }
        }
        // Add node to the current temporary DocumentFragment.
        this._appendToFragment(node);
        // Store the first and last nodes for easy access for merging with sibling nodes.
        if (!this._firstNode) {
            this._firstNode = node;
        }
        this._lastNode = node;
    }
    /**
     * Inserts the temporary DocumentFragment into the model.
     *
     * @private
     */
    _insertPartialFragment() {
        if (this._documentFragment.isEmpty) {
            return;
        }
        const livePosition = LivePosition.fromPosition(this.position, 'toNext');
        this._setAffectedBoundaries(this.position);
        // If the very first node of the whole insertion process is inserted, insert it separately for OT reasons (undo).
        // Note: there can be multiple calls to `_insertPartialFragment()` during one insertion process.
        // Note: only the very first node can be merged so we have to do separate operation only for it.
        if (this._documentFragment.getChild(0) == this._firstNode) {
            this.writer.insert(this._firstNode, this.position);
            // We must merge the first node just after inserting it to avoid problems with OT.
            // (See: https://github.com/ckeditor/ckeditor5/pull/8773#issuecomment-760945652).
            this._mergeOnLeft();
            this.position = livePosition.toPosition();
        }
        // Insert the remaining nodes from document fragment.
        if (!this._documentFragment.isEmpty) {
            this.writer.insert(this._documentFragment, this.position);
        }
        this._documentFragmentPosition = this.writer.createPositionAt(this._documentFragment, 0);
        this.position = livePosition.toPosition();
        livePosition.detach();
    }
    /**
     * @private
     * @param {module:engine/model/element~Element} node The object element.
     */
    _handleObject(node) {
        // Try finding it a place in the tree.
        if (this._checkAndSplitToAllowedPosition(node)) {
            this._appendToFragment(node);
        }
        // Try autoparagraphing.
        else {
            this._tryAutoparagraphing(node);
        }
    }
    /**
     * @private
     * @param {module:engine/model/node~Node} node The disallowed node which needs to be handled.
     */
    _handleDisallowedNode(node) {
        // If the node is an element, try inserting its children (strip the parent).
        if (node.is('element')) {
            this.handleNodes(node.getChildren());
        }
        // If text is not allowed, try autoparagraphing it.
        else {
            this._tryAutoparagraphing(node);
        }
    }
    /**
     * Append a node to the temporary DocumentFragment.
     *
     * @private
     * @param {module:engine/model/node~Node} node The node to insert.
     */
    _appendToFragment(node) {
        /* istanbul ignore if */
        if (!this.schema.checkChild(this.position, node)) {
            // Algorithm's correctness check. We should never end up here but it's good to know that we did.
            // Note that it would often be a silent issue if we insert node in a place where it's not allowed.
            /**
             * Given node cannot be inserted on the given position.
             *
             * @error insertcontent-wrong-position
             * @param {module:engine/model/node~Node} node Node to insert.
             * @param {module:engine/model/position~Position} position Position to insert the node at.
             */
            throw new CKEditorError('insertcontent-wrong-position', this, { node, position: this.position });
        }
        this.writer.insert(node, this._documentFragmentPosition);
        this._documentFragmentPosition = this._documentFragmentPosition.getShiftedBy(node.offsetSize);
        // The last inserted object should be selected because we can't put a collapsed selection after it.
        if (this.schema.isObject(node) && !this.schema.checkChild(this.position, '$text')) {
            this._nodeToSelect = node;
        }
        else {
            this._nodeToSelect = null;
        }
        this._filterAttributesOf.push(node);
    }
    /**
     * Sets `_affectedStart` and `_affectedEnd` to the given `position`. Should be used before a change is done during insertion process to
     * mark the affected range.
     *
     * This method is used before inserting a node or splitting a parent node. `_affectedStart` and `_affectedEnd` are also changed
     * during merging, but the logic there is more complicated so it is left out of this function.
     *
     * @private
     * @param {module:engine/model/position~Position} position
     */
    _setAffectedBoundaries(position) {
        // Set affected boundaries stickiness so that those position will "expand" when something is inserted in between them:
        // <paragraph>Foo][bar</paragraph> -> <paragraph>Foo]xx[bar</paragraph>
        // This is why it cannot be a range but two separate positions.
        if (!this._affectedStart) {
            this._affectedStart = LivePosition.fromPosition(position, 'toPrevious');
        }
        // If `_affectedEnd` is before the new boundary position, expand `_affectedEnd`. This can happen if first inserted node was
        // inserted into the parent but the next node is moved-out of that parent:
        // (1) <paragraph>Foo][</paragraph> -> <paragraph>Foo]xx[</paragraph>
        // (2) <paragraph>Foo]xx[</paragraph> -> <paragraph>Foo]xx</paragraph><widget></widget>[
        if (!this._affectedEnd || this._affectedEnd.isBefore(position)) {
            if (this._affectedEnd) {
                this._affectedEnd.detach();
            }
            this._affectedEnd = LivePosition.fromPosition(position, 'toNext');
        }
    }
    /**
     * Merges the previous sibling of the first node if it should be merged.
     *
     * After the content was inserted we may try to merge it with its siblings.
     * This should happen only if the selection was in those elements initially.
     *
     * @private
     */
    _mergeOnLeft() {
        const node = this._firstNode;
        if (!(node instanceof element_Element)) {
            return;
        }
        if (!this._canMergeLeft(node)) {
            return;
        }
        const mergePosLeft = LivePosition._createBefore(node);
        mergePosLeft.stickiness = 'toNext';
        const livePosition = LivePosition.fromPosition(this.position, 'toNext');
        // If `_affectedStart` is sames as merge position, it means that the element "marked" by `_affectedStart` is going to be
        // removed and its contents will be moved. This won't transform `LivePosition` so `_affectedStart` needs to be moved
        // by hand to properly reflect affected range. (Due to `_affectedStart` and `_affectedEnd` stickiness, the "range" is
        // shown as `][`).
        //
        // Example - insert `<paragraph>Abc</paragraph><paragraph>Xyz</paragraph>` at the end of `<paragraph>Foo^</paragraph>`:
        //
        // <paragraph>Foo</paragraph><paragraph>Bar</paragraph>   -->
        // <paragraph>Foo</paragraph>]<paragraph>Abc</paragraph><paragraph>Xyz</paragraph>[<paragraph>Bar</paragraph>   -->
        // <paragraph>Foo]Abc</paragraph><paragraph>Xyz</paragraph>[<paragraph>Bar</paragraph>
        //
        // Note, that if we are here then something must have been inserted, so `_affectedStart` and `_affectedEnd` have to be set.
        if (this._affectedStart.isEqual(mergePosLeft)) {
            this._affectedStart.detach();
            this._affectedStart = LivePosition._createAt(mergePosLeft.nodeBefore, 'end', 'toPrevious');
        }
        // We need to update the references to the first and last nodes if they will be merged into the previous sibling node
        // because the reference would point to the removed node.
        //
        // <p>A^A</p> + <p>X</p>
        //
        // <p>A</p>^<p>A</p>
        // <p>A</p><p>X</p><p>A</p>
        // <p>AX</p><p>A</p>
        // <p>AXA</p>
        if (this._firstNode === this._lastNode) {
            this._firstNode = mergePosLeft.nodeBefore;
            this._lastNode = mergePosLeft.nodeBefore;
        }
        this.writer.merge(mergePosLeft);
        // If only one element (the merged one) is in the "affected range", also move the affected range end appropriately.
        //
        // Example - insert `<paragraph>Abc</paragraph>` at the of `<paragraph>Foo^</paragraph>`:
        //
        // <paragraph>Foo</paragraph><paragraph>Bar</paragraph>   -->
        // <paragraph>Foo</paragraph>]<paragraph>Abc</paragraph>[<paragraph>Bar</paragraph>   -->
        // <paragraph>Foo]Abc</paragraph>[<paragraph>Bar</paragraph>   -->
        // <paragraph>Foo]Abc[</paragraph><paragraph>Bar</paragraph>
        if (mergePosLeft.isEqual(this._affectedEnd) && this._firstNode === this._lastNode) {
            this._affectedEnd.detach();
            this._affectedEnd = LivePosition._createAt(mergePosLeft.nodeBefore, 'end', 'toNext');
        }
        this.position = livePosition.toPosition();
        livePosition.detach();
        // After merge elements that were marked by _insert() to be filtered might be gone so
        // we need to mark the new container.
        this._filterAttributesOf.push(this.position.parent);
        mergePosLeft.detach();
    }
    /**
     * Merges the next sibling of the last node if it should be merged.
     *
     * After the content was inserted we may try to merge it with its siblings.
     * This should happen only if the selection was in those elements initially.
     *
     * @private
     */
    _mergeOnRight() {
        const node = this._lastNode;
        if (!(node instanceof element_Element)) {
            return;
        }
        if (!this._canMergeRight(node)) {
            return;
        }
        const mergePosRight = LivePosition._createAfter(node);
        mergePosRight.stickiness = 'toNext';
        /* istanbul ignore if */
        if (!this.position.isEqual(mergePosRight)) {
            // Algorithm's correctness check. We should never end up here but it's good to know that we did.
            // At this point the insertion position should be after the node we'll merge. If it isn't,
            // it should need to be secured as in the left merge case.
            /**
             * An internal error occurred when merging inserted content with its siblings.
             * The insertion position should equal the merge position.
             *
             * If you encountered this error, report it back to the CKEditor 5 team
             * with as many details as possible regarding the content being inserted and the insertion position.
             *
             * @error insertcontent-invalid-insertion-position
             */
            throw new CKEditorError('insertcontent-invalid-insertion-position', this);
        }
        // Move the position to the previous node, so it isn't moved to the graveyard on merge.
        // <p>x</p>[]<p>y</p> => <p>x[]</p><p>y</p>
        this.position = position_Position._createAt(mergePosRight.nodeBefore, 'end');
        // Explanation of setting position stickiness to `'toPrevious'`:
        // OK:  <p>xx[]</p> + <p>yy</p> => <p>xx[]yy</p> (when sticks to previous)
        // NOK: <p>xx[]</p> + <p>yy</p> => <p>xxyy[]</p> (when sticks to next)
        const livePosition = LivePosition.fromPosition(this.position, 'toPrevious');
        // See comment in `_mergeOnLeft()` on moving `_affectedStart`.
        if (this._affectedEnd.isEqual(mergePosRight)) {
            this._affectedEnd.detach();
            this._affectedEnd = LivePosition._createAt(mergePosRight.nodeBefore, 'end', 'toNext');
        }
        // We need to update the references to the first and last nodes if they will be merged into the previous sibling node
        // because the reference would point to the removed node.
        //
        // <p>A^A</p> + <p>X</p>
        //
        // <p>A</p>^<p>A</p>
        // <p>A</p><p>X</p><p>A</p>
        // <p>AX</p><p>A</p>
        // <p>AXA</p>
        if (this._firstNode === this._lastNode) {
            this._firstNode = mergePosRight.nodeBefore;
            this._lastNode = mergePosRight.nodeBefore;
        }
        this.writer.merge(mergePosRight);
        // See comment in `_mergeOnLeft()` on moving `_affectedStart`.
        if (mergePosRight.getShiftedBy(-1).isEqual(this._affectedStart) && this._firstNode === this._lastNode) {
            this._affectedStart.detach();
            this._affectedStart = LivePosition._createAt(mergePosRight.nodeBefore, 0, 'toPrevious');
        }
        this.position = livePosition.toPosition();
        livePosition.detach();
        // After merge elements that were marked by _insert() to be filtered might be gone so
        // we need to mark the new container.
        this._filterAttributesOf.push(this.position.parent);
        mergePosRight.detach();
    }
    /**
     * Checks whether specified node can be merged with previous sibling element.
     *
     * @private
     * @param {module:engine/model/node~Node} node The node which could potentially be merged.
     * @returns {Boolean}
     */
    _canMergeLeft(node) {
        const previousSibling = node.previousSibling;
        return (previousSibling instanceof element_Element) &&
            this.canMergeWith.has(previousSibling) &&
            this.model.schema.checkMerge(previousSibling, node);
    }
    /**
     * Checks whether specified node can be merged with next sibling element.
     *
     * @private
     * @param {module:engine/model/node~Node} node The node which could potentially be merged.
     * @returns {Boolean}
     */
    _canMergeRight(node) {
        const nextSibling = node.nextSibling;
        return (nextSibling instanceof element_Element) &&
            this.canMergeWith.has(nextSibling) &&
            this.model.schema.checkMerge(node, nextSibling);
    }
    /**
     * Tries wrapping the node in a new paragraph and inserting it this way.
     *
     * @private
     * @param {module:engine/model/node~Node} node The node which needs to be autoparagraphed.
     */
    _tryAutoparagraphing(node) {
        const paragraph = this.writer.createElement('paragraph');
        // Do not autoparagraph if the paragraph won't be allowed there,
        // cause that would lead to an infinite loop. The paragraph would be rejected in
        // the next _handleNode() call and we'd be here again.
        if (this._getAllowedIn(this.position.parent, paragraph) && this.schema.checkChild(paragraph, node)) {
            paragraph._appendChild(node);
            this._handleNode(paragraph);
        }
    }
    /**
     * Checks if a node can be inserted in the given position or it would be accepted if a paragraph would be inserted.
     * It also handles inserting the paragraph.
     *
     * @private
     * @param {module:engine/model/node~Node} node The node.
     * @returns {Boolean} Whether an allowed position was found.
     * `false` is returned if the node isn't allowed at the current position or in auto paragraph, `true` if was.
     */
    _checkAndAutoParagraphToAllowedPosition(node) {
        if (this.schema.checkChild(this.position.parent, node)) {
            return true;
        }
        // Do not auto paragraph if the paragraph won't be allowed there,
        // cause that would lead to an infinite loop. The paragraph would be rejected in
        // the next _handleNode() call and we'd be here again.
        if (!this.schema.checkChild(this.position.parent, 'paragraph') || !this.schema.checkChild('paragraph', node)) {
            return false;
        }
        // Insert nodes collected in temporary DocumentFragment if the position parent needs change to process further nodes.
        this._insertPartialFragment();
        // Insert a paragraph and move insertion position to it.
        const paragraph = this.writer.createElement('paragraph');
        this.writer.insert(paragraph, this.position);
        this._setAffectedBoundaries(this.position);
        this._lastAutoParagraph = paragraph;
        this.position = this.writer.createPositionAt(paragraph, 0);
        return true;
    }
    /**
     * @private
     * @param {module:engine/model/node~Node} node
     * @returns {Boolean} Whether an allowed position was found.
     * `false` is returned if the node isn't allowed at any position up in the tree, `true` if was.
     */
    _checkAndSplitToAllowedPosition(node) {
        const allowedIn = this._getAllowedIn(this.position.parent, node);
        if (!allowedIn) {
            return false;
        }
        // Insert nodes collected in temporary DocumentFragment if the position parent needs change to process further nodes.
        if (allowedIn != this.position.parent) {
            this._insertPartialFragment();
        }
        while (allowedIn != this.position.parent) {
            if (this.position.isAtStart) {
                // If insertion position is at the beginning of the parent, move it out instead of splitting.
                // <p>^Foo</p> -> ^<p>Foo</p>
                const parent = this.position.parent;
                this.position = this.writer.createPositionBefore(parent);
                // Special case – parent is empty (<p>^</p>).
                //
                // 1. parent.isEmpty
                // We can remove the element after moving insertion position out of it.
                //
                // 2. parent.parent === allowedIn
                // However parent should remain in place when allowed element is above limit element in document tree.
                // For example there shouldn't be allowed to remove empty paragraph from tableCell, when is pasted
                // content allowed in $root.
                if (parent.isEmpty && parent.parent === allowedIn) {
                    this.writer.remove(parent);
                }
            }
            else if (this.position.isAtEnd) {
                // If insertion position is at the end of the parent, move it out instead of splitting.
                // <p>Foo^</p> -> <p>Foo</p>^
                this.position = this.writer.createPositionAfter(this.position.parent);
            }
            else {
                const tempPos = this.writer.createPositionAfter(this.position.parent);
                this._setAffectedBoundaries(this.position);
                this.writer.split(this.position);
                this.position = tempPos;
                this.canMergeWith.add(this.position.nodeAfter);
            }
        }
        return true;
    }
    /**
     * Gets the element in which the given node is allowed. It checks the passed element and all its ancestors.
     *
     * @private
     * @param {module:engine/model/element~Element} contextElement The element in which context the node should be checked.
     * @param {module:engine/model/node~Node} childNode The node to check.
     * @returns {module:engine/model/element~Element|null}
     */
    _getAllowedIn(contextElement, childNode) {
        if (this.schema.checkChild(contextElement, childNode)) {
            return contextElement;
        }
        // If the child wasn't allowed in the context element and the element is a limit there's no point in
        // checking any further towards the root. This is it: the limit is unsplittable and there's nothing
        // we can do about it. Without this check, the algorithm will analyze parent of the limit and may create
        // an illusion of the child being allowed. There's no way to insert it down there, though. It results in
        // infinite loops.
        if (this.schema.isLimit(contextElement)) {
            return null;
        }
        return this._getAllowedIn(contextElement.parent, childNode);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/findoptimalinsertionrange.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/findoptimalinsertionrange
 */

// Returns a model range which is optimal (in terms of UX) for inserting a widget block.
//
// For instance, if a selection is in the middle of a paragraph, the collapsed range before this paragraph
// will be returned so that it is not split. If the selection is at the end of a paragraph,
// the collapsed range after this paragraph will be returned.
//
// Note: If the selection is placed in an empty block, the range in that block will be returned. If that range
// is then passed to {@link module:engine/model/model~Model#insertContent}, the block will be fully replaced
// by the inserted widget block.
//
// **Note:** Use {@link module:widget/utils#findOptimalInsertionRange} instead of this function outside engine.
// This function is only exposed to be used by {@link module:widget/utils#findOptimalInsertionRange findOptimalInsertionRange()}
// in the `widget` package and inside the `engine` package.
//
// @private
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// The selection based on which the insertion position should be calculated.
// @param {module:engine/model/model~Model} model Model instance.
// @param {'auto'|'before'|'after'} [place='auto'] The place where to look for optimal insertion range.
// The default `auto` value will determine itself the best position for insertion.
// The `before` value will try to find a position before selection.
// The `after` value will try to find a position after selection.
// @returns {module:engine/model/range~Range} The optimal range.
function findOptimalInsertionRange(selection, model, place = 'auto') {
    const selectedElement = selection.getSelectedElement();
    if (selectedElement && model.schema.isObject(selectedElement) && !model.schema.isInline(selectedElement)) {
        if (place == 'before' || place == 'after') {
            return model.createRange(model.createPositionAt(selectedElement, place));
        }
        return model.createRangeOn(selectedElement);
    }
    const firstBlock = first_first(selection.getSelectedBlocks());
    // There are no block elements within ancestors (in the current limit element).
    if (!firstBlock) {
        return model.createRange(selection.focus);
    }
    // If inserting into an empty block – return position in that block. It will get
    // replaced with the image by insertContent(). #42.
    if (firstBlock.isEmpty) {
        return model.createRange(model.createPositionAt(firstBlock, 0));
    }
    const positionAfter = model.createPositionAfter(firstBlock);
    // If selection is at the end of the block - return position after the block.
    if (selection.focus.isTouching(positionAfter)) {
        return model.createRange(positionAfter);
    }
    // Otherwise, return position before the block.
    return model.createRange(model.createPositionBefore(firstBlock));
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/insertobject.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/insertobject
 */





/**
 * Inserts an {@glink framework/guides/deep-dive/schema#object-elements object element} at a specific position in the editor content.
 *
 * **Note:** Use {@link module:engine/model/model~Model#insertObject} instead of this function.
 * This function is only exposed to be reusable in algorithms which change the {@link module:engine/model/model~Model#insertObject}
 * method's behavior.
 *
 * **Note**: For more documentation and examples, see {@link module:engine/model/model~Model#insertObject}.
 *
 * @param {module:engine/model/model~Model} model The model in context of which the insertion
 * should be performed.
 * @param {module:engine/model/element~Element} object An object to be inserted into the model document.
 * @param {module:engine/model/selection~Selectable} [selectable=model.document.selection]
 * A selectable where the content should be inserted. If not specified, the current
 * {@link module:engine/model/document~Document#selection document selection} will be used instead.
 * @param {Number|'before'|'end'|'after'|'on'|'in'} placeOrOffset Specifies the exact place or offset for the insertion to take place,
 * relative to `selectable`.
 * @param {Object} [options] Additional options.
 * @param {'auto'|'before'|'after'} [options.findOptimalPosition] An option that, when set, adjusts the insertion position (relative to
 * `selectable` and `placeOrOffset`) so that the content of `selectable` is not split upon insertion (a.k.a. non-destructive insertion).
 * * When `'auto'`, the algorithm will decide whether to insert the object before or after `selectable` to avoid content splitting.
 * * When `'before'`, the closest position before `selectable` will be used that will not result in content splitting.
 * * When `'after'`, the closest position after `selectable` will be used that will not result in content splitting.
 *
 * Note that this option works only for block objects. Inline objects are inserted into text and do not split blocks.
 * @param {'on'|'after'} [options.setSelection] An option that, when set, moves the
 * {@link module:engine/model/document~Document#selection document selection} after inserting the object.
 * * When `'on'`, the document selection will be set on the inserted object.
 * * When `'after'`, the document selection will move to the closest text node after the inserted object. If there is no
 * such text node, a paragraph will be created and the document selection will be moved inside it.
 * @returns {module:engine/model/range~Range} A range which contains all the performed changes. This is a range that, if removed,
 * would return the model to the state before the insertion. If no changes were preformed by `insertObject()`, returns a range collapsed
 * at the insertion position.
 */
function insertObject(model, object, selectable, placeOrOffset, options = {}) {
    if (!model.schema.isObject(object)) {
        /**
         * Tried to insert an element with {@link module:engine/model/utils/insertobject insertObject()} function
         * that is not defined as an object in schema.
         * See {@link module:engine/model/schema~SchemaItemDefinition#isObject `SchemaItemDefinition`}.
         * If you want to insert content that is not an object you might want to use
         * {@link module:engine/model/utils/insertcontent insertContent()} function.
         * @error insertobject-element-not-an-object
         */
        throw new CKEditorError('insertobject-element-not-an-object', model, { object });
    }
    // Normalize selectable to a selection instance.
    let originalSelection;
    if (!selectable) {
        originalSelection = model.document.selection;
    }
    else if (selectable instanceof selection_Selection || selectable instanceof documentselection_DocumentSelection) {
        originalSelection = selectable;
    }
    else {
        originalSelection = model.createSelection(selectable, placeOrOffset);
    }
    // Adjust the insertion selection.
    let insertionSelection = originalSelection;
    if (options.findOptimalPosition && model.schema.isBlock(object)) {
        insertionSelection = model.createSelection(findOptimalInsertionRange(originalSelection, model, options.findOptimalPosition));
    }
    // Collect attributes to be copied on the inserted object.
    const firstSelectedBlock = first_first(originalSelection.getSelectedBlocks());
    const attributesToCopy = {};
    if (firstSelectedBlock) {
        Object.assign(attributesToCopy, model.schema.getAttributesWithProperty(firstSelectedBlock, 'copyOnReplace', true));
    }
    return model.change(writer => {
        // Remove the selected content to find out what the parent of the inserted object would be.
        // It would be removed inside model.insertContent() anyway.
        if (!insertionSelection.isCollapsed) {
            model.deleteContent(insertionSelection, { doNotAutoparagraph: true });
        }
        let elementToInsert = object;
        const insertionPositionParent = insertionSelection.anchor.parent;
        // Autoparagraphing of an inline objects.
        if (!model.schema.checkChild(insertionPositionParent, object) &&
            model.schema.checkChild(insertionPositionParent, 'paragraph') &&
            model.schema.checkChild('paragraph', object)) {
            elementToInsert = writer.createElement('paragraph');
            writer.insert(object, elementToInsert);
        }
        // Apply attributes that are allowed on the inserted object (or paragraph if autoparagraphed).
        model.schema.setAllowedAttributes(elementToInsert, attributesToCopy, writer);
        // Insert the prepared content at the optionally adjusted selection.
        const affectedRange = model.insertContent(elementToInsert, insertionSelection);
        // Nothing got inserted.
        if (affectedRange.isCollapsed) {
            return affectedRange;
        }
        if (options.setSelection) {
            updateSelection(writer, object, options.setSelection, attributesToCopy);
        }
        return affectedRange;
    });
}
// Updates document selection based on given `place` parameter in relation to `contextElement` element.
//
// @private
// @param {module:engine/model/writer~Writer} writer An instance of the model writer.
// @param {module:engine/model/element~Element} contextElement An element to set the attributes on.
// @param {'on'|'after'} place The place where selection should be set in relation to the `contextElement` element.
// Value `on` will set selection on the passed `contextElement`. Value `after` will set selection after `contextElement`.
// @param {Object} attributes Attributes keys and values to set on a paragraph that this function can create when
// `place` parameter is equal to `after` but there is no element with `$text` node to set selection in.
function updateSelection(writer, contextElement, place, paragraphAttributes) {
    const model = writer.model;
    if (place == 'after') {
        let nextElement = contextElement.nextSibling;
        // Check whether an element next to the inserted element is defined and can contain a text.
        const canSetSelection = nextElement && model.schema.checkChild(nextElement, '$text');
        // If the element is missing, but a paragraph could be inserted next to the element, let's add it.
        if (!canSetSelection && model.schema.checkChild(contextElement.parent, 'paragraph')) {
            nextElement = writer.createElement('paragraph');
            model.schema.setAllowedAttributes(nextElement, paragraphAttributes, writer);
            model.insertContent(nextElement, writer.createPositionAfter(contextElement));
        }
        // Put the selection inside the element, at the beginning.
        if (nextElement) {
            writer.setSelection(nextElement, 0);
        }
    }
    else if (place == 'on') {
        writer.setSelection(contextElement, 'on');
    }
    else {
        /**
         * The unsupported `options.setSelection` parameter was passed
         * to the {@link module:engine/model/utils/insertobject insertObject()} function.
         * Check the {@link module:engine/model/utils/insertobject insertObject()} API documentation for allowed
         * `options.setSelection` parameter values.
         *
         * @error insertobject-invalid-place-parameter-value
         */
        throw new CKEditorError('insertobject-invalid-place-parameter-value', model);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/utils/modifyselection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/utils/modifyselection
 */





const wordBoundaryCharacters = ' ,.?!:;"-()';
/**
 * Modifies the selection. Currently, the supported modifications are:
 *
 * * Extending. The selection focus is moved in the specified `options.direction` with a step specified in `options.unit`.
 * Possible values for `unit` are:
 *  * `'character'` (default) - moves selection by one user-perceived character. In most cases this means moving by one
 *  character in `String` sense. However, unicode also defines "combing marks". These are special symbols, that combines
 *  with a symbol before it ("base character") to create one user-perceived character. For example, `q̣̇` is a normal
 *  letter `q` with two "combining marks": upper dot (`Ux0307`) and lower dot (`Ux0323`). For most actions, i.e. extending
 *  selection by one position, it is correct to include both "base character" and all of it's "combining marks". That is
 *  why `'character'` value is most natural and common method of modifying selection.
 *  * `'codePoint'` - moves selection by one unicode code point. In contrary to, `'character'` unit, this will insert
 *  selection between "base character" and "combining mark", because "combining marks" have their own unicode code points.
 *  However, for technical reasons, unicode code points with values above `UxFFFF` are represented in native `String` by
 *  two characters, called "surrogate pairs". Halves of "surrogate pairs" have a meaning only when placed next to each other.
 *  For example `𨭎` is represented in `String` by `\uD862\uDF4E`. Both `\uD862` and `\uDF4E` do not have any meaning
 *  outside the pair (are rendered as ? when alone). Position between them would be incorrect. In this case, selection
 *  extension will include whole "surrogate pair".
 *  * `'word'` - moves selection by a whole word.
 *
 * **Note:** if you extend a forward selection in a backward direction you will in fact shrink it.
 *
 * **Note:** Use {@link module:engine/model/model~Model#modifySelection} instead of this function.
 * This function is only exposed to be reusable in algorithms
 * which change the {@link module:engine/model/model~Model#modifySelection}
 * method's behavior.
 *
 * @param {module:engine/model/model~Model} model The model in context of which
 * the selection modification should be performed.
 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
 * The selection to modify.
 * @param {Object} [options]
 * @param {'forward'|'backward'} [options.direction='forward'] The direction in which the selection should be modified.
 * @param {'character'|'codePoint'|'word'} [options.unit='character'] The unit by which selection should be modified.
 * @param {Boolean} [options.treatEmojiAsSingleUnit=false] Whether multi-characer emoji sequences should be handled as single unit.
 */
function modifySelection(model, selection, options = {}) {
    const schema = model.schema;
    const isForward = options.direction != 'backward';
    const unit = options.unit ? options.unit : 'character';
    const treatEmojiAsSingleUnit = !!options.treatEmojiAsSingleUnit;
    const focus = selection.focus;
    const walker = new model_treewalker_TreeWalker({
        boundaries: getSearchRange(focus, isForward),
        singleCharacters: true,
        direction: isForward ? 'forward' : 'backward'
    });
    const data = { walker, schema, isForward, unit, treatEmojiAsSingleUnit };
    let next;
    while ((next = walker.next())) {
        if (next.done) {
            return;
        }
        const position = tryExtendingTo(data, next.value);
        if (position) {
            if (selection instanceof documentselection_DocumentSelection) {
                model.change(writer => {
                    writer.setSelectionFocus(position);
                });
            }
            else {
                selection.setFocus(position);
            }
            return;
        }
    }
}
// Checks whether the selection can be extended to the the walker's next value (next position).
// @param {{ walker, unit, isForward, schema, treatEmojiAsSingleUnit }} data
// @param {module:engine/view/treewalker~TreeWalkerValue} value
function tryExtendingTo(data, value) {
    const { isForward, walker, unit, schema, treatEmojiAsSingleUnit } = data;
    const { type, item, nextPosition } = value;
    // If found text, we can certainly put the focus in it. Let's just find a correct position
    // based on the unit.
    if (type == 'text') {
        if (data.unit === 'word') {
            return getCorrectWordBreakPosition(walker, isForward);
        }
        return getCorrectPosition(walker, unit, treatEmojiAsSingleUnit);
    }
    // Entering an element.
    if (type == (isForward ? 'elementStart' : 'elementEnd')) {
        // If it's a selectable, we can select it now.
        if (schema.isSelectable(item)) {
            return position_Position._createAt(item, isForward ? 'after' : 'before');
        }
        // If text allowed on this position, extend to this place.
        if (schema.checkChild(nextPosition, '$text')) {
            return nextPosition;
        }
    }
    // Leaving an element.
    else {
        // If leaving a limit element, stop.
        if (schema.isLimit(item)) {
            // NOTE: Fast-forward the walker until the end.
            walker.skip(() => true);
            return;
        }
        // If text allowed on this position, extend to this place.
        if (schema.checkChild(nextPosition, '$text')) {
            return nextPosition;
        }
    }
}
// Finds a correct position by walking in a text node and checking whether selection can be extended to given position
// or should be extended further.
//
// @param {module:engine/model/treewalker~TreeWalker} walker
// @param {String} unit The unit by which selection should be modified.
// @param {Boolean} treatEmojiAsSingleUnit
function getCorrectPosition(walker, unit, treatEmojiAsSingleUnit) {
    const textNode = walker.position.textNode;
    if (textNode) {
        const data = textNode.data;
        let offset = walker.position.offset - textNode.startOffset;
        while (isInsideSurrogatePair(data, offset) ||
            (unit == 'character' && isInsideCombinedSymbol(data, offset)) ||
            (treatEmojiAsSingleUnit && isInsideEmojiSequence(data, offset))) {
            walker.next();
            offset = walker.position.offset - textNode.startOffset;
        }
    }
    return walker.position;
}
// Finds a correct position of a word break by walking in a text node and checking whether selection can be extended to given position
// or should be extended further.
//
// @param {module:engine/model/treewalker~TreeWalker} walker
// @param {Boolean} isForward Is the direction in which the selection should be modified is forward.
function getCorrectWordBreakPosition(walker, isForward) {
    let textNode = walker.position.textNode;
    if (!textNode) {
        textNode = isForward ? walker.position.nodeAfter : walker.position.nodeBefore;
    }
    while (textNode && textNode.is('$text')) {
        const offset = walker.position.offset - textNode.startOffset;
        // Check of adjacent text nodes with different attributes (like BOLD).
        // Example          : 'foofoo []bar<$text bold="true">bar</$text> bazbaz'
        // should expand to : 'foofoo [bar<$text bold="true">bar</$text>] bazbaz'.
        if (isAtNodeBoundary(textNode, offset, isForward)) {
            textNode = isForward ? walker.position.nodeAfter : walker.position.nodeBefore;
        }
        // Check if this is a word boundary.
        else if (isAtWordBoundary(textNode.data, offset, isForward)) {
            break;
        }
        // Maybe one more character.
        else {
            walker.next();
        }
    }
    return walker.position;
}
function getSearchRange(start, isForward) {
    const root = start.root;
    const searchEnd = position_Position._createAt(root, isForward ? 'end' : 0);
    if (isForward) {
        return new range_Range(start, searchEnd);
    }
    else {
        return new range_Range(searchEnd, start);
    }
}
// Checks if selection is on word boundary.
//
// @param {String} data The text node value to investigate.
// @param {Number} offset Position offset.
// @param {Boolean} isForward Is the direction in which the selection should be modified is forward.
function isAtWordBoundary(data, offset, isForward) {
    // The offset to check depends on direction.
    const offsetToCheck = offset + (isForward ? 0 : -1);
    return wordBoundaryCharacters.includes(data.charAt(offsetToCheck));
}
// Checks if selection is on node boundary.
//
// @param {module:engine/model/text~Text} textNode The text node to investigate.
// @param {Number} offset Position offset.
// @param {Boolean} isForward Is the direction in which the selection should be modified is forward.
function isAtNodeBoundary(textNode, offset, isForward) {
    return offset === (isForward ? textNode.offsetSize : 0);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-engine/src/model/model.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module engine/model/model
 */


















// @if CK_DEBUG_ENGINE // const { dumpTrees } = require( '../dev-utils/utils' );
// @if CK_DEBUG_ENGINE // const { OperationReplayer } = require( '../dev-utils/operationreplayer' ).default;
/**
 * Editor's data model. Read about the model in the
 * {@glink framework/guides/architecture/editing-engine engine architecture guide}.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class Model extends Observable {
    constructor() {
        super();
        /**
         * Model's marker collection.
         *
         * @readonly
         * @member {module:engine/model/markercollection~MarkerCollection}
         */
        this.markers = new MarkerCollection();
        /**
         * Model's document.
         *
         * @readonly
         * @member {module:engine/model/document~Document}
         */
        this.document = new document_Document(this);
        /**
         * Model's schema.
         *
         * @readonly
         * @member {module:engine/model/schema~Schema}
         */
        this.schema = new Schema();
        /**
         * All callbacks added by {@link module:engine/model/model~Model#change} or
         * {@link module:engine/model/model~Model#enqueueChange} methods waiting to be executed.
         *
         * @private
         * @type {Array.<Function>}
         */
        this._pendingChanges = [];
        /**
         * The last created and currently used writer instance.
         *
         * @private
         * @member {module:engine/model/writer~Writer}
         */
        this._currentWriter = null;
        ['insertContent', 'insertObject', 'deleteContent', 'modifySelection', 'getSelectedContent', 'applyOperation']
            .forEach(methodName => this.decorate(methodName));
        // Adding operation validation with `highest` priority, so it is called before any other feature would like
        // to do anything with the operation. If the operation has incorrect parameters it should throw on the earliest occasion.
        this.on('applyOperation', (evt, args) => {
            const operation = args[0];
            operation._validate();
        }, { priority: 'highest' });
        // Register some default abstract entities.
        this.schema.register('$root', {
            isLimit: true
        });
        this.schema.register('$container', {
            allowIn: ['$root', '$container']
        });
        this.schema.register('$block', {
            allowIn: ['$root', '$container'],
            isBlock: true
        });
        this.schema.register('$blockObject', {
            allowWhere: '$block',
            isBlock: true,
            isObject: true
        });
        this.schema.register('$inlineObject', {
            allowWhere: '$text',
            allowAttributesOf: '$text',
            isInline: true,
            isObject: true
        });
        this.schema.register('$text', {
            allowIn: '$block',
            isInline: true,
            isContent: true
        });
        this.schema.register('$clipboardHolder', {
            allowContentOf: '$root',
            allowChildren: '$text',
            isLimit: true
        });
        this.schema.register('$documentFragment', {
            allowContentOf: '$root',
            allowChildren: '$text',
            isLimit: true
        });
        // An element needed by the `upcastElementToMarker` converter.
        // This element temporarily represents a marker boundary during the conversion process and is removed
        // at the end of the conversion. `UpcastDispatcher` or at least `Conversion` class looks like a
        // better place for this registration but both know nothing about `Schema`.
        this.schema.register('$marker');
        this.schema.addChildCheck((context, childDefinition) => {
            if (childDefinition.name === '$marker') {
                return true;
            }
        });
        injectSelectionPostFixer(this);
        // Post-fixer which takes care of adding empty paragraph elements to the empty roots.
        this.document.registerPostFixer(autoParagraphEmptyRoots);
        // @if CK_DEBUG_ENGINE // this.on( 'applyOperation', () => {
        // @if CK_DEBUG_ENGINE // 	dumpTrees( this.document, this.document.version );
        // @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
    }
    /**
     * The `change()` method is the primary way of changing the model. You should use it to modify all document nodes
     * (including detached nodes – i.e. nodes not added to the {@link module:engine/model/model~Model#document model document}),
     * the {@link module:engine/model/document~Document#selection document's selection}, and
     * {@link module:engine/model/model~Model#markers model markers}.
     *
     *		model.change( writer => {
     *			writer.insertText( 'foo', paragraph, 'end' );
     *		} );
     *
     * All changes inside the change block use the same {@link module:engine/model/batch~Batch} so they are combined
     * into a single undo step.
     *
     *		model.change( writer => {
     *			writer.insertText( 'foo', paragraph, 'end' ); // foo.
     *
     *			model.change( writer => {
     *				writer.insertText( 'bar', paragraph, 'end' ); // foobar.
     *			} );
     *
     * 			writer.insertText( 'bom', paragraph, 'end' ); // foobarbom.
     *		} );
     *
     * The callback of the `change()` block is executed synchronously.
     *
     * You can also return a value from the change block.
     *
     *		const img = model.change( writer => {
     *			return writer.createElement( 'img' );
     *		} );
     *
     * @see #enqueueChange
     * @param {Function} callback Callback function which may modify the model.
     * @returns {*} Value returned by the callback.
     */
    change(callback) {
        try {
            if (this._pendingChanges.length === 0) {
                // If this is the outermost block, create a new batch and start `_runPendingChanges` execution flow.
                this._pendingChanges.push({ batch: new Batch(), callback });
                return this._runPendingChanges()[0];
            }
            else {
                // If this is not the outermost block, just execute the callback.
                return callback(this._currentWriter);
            }
        }
        catch (err) {
            // @if CK_DEBUG // throw err;
            /* istanbul ignore next */
            CKEditorError.rethrowUnexpectedError(err, this);
        }
    }
    /**
     * The `enqueueChange()` method performs similar task as the {@link #change `change()` method}, with two major differences.
     *
     * First, the callback of `enqueueChange()` is executed when all other enqueued changes are done. It might be executed
     * immediately if it is not nested in any other change block, but if it is nested in another (enqueue)change block,
     * it will be delayed and executed after the outermost block.
     *
     *		model.change( writer => {
     *			console.log( 1 );
     *
     *			model.enqueueChange( writer => {
     *				console.log( 2 );
     *			} );
     *
     * 			console.log( 3 );
     *		} ); // Will log: 1, 3, 2.
     *
     * In addition to that, the changes enqueued with `enqueueChange()` will be converted separately from the changes
     * done in the outer `change()` block.
     *
     * Second, it lets you define the {@link module:engine/model/batch~Batch} into which you want to add your changes.
     * By default, a new batch with the default {@link module:engine/model/batch~Batch#constructor batch type} is created.
     * In the sample above, the `change` and `enqueueChange` blocks will use a different batch (and a different
     * {@link module:engine/model/writer~Writer} instance since each of them operates on a separate batch).
     *
     *		model.enqueueChange( { isUndoable: false }, writer => {
     *			writer.insertText( 'foo', paragraph, 'end' );
     *		} );
     *
     * When using the `enqueueChange()` block you can also add some changes to the batch you used before.
     *
     *		model.enqueueChange( batch, writer => {
     *			writer.insertText( 'foo', paragraph, 'end' );
     *		} );
     *
     * In order to make a nested `enqueueChange()` create a single undo step together with the changes done in the outer `change()`
     * block, you can obtain the batch instance from the  {@link module:engine/model/writer~Writer#batch writer} of the outer block.
     *
     * @param {module:engine/model/batch~Batch|Object} [batchOrType] A batch or a
     * {@link module:engine/model/batch~Batch#constructor batch type} that should be used in the callback. If not defined, a new batch with
     * the default type will be created.
     * @param {Function} callback Callback function which may modify the model.
     */
    enqueueChange(batchOrType, callback) {
        try {
            if (!batchOrType) {
                batchOrType = new Batch();
            }
            else if (typeof batchOrType === 'function') {
                callback = batchOrType;
                batchOrType = new Batch();
            }
            else if (!(batchOrType instanceof Batch)) {
                batchOrType = new Batch(batchOrType);
            }
            this._pendingChanges.push({ batch: batchOrType, callback });
            if (this._pendingChanges.length == 1) {
                this._runPendingChanges();
            }
        }
        catch (err) {
            // @if CK_DEBUG // throw err;
            /* istanbul ignore next */
            CKEditorError.rethrowUnexpectedError(err, this);
        }
    }
    /**
     * {@link module:utils/observablemixin~ObservableMixin#decorate Decorated} function for applying
     * {@link module:engine/model/operation/operation~Operation operations} to the model.
     *
     * This is a low-level way of changing the model. It is exposed for very specific use cases (like the undo feature).
     * Normally, to modify the model, you will want to use {@link module:engine/model/writer~Writer `Writer`}.
     * See also {@glink framework/guides/architecture/editing-engine#changing-the-model Changing the model} section
     * of the {@glink framework/guides/architecture/editing-engine Editing architecture} guide.
     *
     * @param {module:engine/model/operation/operation~Operation} operation The operation to apply.
     */
    applyOperation(operation) {
        // @if CK_DEBUG_ENGINE // console.log( 'Applying ' + operation );
        // @if CK_DEBUG_ENGINE // if ( !this._operationLogs ) {
        // @if CK_DEBUG_ENGINE //	this._operationLogs = [];
        // @if CK_DEBUG_ENGINE // }
        // @if CK_DEBUG_ENGINE // this._operationLogs.push( JSON.stringify( operation ) );
        // @if CK_DEBUG_ENGINE //if ( !this._appliedOperations ) {
        // @if CK_DEBUG_ENGINE //	this._appliedOperations = [];
        // @if CK_DEBUG_ENGINE //}
        // @if CK_DEBUG_ENGINE //this._appliedOperations.push( operation );
        operation._execute();
    }
    // @if CK_DEBUG_ENGINE // getAppliedOperation() {
    // @if CK_DEBUG_ENGINE //	if ( !this._appliedOperations ) {
    // @if CK_DEBUG_ENGINE //		return '';
    // @if CK_DEBUG_ENGINE //	}
    // @if CK_DEBUG_ENGINE //	return this._appliedOperations.map( JSON.stringify ).join( '-------' );
    // @if CK_DEBUG_ENGINE // }
    // @if CK_DEBUG_ENGINE // createReplayer( stringifiedOperations ) {
    // @if CK_DEBUG_ENGINE //	return new OperationReplayer( this, '-------', stringifiedOperations );
    // @if CK_DEBUG_ENGINE // }
    /**
     * Inserts content at the position in the editor specified by the selection, as one would expect the paste
     * functionality to work.
     *
     * **Note**: If you want to insert an {@glink framework/guides/deep-dive/schema#object-elements object element}
     * (e.g. a {@link module:widget/utils~toWidget widget}), see {@link #insertObject} instead.
     *
     * This is a high-level method. It takes the {@link #schema schema} into consideration when inserting
     * the content, clears the given selection's content before inserting nodes and moves the selection
     * to its target position at the end of the process.
     * It can split elements, merge them, wrap bare text nodes with paragraphs, etc. &mdash; just like the
     * pasting feature should do.
     *
     * For lower-level methods see {@link module:engine/model/writer~Writer `Writer`}.
     *
     * This method, unlike {@link module:engine/model/writer~Writer `Writer`}'s methods, does not have to be used
     * inside a {@link #change `change()` block}.
     *
     * # Conversion and schema
     *
     * Inserting elements and text nodes into the model is not enough to make CKEditor 5 render that content
     * to the user. CKEditor 5 implements a model-view-controller architecture and what `model.insertContent()` does
     * is only adding nodes to the model. Additionally, you need to define
     * {@glink framework/guides/architecture/editing-engine#conversion converters} between the model and view
     * and define those nodes in the {@glink framework/guides/architecture/editing-engine#schema schema}.
     *
     * So, while this method may seem similar to CKEditor 4 `editor.insertHtml()` (in fact, both methods
     * are used for paste-like content insertion), the CKEditor 5 method cannot be use to insert arbitrary HTML
     * unless converters are defined for all elements and attributes in that HTML.
     *
     * # Examples
     *
     * Using `insertContent()` with a manually created model structure:
     *
     *		// Let's create a document fragment containing such content as:
     *		//
     *		// <paragraph>foo</paragraph>
     *		// <blockQuote>
     *		//    <paragraph>bar</paragraph>
     *		// </blockQuote>
     *		const docFrag = editor.model.change( writer => {
     *			const p1 = writer.createElement( 'paragraph' );
     *			const p2 = writer.createElement( 'paragraph' );
     *			const blockQuote = writer.createElement( 'blockQuote' );
     *			const docFrag = writer.createDocumentFragment();
     *
     *			writer.append( p1, docFrag );
     *			writer.append( blockQuote, docFrag );
     *			writer.append( p2, blockQuote );
     *			writer.insertText( 'foo', p1 );
     *			writer.insertText( 'bar', p2 );
     *
     *			return docFrag;
     *		} );
     *
     *		// insertContent() does not have to be used in a change() block. It can, though,
     *		// so this code could be moved to the callback defined above.
     *		editor.model.insertContent( docFrag );
     *
     * Using `insertContent()` with an HTML string converted to a model document fragment (similar to the pasting mechanism):
     *
     *		// You can create your own HtmlDataProcessor instance or use editor.data.processor
     *		// if you have not overridden the default one (which is the HtmlDataProcessor instance).
     *		const htmlDP = new HtmlDataProcessor( viewDocument );
     *
     *		// Convert an HTML string to a view document fragment:
     *		const viewFragment = htmlDP.toView( htmlString );
     *
     *		// Convert the view document fragment to a model document fragment
     *		// in the context of $root. This conversion takes the schema into
     *		// account so if, for example, the view document fragment contained a bare text node,
     *		// this text node cannot be a child of $root, so it will be automatically
     *		// wrapped with a <paragraph>. You can define the context yourself (in the second parameter),
     *		// and e.g. convert the content like it would happen in a <paragraph>.
     *		// Note: The clipboard feature uses a custom context called $clipboardHolder
     *		// which has a loosened schema.
     *		const modelFragment = editor.data.toModel( viewFragment );
     *
     *		editor.model.insertContent( modelFragment );
     *
     * By default this method will use the document selection but it can also be used with a position, range or selection instance.
     *
     *		// Insert text at the current document selection position.
     *		editor.model.change( writer => {
     *			editor.model.insertContent( writer.createText( 'x' ) );
     *		} );
     *
     *		// Insert text at a given position - the document selection will not be modified.
     *		editor.model.change( writer => {
     *			editor.model.insertContent( writer.createText( 'x' ), doc.getRoot(), 2 );
     *
     *			// Which is a shorthand for:
     *			editor.model.insertContent( writer.createText( 'x' ), writer.createPositionAt( doc.getRoot(), 2 ) );
     *		} );
     *
     * If you want the document selection to be moved to the inserted content, use the
     * {@link module:engine/model/writer~Writer#setSelection `setSelection()`} method of the writer after inserting
     * the content:
     *
     *		editor.model.change( writer => {
     *			const paragraph = writer.createElement( 'paragraph' );
     *
     *			// Insert an empty paragraph at the beginning of the root.
     *			editor.model.insertContent( paragraph, writer.createPositionAt( editor.model.document.getRoot(), 0 ) );
     *
     *			// Move the document selection to the inserted paragraph.
     *			writer.setSelection( paragraph, 'in' );
     *		} );
     *
     * If an instance of the {@link module:engine/model/selection~Selection model selection} is passed as `selectable`,
     * the new content will be inserted at the passed selection (instead of document selection):
     *
     *		editor.model.change( writer => {
     *			// Create a selection in a paragraph that will be used as a place of insertion.
     *			const selection = writer.createSelection( paragraph, 'in' );
     *
     *			// Insert the new text at the created selection.
     *			editor.model.insertContent( writer.createText( 'x' ), selection );
     *
     *			// insertContent() modifies the passed selection instance so it can be used to set the document selection.
     *			// Note: This is not necessary when you passed the document selection to insertContent().
     *			writer.setSelection( selection );
     *		} );
     *
     * @fires insertContent
     * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
     * @param {module:engine/model/selection~Selectable} [selectable=model.document.selection]
     * The selection into which the content should be inserted. If not provided the current model document selection will be used.
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] To be used when a model item was passed as `selectable`.
     * This param defines a position in relation to that item.
     * @returns {module:engine/model/range~Range} Range which contains all the performed changes. This is a range that, if removed,
     * would return the model to the state before the insertion. If no changes were preformed by `insertContent`, returns a range collapsed
     * at the insertion position.
     */
    insertContent(content, selectable, placeOrOffset) {
        return insertContent(this, content, selectable, placeOrOffset);
    }
    /**
     * Inserts an {@glink framework/guides/deep-dive/schema#object-elements object element} at a specific position in the editor content.
     *
     * This is a high-level API:
     * * It takes the {@link #schema schema} into consideration,
     * * It clears the content of passed `selectable` before inserting,
     * * It can move the selection at the end of the process,
     * * It will copy the selected block's attributes to preserve them upon insertion,
     * * It can split elements or wrap inline objects with paragraphs if they are not allowed in target position,
     * * etc.
     *
     * # Notes
     *
     * * If you want to insert a non-object content, see {@link #insertContent} instead.
     * * For lower-level API, see {@link module:engine/model/writer~Writer `Writer`}.
     * * Unlike {@link module:engine/model/writer~Writer `Writer`}, this method does not have to be used inside
     * a {@link #change `change()` block}.
     * * Inserting object into the model is not enough to make CKEditor 5 render that content to the user.
     * CKEditor 5 implements a model-view-controller architecture and what `model.insertObject()` does
     * is only adding nodes to the model. Additionally, you need to define
     * {@glink framework/guides/architecture/editing-engine#conversion converters} between the model and view
     * and define those nodes in the {@glink framework/guides/architecture/editing-engine#schema schema}.
     *
     * # Examples
     *
     * Use the following code to insert an object at the current selection and keep the selection on the inserted element:
     *
     *		const rawHtmlEmbedElement = writer.createElement( 'rawHtml' );
     *
     *		model.insertObject( rawHtmlEmbedElement, null, null, {
     *			setSelection: 'on'
     *		} );
     *
     * Use the following code to insert an object at the current selection and nudge the selection after the inserted object:
     *
     *		const pageBreakElement = writer.createElement( 'pageBreak' );
     *
     *		model.insertObject( pageBreakElement, null, null, {
     *			setSelection: 'after'
     *		} );
     *
     * Use the following code to insert an object at the current selection and avoid splitting the content (non-destructive insertion):
     *
     *		const tableElement = writer.createElement( 'table' );
     *
     *		model.insertObject( tableElement, null, null, {
     *			findOptimalPosition: 'auto'
     *		} );
     *
     * Use the following code to insert an object at the specific range (also: replace the content of the range):
     *
     *		const tableElement = writer.createElement( 'table' );
     *		const range = model.createRangeOn( model.document.getRoot().getChild( 1 ) );
     *
     *		model.insertObject( tableElement, range );
     *
     * @param {module:engine/model/element~Element} object An object to be inserted into the model document.
     * @param {module:engine/model/selection~Selectable} [selectable=model.document.selection]
     * A selectable where the content should be inserted. If not specified, the current
     * {@link module:engine/model/document~Document#selection document selection} will be used instead.
     * @param {Number|'before'|'end'|'after'|'on'|'in'} placeOrOffset Specifies the exact place or offset for the insertion to take place,
     * relative to `selectable`.
     * @param {Object} [options] Additional options.
     * @param {'auto'|'before'|'after'} [options.findOptimalPosition] An option that, when set, adjusts the insertion position (relative to
     * `selectable` and `placeOrOffset`) so that the content of `selectable` is not split upon insertion (a.k.a. non-destructive insertion).
     * * When `'auto'`, the algorithm will decide whether to insert the object before or after `selectable` to avoid content splitting.
     * * When `'before'`, the closest position before `selectable` will be used that will not result in content splitting.
     * * When `'after'`, the closest position after `selectable` will be used that will not result in content splitting.
     *
     * Note that this option only works for block objects. Inline objects are inserted into text and do not split blocks.
     * @param {'on'|'after'} [options.setSelection] An option that, when set, moves the
     * {@link module:engine/model/document~Document#selection document selection} after inserting the object.
     * * When `'on'`, the document selection will be set on the inserted object.
     * * When `'after'`, the document selection will move to the closest text node after the inserted object. If there is no
     * such text node, a paragraph will be created and the document selection will be moved inside it.
     * @returns {module:engine/model/range~Range} A range which contains all the performed changes. This is a range that, if removed,
     * would return the model to the state before the insertion. If no changes were preformed by `insertObject()`, returns a range collapsed
     * at the insertion position.
     */
    insertObject(object, selectable, placeOrOffset, options) {
        return insertObject(this, object, selectable, placeOrOffset, options);
    }
    /**
     * Deletes content of the selection and merge siblings. The resulting selection is always collapsed.
     *
     * **Note:** For the sake of predictability, the resulting selection should always be collapsed.
     * In cases where a feature wants to modify deleting behavior so selection isn't collapsed
     * (e.g. a table feature may want to keep row selection after pressing <kbd>Backspace</kbd>),
     * then that behavior should be implemented in the view's listener. At the same time, the table feature
     * will need to modify this method's behavior too, e.g. to "delete contents and then collapse
     * the selection inside the last selected cell" or "delete the row and collapse selection somewhere near".
     * That needs to be done in order to ensure that other features which use `deleteContent()` will work well with tables.
     *
     * @fires deleteContent
     * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
     * Selection of which the content should be deleted.
     * @param {Object} [options]
     * @param {Boolean} [options.leaveUnmerged=false] Whether to merge elements after removing the content of the selection.
     *
     * For example `<heading1>x[x</heading1><paragraph>y]y</paragraph>` will become:
     *
     * * `<heading1>x^y</heading1>` with the option disabled (`leaveUnmerged == false`)
     * * `<heading1>x^</heading1><paragraph>y</paragraph>` with enabled (`leaveUnmerged == true`).
     *
     * Note: {@link module:engine/model/schema~Schema#isObject object} and {@link module:engine/model/schema~Schema#isLimit limit}
     * elements will not be merged.
     *
     * @param {Boolean} [options.doNotResetEntireContent=false] Whether to skip replacing the entire content with a
     * paragraph when the entire content was selected.
     *
     * For example `<heading1>[x</heading1><paragraph>y]</paragraph>` will become:
     *
     * * `<paragraph>^</paragraph>` with the option disabled (`doNotResetEntireContent == false`)
     * * `<heading1>^</heading1>` with enabled (`doNotResetEntireContent == true`)
     *
     * @param {Boolean} [options.doNotAutoparagraph=false] Whether to create a paragraph if after content deletion selection is moved
     * to a place where text cannot be inserted.
     *
     * For example `<paragraph>x</paragraph>[<imageBlock src="foo.jpg"></imageBlock>]` will become:
     *
     * * `<paragraph>x</paragraph><paragraph>[]</paragraph>` with the option disabled (`doNotAutoparagraph == false`)
     * * `<paragraph>x[]</paragraph>` with the option enabled (`doNotAutoparagraph == true`).
     *
     * **Note:** if there is no valid position for the selection, the paragraph will always be created:
     *
     * `[<imageBlock src="foo.jpg"></imageBlock>]` -> `<paragraph>[]</paragraph>`.
     *
     * @param {'forward'|'backward'} [options.direction='backward'] The direction in which the content is being consumed.
     * Deleting backward corresponds to using the <kbd>Backspace</kbd> key, while deleting content forward corresponds to
     * the <kbd>Shift</kbd>+<kbd>Backspace</kbd> keystroke.
     */
    deleteContent(selection, options) {
        deleteContent(this, selection, options);
    }
    /**
     * Modifies the selection. Currently, the supported modifications are:
     *
     * * Extending. The selection focus is moved in the specified `options.direction` with a step specified in `options.unit`.
     * Possible values for `unit` are:
     *  * `'character'` (default) - moves selection by one user-perceived character. In most cases this means moving by one
     *  character in `String` sense. However, unicode also defines "combing marks". These are special symbols, that combines
     *  with a symbol before it ("base character") to create one user-perceived character. For example, `q̣̇` is a normal
     *  letter `q` with two "combining marks": upper dot (`Ux0307`) and lower dot (`Ux0323`). For most actions, i.e. extending
     *  selection by one position, it is correct to include both "base character" and all of it's "combining marks". That is
     *  why `'character'` value is most natural and common method of modifying selection.
     *  * `'codePoint'` - moves selection by one unicode code point. In contrary to, `'character'` unit, this will insert
     *  selection between "base character" and "combining mark", because "combining marks" have their own unicode code points.
     *  However, for technical reasons, unicode code points with values above `UxFFFF` are represented in native `String` by
     *  two characters, called "surrogate pairs". Halves of "surrogate pairs" have a meaning only when placed next to each other.
     *  For example `𨭎` is represented in `String` by `\uD862\uDF4E`. Both `\uD862` and `\uDF4E` do not have any meaning
     *  outside the pair (are rendered as ? when alone). Position between them would be incorrect. In this case, selection
     *  extension will include whole "surrogate pair".
     *  * `'word'` - moves selection by a whole word.
     *
     * **Note:** if you extend a forward selection in a backward direction you will in fact shrink it.
     *
     * @fires modifySelection
     * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
     * The selection to modify.
     * @param {Object} [options]
     * @param {'forward'|'backward'} [options.direction='forward'] The direction in which the selection should be modified.
     * @param {'character'|'codePoint'|'word'} [options.unit='character'] The unit by which selection should be modified.
     * @param {Boolean} [options.treatEmojiAsSingleUnit=false] Whether multi-characer emoji sequences should be handled as single unit.
     */
    modifySelection(selection, options) {
        modifySelection(this, selection, options);
    }
    /**
     * Gets a clone of the selected content.
     *
     * For example, for the following selection:
     *
     * ```html
     * <paragraph>x</paragraph>
     * <blockQuote>
     *	<paragraph>y</paragraph>
     *	<heading1>fir[st</heading1>
     * </blockQuote>
     * <paragraph>se]cond</paragraph>
     * <paragraph>z</paragraph>
     * ```
     *
     * It will return a document fragment with such a content:
     *
     * ```html
     * <blockQuote>
     *	<heading1>st</heading1>
     * </blockQuote>
     * <paragraph>se</paragraph>
     * ```
     *
     * @fires getSelectedContent
     * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
     * The selection of which content will be returned.
     * @returns {module:engine/model/documentfragment~DocumentFragment}
     */
    getSelectedContent(selection) {
        return getSelectedContent(this, selection);
    }
    /**
     * Checks whether the given {@link module:engine/model/range~Range range} or
     * {@link module:engine/model/element~Element element} has any meaningful content.
     *
     * Meaningful content is:
     *
     * * any text node (`options.ignoreWhitespaces` allows controlling whether this text node must also contain
     * any non-whitespace characters),
     * * or any {@link module:engine/model/schema~Schema#isContent content element},
     * * or any {@link module:engine/model/markercollection~Marker marker} which
     * {@link module:engine/model/markercollection~Marker#_affectsData affects data}.
     *
     * This means that a range containing an empty `<paragraph></paragraph>` is not considered to have a meaningful content.
     * However, a range containing an `<imageBlock></imageBlock>` (which would normally be marked in the schema as an object element)
     * is considered non-empty.
     *
     * @param {module:engine/model/range~Range|module:engine/model/element~Element} rangeOrElement Range or element to check.
     * @param {Object} [options]
     * @param {Boolean} [options.ignoreWhitespaces] Whether text node with whitespaces only should be considered empty.
     * @param {Boolean} [options.ignoreMarkers] Whether markers should be ignored.
     * @returns {Boolean}
     */
    hasContent(rangeOrElement, options = {}) {
        const range = rangeOrElement instanceof range_Range ? rangeOrElement : range_Range._createIn(rangeOrElement);
        if (range.isCollapsed) {
            return false;
        }
        const { ignoreWhitespaces = false, ignoreMarkers = false } = options;
        // Check if there are any markers which affects data in this given range.
        if (!ignoreMarkers) {
            for (const intersectingMarker of this.markers.getMarkersIntersectingRange(range)) {
                if (intersectingMarker.affectsData) {
                    return true;
                }
            }
        }
        for (const item of range.getItems()) {
            if (this.schema.isContent(item)) {
                if (item.is('$textProxy')) {
                    if (!ignoreWhitespaces) {
                        return true;
                    }
                    else if (item.data.search(/\S/) !== -1) {
                        return true;
                    }
                }
                else {
                    return true;
                }
            }
        }
        return false;
    }
    /**
     * Creates a position from the given root and path in that root.
     *
     * Note: This method is also available as
     * {@link module:engine/model/writer~Writer#createPositionFromPath `Writer#createPositionFromPath()`}.
     *
     * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} root Root of the position.
     * @param {Array.<Number>} path Position path. See {@link module:engine/model/position~Position#path}.
     * @param {module:engine/model/position~PositionStickiness} [stickiness='toNone'] Position stickiness.
     * See {@link module:engine/model/position~PositionStickiness}.
     * @returns {module:engine/model/position~Position}
     */
    createPositionFromPath(root, path, stickiness) {
        return new position_Position(root, path, stickiness);
    }
    /**
     * Creates position at the given location. The location can be specified as:
     *
     * * a {@link module:engine/model/position~Position position},
     * * a parent element and offset in that element,
     * * a parent element and `'end'` (the position will be set at the end of that element),
     * * a {@link module:engine/model/item~Item model item} and `'before'` or `'after'`
     * (the position will be set before or after the given model item).
     *
     * This method is a shortcut to other factory methods such as:
     *
     * * {@link module:engine/model/model~Model#createPositionBefore `createPositionBefore()`},
     * * {@link module:engine/model/model~Model#createPositionAfter `createPositionAfter()`}.
     *
     * Note: This method is also available as
     * {@link module:engine/model/writer~Writer#createPositionAt `Writer#createPositionAt()`},
     *
     * @param {module:engine/model/item~Item|module:engine/model/position~Position} itemOrPosition
     * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
     * first parameter is a {@link module:engine/model/item~Item model item}.
     */
    createPositionAt(itemOrPosition, offset) {
        return position_Position._createAt(itemOrPosition, offset);
    }
    /**
     * Creates a new position after the given {@link module:engine/model/item~Item model item}.
     *
     * Note: This method is also available as
     * {@link module:engine/model/writer~Writer#createPositionAfter `Writer#createPositionAfter()`}.
     *
     * @param {module:engine/model/item~Item} item Item after which the position should be placed.
     * @returns {module:engine/model/position~Position}
     */
    createPositionAfter(item) {
        return position_Position._createAfter(item);
    }
    /**
     * Creates a new position before the given {@link module:engine/model/item~Item model item}.
     *
     * Note: This method is also available as
     * {@link module:engine/model/writer~Writer#createPositionBefore `Writer#createPositionBefore()`}.
     *
     * @param {module:engine/model/item~Item} item Item before which the position should be placed.
     * @returns {module:engine/model/position~Position}
     */
    createPositionBefore(item) {
        return position_Position._createBefore(item);
    }
    /**
     * Creates a range spanning from the `start` position to the `end` position.
     *
     * Note: This method is also available as
     * {@link module:engine/model/writer~Writer#createRange `Writer#createRange()`}:
     *
     *		model.change( writer => {
     *			const range = writer.createRange( start, end );
     *		} );
     *
     * @param {module:engine/model/position~Position} start Start position.
     * @param {module:engine/model/position~Position} [end] End position. If not set, the range will be collapsed
     * to the `start` position.
     * @returns {module:engine/model/range~Range}
     */
    createRange(start, end) {
        return new range_Range(start, end);
    }
    /**
     * Creates a range inside the given element which starts before the first child of
     * that element and ends after the last child of that element.
     *
     * Note: This method is also available as
     * {@link module:engine/model/writer~Writer#createRangeIn `Writer#createRangeIn()`}:
     *
     *		model.change( writer => {
     *			const range = writer.createRangeIn( paragraph );
     *		} );
     *
     * @param {module:engine/model/element~Element} element Element which is a parent for the range.
     * @returns {module:engine/model/range~Range}
     */
    createRangeIn(element) {
        return range_Range._createIn(element);
    }
    /**
     * Creates a range that starts before the given {@link module:engine/model/item~Item model item} and ends after it.
     *
     * Note: This method is also available on `writer` instance as
     * {@link module:engine/model/writer~Writer#createRangeOn `Writer.createRangeOn()`}:
     *
     *		model.change( writer => {
     *			const range = writer.createRangeOn( paragraph );
     *		} );
     *
     * @param {module:engine/model/item~Item} item
     * @returns {module:engine/model/range~Range}
     */
    createRangeOn(item) {
        return range_Range._createOn(item);
    }
    /**
     * Creates a new selection instance based on the given {@link module:engine/model/selection~Selectable selectable}
     * or creates an empty selection if no arguments were passed.
     *
     * Note: This method is also available as
     * {@link module:engine/model/writer~Writer#createSelection `Writer#createSelection()`}.
     *
     *		// Creates empty selection without ranges.
     *		const selection = writer.createSelection();
     *
     *		// Creates selection at the given range.
     *		const range = writer.createRange( start, end );
     *		const selection = writer.createSelection( range );
     *
     *		// Creates selection at the given ranges
     *		const ranges = [ writer.createRange( start1, end2 ), writer.createRange( star2, end2 ) ];
     *		const selection = writer.createSelection( ranges );
     *
     *		// Creates selection from the other selection.
     *		// Note: It doesn't copies selection attributes.
     *		const otherSelection = writer.createSelection();
     *		const selection = writer.createSelection( otherSelection );
     *
     *		// Creates selection from the given document selection.
     *		// Note: It doesn't copies selection attributes.
     *		const documentSelection = model.document.selection;
     *		const selection = writer.createSelection( documentSelection );
     *
     *		// Creates selection at the given position.
     *		const position = writer.createPositionFromPath( root, path );
     *		const selection = writer.createSelection( position );
     *
     *		// Creates selection at the given offset in the given element.
     *		const paragraph = writer.createElement( 'paragraph' );
     *		const selection = writer.createSelection( paragraph, offset );
     *
     *		// Creates a range inside an {@link module:engine/model/element~Element element} which starts before the
     *		// first child of that element and ends after the last child of that element.
     *		const selection = writer.createSelection( paragraph, 'in' );
     *
     *		// Creates a range on an {@link module:engine/model/item~Item item} which starts before the item and ends
     *		// just after the item.
     *		const selection = writer.createSelection( paragraph, 'on' );
     *
     *		// Additional options (`'backward'`) can be specified as the last argument.
     *
     *		// Creates backward selection.
     *		const selection = writer.createSelection( range, { backward: true } );
     *
     * @param {module:engine/model/selection~Selectable} selectable
     * @param {Number|'before'|'end'|'after'|'on'|'in'} [optionsOrPlaceOrOffset] Sets place or offset of the selection.
     * @param {Object} [options]
     * @param {Boolean} [options.backward] Sets this selection instance to be backward.
     * @returns {module:engine/model/selection~Selection}
     */
    createSelection(...args) {
        return new selection_Selection(...args);
    }
    /**
     * Creates a {@link module:engine/model/batch~Batch} instance.
     *
     * **Note:** In most cases creating a batch instance is not necessary as they are created when using:
     *
     * * {@link #change `change()`},
     * * {@link #enqueueChange `enqueueChange()`}.
     *
     * @param {Object} [type] {@link module:engine/model/batch~Batch#constructor The type} of the batch.
     * @returns {module:engine/model/batch~Batch}
     */
    createBatch(type) {
        return new Batch(type);
    }
    /**
     * Creates an operation instance from a JSON object (parsed JSON string).
     *
     * This is an alias for {@link module:engine/model/operation/operationfactory~OperationFactory.fromJSON `OperationFactory.fromJSON()`}.
     *
     * @param {Object} json Deserialized JSON object.
     * @returns {module:engine/model/operation/operation~Operation}
     */
    createOperationFromJSON(json) {
        return OperationFactory.fromJSON(json, this.document);
    }
    /**
     * Removes all events listeners set by model instance and destroys {@link module:engine/model/document~Document}.
     */
    destroy() {
        this.document.destroy();
        this.stopListening();
    }
    /**
     * Common part of {@link module:engine/model/model~Model#change} and {@link module:engine/model/model~Model#enqueueChange}
     * which calls callbacks and returns array of values returned by these callbacks.
     *
     * @private
     * @returns {Array.<*>} Array of values returned by callbacks.
     */
    _runPendingChanges() {
        const ret = [];
        this.fire('_beforeChanges');
        try {
            while (this._pendingChanges.length) {
                // Create a new writer using batch instance created for this chain of changes.
                const currentBatch = this._pendingChanges[0].batch;
                this._currentWriter = new Writer(this, currentBatch);
                // Execute changes callback and gather the returned value.
                const callbackReturnValue = this._pendingChanges[0].callback(this._currentWriter);
                ret.push(callbackReturnValue);
                this.document._handleChangeBlock(this._currentWriter);
                this._pendingChanges.shift();
                this._currentWriter = null;
            }
        }
        finally {
            this._pendingChanges.length = 0;
            this._currentWriter = null;
            this.fire('_afterChanges');
        }
        return ret;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/editingkeystrokehandler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/editingkeystrokehandler
 */

/**
 * A keystroke handler for editor editing. Its instance is available
 * in {@link module:core/editor/editor~Editor#keystrokes} so plugins
 * can register their keystrokes.
 *
 * E.g. an undo plugin would do this:
 *
 *		editor.keystrokes.set( 'Ctrl+Z', 'undo' );
 *		editor.keystrokes.set( 'Ctrl+Shift+Z', 'redo' );
 *		editor.keystrokes.set( 'Ctrl+Y', 'redo' );
 *
 * @extends module:utils/keystrokehandler~KeystrokeHandler
 */
class EditingKeystrokeHandler extends KeystrokeHandler {
    /**
     * Creates an instance of the keystroke handler.
     *
     * @param {module:core/editor/editor~Editor} editor
     */
    constructor(editor) {
        super();
        /**
         * The editor instance.
         *
         * @readonly
         * @member {module:core/editor/editor~Editor}
         */
        this.editor = editor;
    }
    /**
     * Registers a handler for the specified keystroke.
     *
     * The handler can be specified as a command name or a callback.
     *
     * @param {String|Array.<String|Number>} keystroke Keystroke defined in a format accepted by
     * the {@link module:utils/keyboard~parseKeystroke} function.
     * @param {Function|String} callback If a string is passed, then the keystroke will
     * {@link module:core/editor/editor~Editor#execute execute a command}.
     * If a function, then it will be called with the
     * {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
     * a `cancel()` helper to both `preventDefault()` and `stopPropagation()` of the event.
     * @param {Object} [options={}] Additional options.
     * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of the keystroke
     * callback. The higher the priority value the sooner the callback will be executed. Keystrokes having the same priority
     * are called in the order they were added.
     */
    set(keystroke, callback, options = {}) {
        if (typeof callback == 'string') {
            const commandName = callback;
            callback = (evtData, cancel) => {
                this.editor.execute(commandName);
                cancel();
            };
        }
        super.set(keystroke, callback, options);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/editor/editor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/editor/editor
 */












/**
 * The class representing a basic, generic editor.
 *
 * Check out the list of its subclasses to learn about specific editor implementations.
 *
 * All editor implementations (like {@link module:editor-classic/classiceditor~ClassicEditor} or
 * {@link module:editor-inline/inlineeditor~InlineEditor}) should extend this class. They can add their
 * own methods and properties.
 *
 * When you are implementing a plugin, this editor represents the API
 * which your plugin can expect to get when using its {@link module:core/plugin~Plugin#editor} property.
 *
 * This API should be sufficient in order to implement the "editing" part of your feature
 * (schema definition, conversion, commands, keystrokes, etc.).
 * It does not define the editor UI, which is available only if
 * the specific editor implements also the {@link module:core/editor/editorwithui~EditorWithUI} interface
 * (as most editor implementations do).
 *
 * @abstract
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class Editor extends Observable {
    /**
     * Creates a new instance of the editor class.
     *
     * Usually, not to be used directly. See the static {@link module:core/editor/editor~Editor.create `create()`} method.
     *
     * @param {Object} [config={}] The editor configuration.
     */
    constructor(config = {}) {
        super();
        const constructor = this.constructor;
        // Prefer the language passed as the argument to the constructor instead of the constructor's `defaultConfig`, if both are set.
        const language = config.language || (constructor.defaultConfig && constructor.defaultConfig.language);
        /**
         * The editor context.
         * When it is not provided through the configuration, the editor creates it.
         *
         * @protected
         * @type {module:core/context~Context}
         */
        this._context = config.context || new Context({ language });
        this._context._addEditor(this, !config.context);
        // Clone the plugins to make sure that the plugin array will not be shared
        // between editors and make the watchdog feature work correctly.
        const availablePlugins = Array.from(constructor.builtinPlugins || []);
        /**
         * Stores all configurations specific to this editor instance.
         *
         *		editor.config.get( 'image.toolbar' );
         *		// -> [ 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ]
         *
         * @readonly
         * @member {module:utils/config~Config}
         */
        this.config = new Config(config, constructor.defaultConfig);
        this.config.define('plugins', availablePlugins);
        this.config.define(this._context._getEditorConfig());
        /**
         * The plugins loaded and in use by this editor instance.
         *
         *		editor.plugins.get( 'ClipboardPipeline' ); // -> An instance of the clipboard pipeline plugin.
         *
         * @readonly
         * @member {module:core/plugincollection~PluginCollection}
         */
        this.plugins = new PluginCollection(this, availablePlugins, this._context.plugins);
        /**
         * The locale instance.
         *
         * @readonly
         * @type {module:utils/locale~Locale}
         */
        this.locale = this._context.locale;
        /**
         * Shorthand for {@link module:utils/locale~Locale#t}.
         *
         * @see module:utils/locale~Locale#t
         * @method #t
         */
        this.t = this.locale.t;
        /**
         * A set of lock IDs for the {@link #isReadOnly} getter.
         *
         * @private
         * @type {Set.<String|Symbol>}
         */
        this._readOnlyLocks = new Set();
        /**
         * Commands registered to the editor.
         *
         * Use the shorthand {@link #execute `editor.execute()`} method to execute commands:
         *
         *		// Execute the bold command:
         *		editor.execute( 'bold' );
         *
         *		// Check the state of the bold command:
         *		editor.commands.get( 'bold' ).value;
         *
         * @readonly
         * @member {module:core/commandcollection~CommandCollection}
         */
        this.commands = new CommandCollection();
        /**
         * Indicates the editor life-cycle state.
         *
         * The editor is in one of the following states:
         *
         * * `initializing` &ndash; During the editor initialization (before
         * {@link module:core/editor/editor~Editor.create `Editor.create()`}) finished its job.
         * * `ready` &ndash; After the promise returned by the {@link module:core/editor/editor~Editor.create `Editor.create()`}
         * method is resolved.
         * * `destroyed` &ndash; Once the {@link #destroy `editor.destroy()`} method was called.
         *
         * @observable
         * @member {'initializing'|'ready'|'destroyed'} #state
         */
        this.set('state', 'initializing');
        this.once('ready', () => (this.state = 'ready'), { priority: 'high' });
        this.once('destroy', () => (this.state = 'destroyed'), { priority: 'high' });
        /**
         * The editor's model.
         *
         * The central point of the editor's abstract data model.
         *
         * @readonly
         * @member {module:engine/model/model~Model}
         */
        this.model = new Model();
        const stylesProcessor = new StylesProcessor();
        /**
         * The {@link module:engine/controller/datacontroller~DataController data controller}.
         * Used e.g. for setting and retrieving the editor data.
         *
         * @readonly
         * @member {module:engine/controller/datacontroller~DataController}
         */
        this.data = new DataController(this.model, stylesProcessor);
        /**
         * The {@link module:engine/controller/editingcontroller~EditingController editing controller}.
         * Controls user input and rendering the content for editing.
         *
         * @readonly
         * @member {module:engine/controller/editingcontroller~EditingController}
         */
        this.editing = new EditingController(this.model, stylesProcessor);
        this.editing.view.document.bind('isReadOnly').to(this);
        /**
         * Conversion manager through which you can register model-to-view and view-to-model converters.
         *
         * See the {@link module:engine/conversion/conversion~Conversion} documentation to learn how to add converters.
         *
         * @readonly
         * @member {module:engine/conversion/conversion~Conversion}
         */
        this.conversion = new Conversion([this.editing.downcastDispatcher, this.data.downcastDispatcher], this.data.upcastDispatcher);
        this.conversion.addAlias('dataDowncast', this.data.downcastDispatcher);
        this.conversion.addAlias('editingDowncast', this.editing.downcastDispatcher);
        /**
         * An instance of the {@link module:core/editingkeystrokehandler~EditingKeystrokeHandler}.
         *
         * It allows setting simple keystrokes:
         *
         *		// Execute the bold command on Ctrl+E:
         *		editor.keystrokes.set( 'Ctrl+E', 'bold' );
         *
         *		// Execute your own callback:
         *		editor.keystrokes.set( 'Ctrl+E', ( data, cancel ) => {
         *			console.log( data.keyCode );
         *
         *			// Prevent the default (native) action and stop the underlying keydown event
         *			// so no other editor feature will interfere.
         *			cancel();
         *		} );
         *
         * Note: Certain typing-oriented keystrokes (like <kbd>Backspace</kbd> or <kbd>Enter</kbd>) are handled
         * by a low-level mechanism and trying to listen to them via the keystroke handler will not work reliably.
         * To handle these specific keystrokes, see the events fired by the
         * {@link module:engine/view/document~Document editing view document} (`editor.editing.view.document`).
         *
         * @readonly
         * @member {module:core/editingkeystrokehandler~EditingKeystrokeHandler}
         */
        this.keystrokes = new EditingKeystrokeHandler(this);
        this.keystrokes.listenTo(this.editing.view.document);
    }
    /**
     * Defines whether the editor is in the read-only mode.
     *
     * In read-only mode the editor {@link #commands commands} are disabled so it is not possible
     * to modify the document by using them. Also, the editable element(s) become non-editable.
     *
     * In order to make the editor read-only, you need to call the {@link #enableReadOnlyMode} method:
     *
     *		editor.enableReadOnlyMode( 'feature-id' );
     *
     * Later, to turn off the read-only mode, call {@link #disableReadOnlyMode}:
     *
     * 		editor.disableReadOnlyMode( 'feature-id' );
     *
     * @readonly
     * @observable
     * @member {Boolean} #isReadOnly
     */
    get isReadOnly() {
        return this._readOnlyLocks.size > 0;
    }
    set isReadOnly(value) {
        /**
         * The {@link #isReadOnly Editor#isReadOnly} property is read-only since version `34.0.0` and can be set only using
         * {@link #enableReadOnlyMode `Editor#enableReadOnlyMode( lockId )`} and
         * {@link #disableReadOnlyMode `Editor#disableReadOnlyMode( lockId )`}.
         *
         * Usage before version `34.0.0`:
         *
         *		editor.isReadOnly = true;
         * 		editor.isReadOnly = false;
         *
         * Usage since version `34.0.0`:
         *
         *		editor.enableReadOnlyMode( 'my-feature-id' );
         * 		editor.disableReadOnlyMode( 'my-feature-id' );
         *
         * @error editor-isreadonly-has-no-setter
         */
        throw new CKEditorError('editor-isreadonly-has-no-setter');
    }
    /**
     * Turns on the read-only mode in the editor.
     *
     * Editor can be switched to or out of the read-only mode by many features, under various circumstances. The editor supports locking
     * mechanism for the read-only mode. It enables easy control over the read-only mode when many features wants to turn it on or off at
     * the same time, without conflicting with each other. It guarantees that you will not make the editor editable accidentally (which
     * could lead to errors).
     *
     * Each read-only mode request is identified by a unique id (also called "lock"). If multiple plugins requested to turn on the
     * read-only mode, then, the editor will become editable only after all these plugins turn the read-only mode off (using the same ids).
     *
     * Note, that you cannot force the editor to disable the read-only mode if other plugins set it.
     *
     * After the first `enableReadOnlyMode()` call, the {@link #isReadOnly `isReadOnly` property} will be set to `true`:
     *
     *		editor.isReadOnly; // `false`.
     * 		editor.enableReadOnlyMode( 'my-feature-id' );
     * 		editor.isReadOnly; // `true`.
     *
     * You can turn off the read-only mode ("clear the lock") using the {@link #disableReadOnlyMode `disableReadOnlyMode()`} method:
     *
     * 		editor.enableReadOnlyMode( 'my-feature-id' );
     * 		// ...
     * 		editor.disableReadOnlyMode( 'my-feature-id' );
     * 		editor.isReadOnly; // `false`.
     *
     * All "locks" need to be removed to enable editing:
     *
     * 		editor.enableReadOnlyMode( 'my-feature-id' );
     * 		editor.enableReadOnlyMode( 'my-other-feature-id' );
     * 		// ...
     * 		editor.disableReadOnlyMode( 'my-feature-id' );
     * 		editor.isReadOnly; // `true`.
     * 		editor.disableReadOnlyMode( 'my-other-feature-id' );
     * 		editor.isReadOnly; // `false`.
     *
     * @param {String|Symbol} lockId A unique ID for setting the editor to the read-only state.
     */
    enableReadOnlyMode(lockId) {
        if (typeof lockId !== 'string' && typeof lockId !== 'symbol') {
            /**
             * The lock ID is missing or it is not a string or symbol.
             *
             * @error editor-read-only-lock-id-invalid
             */
            throw new CKEditorError('editor-read-only-lock-id-invalid', null, { lockId });
        }
        if (this._readOnlyLocks.has(lockId)) {
            return;
        }
        this._readOnlyLocks.add(lockId);
        if (this._readOnlyLocks.size === 1) {
            // Manually fire the `change:isReadOnly` event as only getter is provided.
            this.fire('change:isReadOnly', 'isReadOnly', true, false);
        }
    }
    /**
     * Removes the read-only lock from the editor with given lock ID.
     *
     * When no lock is present on the editor anymore, then the {@link #isReadOnly `isReadOnly` property} will be set to `false`.
     *
     * @param {String|Symbol} lockId The lock ID for setting the editor to the read-only state.
     */
    disableReadOnlyMode(lockId) {
        if (typeof lockId !== 'string' && typeof lockId !== 'symbol') {
            throw new CKEditorError('editor-read-only-lock-id-invalid', null, { lockId });
        }
        if (!this._readOnlyLocks.has(lockId)) {
            return;
        }
        this._readOnlyLocks.delete(lockId);
        if (this._readOnlyLocks.size === 0) {
            // Manually fire the `change:isReadOnly` event as only getter is provided.
            this.fire('change:isReadOnly', 'isReadOnly', false, true);
        }
    }
    /**
     * Loads and initializes plugins specified in the configuration.
     *
     * @returns {Promise.<module:core/plugin~LoadedPlugins>} A promise which resolves
     * once the initialization is completed, providing an array of loaded plugins.
     */
    initPlugins() {
        const config = this.config;
        const plugins = config.get('plugins');
        const removePlugins = config.get('removePlugins') || [];
        const extraPlugins = config.get('extraPlugins') || [];
        const substitutePlugins = config.get('substitutePlugins') || [];
        return this.plugins.init(plugins.concat(extraPlugins), removePlugins, substitutePlugins);
    }
    /**
     * Destroys the editor instance, releasing all resources used by it.
     *
     * **Note** The editor cannot be destroyed during the initialization phase so if it is called
     * while the editor {@link #state is being initialized}, it will wait for the editor initialization before destroying it.
     *
     * @fires destroy
     * @returns {Promise} A promise that resolves once the editor instance is fully destroyed.
     */
    destroy() {
        let readyPromise = Promise.resolve();
        if (this.state == 'initializing') {
            readyPromise = new Promise(resolve => this.once('ready', resolve));
        }
        return readyPromise
            .then(() => {
            this.fire('destroy');
            this.stopListening();
            this.commands.destroy();
        })
            .then(() => this.plugins.destroy())
            .then(() => {
            this.model.destroy();
            this.data.destroy();
            this.editing.destroy();
            this.keystrokes.destroy();
        })
            // Remove the editor from the context.
            // When the context was created by this editor, the context will be destroyed.
            .then(() => this._context._removeEditor(this));
    }
    /**
     * Executes the specified command with given parameters.
     *
     * Shorthand for:
     *
     *		editor.commands.get( commandName ).execute( ... );
     *
     * @param {String} commandName The name of the command to execute.
     * @param {*} [...commandParams] Command parameters.
     * @returns {*} The value returned by the {@link module:core/commandcollection~CommandCollection#execute `commands.execute()`}.
     */
    execute(commandName, ...args) {
        try {
            return this.commands.execute(commandName, ...args);
        }
        catch (err) {
            // @if CK_DEBUG // throw err;
            /* istanbul ignore next */
            CKEditorError.rethrowUnexpectedError(err, this);
        }
    }
    /**
     * Focuses the editor.
     *
     * **Note** To explicitly focus the editing area of the editor, use the
     * {@link module:engine/view/view~View#focus `editor.editing.view.focus()`} method of the editing view.
     *
     * Check out the {@glink framework/guides/deep-dive/ui/focus-tracking#focus-in-the-editor-ui Focus in the editor UI} section
     * of the {@glink framework/guides/deep-dive/ui/focus-tracking Deep dive into focus tracking} guide to learn more.
     */
    focus() {
        this.editing.view.focus();
    }
}
/**
 * This error is thrown when trying to pass a `<textarea>` element to a `create()` function of an editor class.
 *
 * The only editor type which can be initialized on `<textarea>` elements is
 * the {@glink installation/getting-started/predefined-builds#classic-editor classic editor}.
 * This editor hides the passed element and inserts its own UI next to it. Other types of editors reuse the passed element as their root
 * editable element and therefore `<textarea>` is not appropriate for them. Use a `<div>` or another text container instead:
 *
 *		<div id="editor">
 *			<p>Initial content.</p>
 *		</div>
 *
 * @error editor-wrong-element
 */
/**
 * An array of plugins built into this editor class.
 *
 * It is used in CKEditor 5 builds to provide a list of plugins which are later automatically initialized
 * during the editor initialization.
 *
 * They will be automatically initialized by the editor, unless listed in `config.removePlugins` and
 * unless `config.plugins` is passed.
 *
 *		// Build some plugins into the editor class first.
 *		ClassicEditor.builtinPlugins = [ FooPlugin, BarPlugin ];
 *
 *		// Normally, you need to define config.plugins, but since ClassicEditor.builtinPlugins was
 *		// defined, now you can call create() without any configuration.
 *		ClassicEditor
 *			.create( sourceElement )
 *			.then( editor => {
 *				editor.plugins.get( FooPlugin ); // -> An instance of the Foo plugin.
 *				editor.plugins.get( BarPlugin ); // -> An instance of the Bar plugin.
 *			} );
 *
 *		ClassicEditor
 *			.create( sourceElement, {
 *				// Do not initialize these plugins (note: it is defined by a string):
 *				removePlugins: [ 'Foo' ]
 *			} )
 *			.then( editor => {
 *				editor.plugins.get( FooPlugin ); // -> Undefined.
 *				editor.config.get( BarPlugin ); // -> An instance of the Bar plugin.
 *			} );
 *
 *		ClassicEditor
 *			.create( sourceElement, {
 *				// Load only this plugin. It can also be defined by a string if
 *				// this plugin was built into the editor class.
 *				plugins: [ FooPlugin ]
 *			} )
 *			.then( editor => {
 *				editor.plugins.get( FooPlugin ); // -> An instance of the Foo plugin.
 *				editor.config.get( BarPlugin ); // -> Undefined.
 *			} );
 *
 * See also {@link module:core/editor/editor~Editor.defaultConfig}.
 *
 * @static
 * @member {Array.<Function>} module:core/editor/editor~Editor.builtinPlugins
 */
/**
 * The default configuration which is built into the editor class.
 *
 * It is used in CKEditor 5 builds to provide the default configuration options which are later used during the editor initialization.
 *
 *		ClassicEditor.defaultConfig = {
 *			foo: 1,
 *			bar: 2
 *		};
 *
 *		ClassicEditor
 *			.create( sourceElement )
 *			.then( editor => {
 *				editor.config.get( 'foo' ); // -> 1
 *				editor.config.get( 'bar' ); // -> 2
 *			} );
 *
 *		// The default options can be overridden by the configuration passed to create().
 *		ClassicEditor
 *			.create( sourceElement, { bar: 3 } )
 *			.then( editor => {
 *				editor.config.get( 'foo' ); // -> 1
 *				editor.config.get( 'bar' ); // -> 3
 *			} );
 *
 * See also {@link module:core/editor/editor~Editor.builtinPlugins}.
 *
 * @static
 * @member {Object} module:core/editor/editor~Editor.defaultConfig
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/componentfactory.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/componentfactory
 */

/**
 * A helper class implementing the UI component ({@link module:ui/view~View view}) factory.
 *
 * It allows functions producing specific UI components to be registered under their unique names
 * in the factory. A registered component can be then instantiated by providing its name.
 * Note that names are case insensitive.
 *
 *		// The editor provides localization tools for the factory.
 *		const factory = new ComponentFactory( editor );
 *
 *		factory.add( 'foo', locale => new FooView( locale ) );
 *		factory.add( 'bar', locale => new BarView( locale ) );
 *
 *		// An instance of FooView.
 *		const fooInstance = factory.create( 'foo' );
 *
 *		// Names are case insensitive so this is also allowed:
 *		const barInstance = factory.create( 'Bar' );
 *
 * The {@link module:core/editor/editor~Editor#locale editor locale} is passed to the factory
 * function when {@link module:ui/componentfactory~ComponentFactory#create} is called.
 */
class ComponentFactory {
    /**
     * Creates an instance of the factory.
     *
     * @constructor
     * @param {module:core/editor/editor~Editor} editor The editor instance.
     */
    constructor(editor) {
        /**
         * The editor instance that the factory belongs to.
         *
         * @readonly
         * @member {module:core/editor/editor~Editor}
         */
        this.editor = editor;
        /**
         * Registered component factories.
         *
         * @private
         * @member {Map}
         */
        this._components = new Map();
    }
    /**
     * Returns an iterator of registered component names. Names are returned in lower case.
     *
     * @returns {Iterable.<String>}
     */
    *names() {
        for (const value of this._components.values()) {
            yield value.originalName;
        }
    }
    /**
     * Registers a component factory function that will be used by the
     * {@link #create create} method and called with the
     * {@link module:core/editor/editor~Editor#locale editor locale} as an argument,
     * allowing localization of the {@link module:ui/view~View view}.
     *
     * @param {String} name The name of the component.
     * @param {Function} callback The callback that returns the component.
     */
    add(name, callback) {
        this._components.set(getNormalized(name), { callback, originalName: name });
    }
    /**
     * Creates an instance of a component registered in the factory under a specific name.
     *
     * When called, the {@link module:core/editor/editor~Editor#locale editor locale} is passed to
     * the previously {@link #add added} factory function, allowing localization of the
     * {@link module:ui/view~View view}.
     *
     * @param {String} name The name of the component.
     * @returns {module:ui/view~View} The instantiated component view.
     */
    create(name) {
        if (!this.has(name)) {
            /**
             * The required component is not registered in the component factory. Please make sure
             * the provided name is correct and the component has been correctly
             * {@link #add added} to the factory.
             *
             * @error componentfactory-item-missing
             * @param {String} name The name of the missing component.
             */
            throw new CKEditorError('componentfactory-item-missing', this, { name });
        }
        return this._components.get(getNormalized(name)).callback(this.editor.locale);
    }
    /**
     * Checks if a component of a given name is registered in the factory.
     *
     * @param {String} name The name of the component.
     * @returns {Boolean}
     */
    has(name) {
        return this._components.has(getNormalized(name));
    }
}
//
// Ensures that the component name used as the key in the internal map is in lower case.
//
// @private
// @param {String} name
// @returns {String}
function getNormalized(name) {
    return String(name).toLowerCase();
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/viewcollection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/viewcollection
 */


/**
 * Collects {@link module:ui/view~View} instances.
 *
 *		const parentView = new ParentView( locale );
 *		const collection = new ViewCollection( locale );
 *
 *		collection.setParent( parentView.element );
 *
 *		const viewA = new ChildView( locale );
 *		const viewB = new ChildView( locale );
 *
 * View collection renders and manages view {@link module:ui/view~View#element elements}:
 *
 *		collection.add( viewA );
 *		collection.add( viewB );
 *
 *		console.log( parentView.element.firsChild ); // -> viewA.element
 *		console.log( parentView.element.lastChild ); // -> viewB.element
 *
 * It {@link module:ui/viewcollection~ViewCollection#delegate propagates} DOM events too:
 *
 *		// Delegate #click and #keydown events from viewA and viewB to the parentView.
 *		collection.delegate( 'click' ).to( parentView );
 *
 *		parentView.on( 'click', ( evt ) => {
 *			console.log( `${ evt.source } has been clicked.` );
 *		} );
 *
 *		// This event will be delegated to the parentView.
 *		viewB.fire( 'click' );
 *
 * **Note**: A view collection can be used directly in the {@link module:ui/template~TemplateDefinition definition}
 * of a {@link module:ui/template~Template template}.
 *
 * @extends module:utils/collection~Collection
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class ViewCollection extends Collection {
    /**
     * Creates a new instance of the {@link module:ui/viewcollection~ViewCollection}.
     *
     * @param {Iterable.<module:ui/view~View>} [initialItems] The initial items of the collection.
     */
    constructor(initialItems = []) {
        super(initialItems, {
            // An #id Number attribute should be legal and not break the `ViewCollection` instance.
            // https://github.com/ckeditor/ckeditor5-ui/issues/93
            idProperty: 'viewUid'
        });
        // Handle {@link module:ui/view~View#element} in DOM when a new view is added to the collection.
        this.on('add', (evt, view, index) => {
            this._renderViewIntoCollectionParent(view, index);
        });
        // Handle {@link module:ui/view~View#element} in DOM when a view is removed from the collection.
        this.on('remove', (evt, view) => {
            if (view.element && this._parentElement) {
                view.element.remove();
            }
        });
        /**
         * A parent element within which child views are rendered and managed in DOM.
         *
         * @protected
         * @member {HTMLElement}
         */
        this._parentElement = null;
    }
    /**
     * Destroys the view collection along with child views.
     * See the view {@link module:ui/view~View#destroy} method.
     */
    destroy() {
        this.map(view => view.destroy());
    }
    /**
     * Sets the parent HTML element of this collection. When parent is set, {@link #add adding} and
     * {@link #remove removing} views in the collection synchronizes their
     * {@link module:ui/view~View#element elements} in the parent element.
     *
     * @param {HTMLElement} element A new parent element.
     */
    setParent(elementOrDocFragment) {
        this._parentElement = elementOrDocFragment;
        // Take care of the initial collection items passed to the constructor.
        for (const view of this) {
            this._renderViewIntoCollectionParent(view);
        }
    }
    /**
     * Delegates selected events coming from within views in the collection to any
     * {@link module:utils/emittermixin~Emitter}.
     *
     * For the following views and collection:
     *
     *		const viewA = new View();
     *		const viewB = new View();
     *		const viewC = new View();
     *
     *		const views = parentView.createCollection();
     *
     *		views.delegate( 'eventX' ).to( viewB );
     *		views.delegate( 'eventX', 'eventY' ).to( viewC );
     *
     *		views.add( viewA );
     *
     * the `eventX` is delegated (fired by) `viewB` and `viewC` along with `customData`:
     *
     *		viewA.fire( 'eventX', customData );
     *
     * and `eventY` is delegated (fired by) `viewC` along with `customData`:
     *
     *		viewA.fire( 'eventY', customData );
     *
     * See {@link module:utils/emittermixin~Emitter#delegate}.
     *
     * @param {...String} events {@link module:ui/view~View} event names to be delegated to another
     * {@link module:utils/emittermixin~Emitter}.
     * @returns {Object}
     * @returns {Function} return.to A function which accepts the destination of
     * {@link module:utils/emittermixin~Emitter#delegate delegated} events.
     */
    delegate(...events) {
        if (!events.length || !viewcollection_isStringArray(events)) {
            /**
             * All event names must be strings.
             *
             * @error ui-viewcollection-delegate-wrong-events
             */
            throw new CKEditorError('ui-viewcollection-delegate-wrong-events', this);
        }
        return {
            /**
             * Selects destination for {@link module:utils/emittermixin~Emitter#delegate} events.
             *
             * @memberOf module:ui/viewcollection~ViewCollection#delegate
             * @function module:ui/viewcollection~ViewCollection#delegate.to
             * @param {module:utils/emittermixin~Emitter} dest An `Emitter` instance which is
             * the destination for delegated events.
             */
            to: dest => {
                // Activate delegating on existing views in this collection.
                for (const view of this) {
                    for (const evtName of events) {
                        view.delegate(evtName).to(dest);
                    }
                }
                // Activate delegating on future views in this collection.
                this.on('add', (evt, view) => {
                    for (const evtName of events) {
                        view.delegate(evtName).to(dest);
                    }
                });
                // Deactivate delegating when view is removed from this collection.
                this.on('remove', (evt, view) => {
                    for (const evtName of events) {
                        view.stopDelegating(evtName, dest);
                    }
                });
            }
        };
    }
    /**
     * This method {@link module:ui/view~View#render renders} a new view added to the collection.
     *
     * If the {@link #_parentElement parent element} of the collection is set, this method also adds
     * the view's {@link module:ui/view~View#element} as a child of the parent in DOM at a specified index.
     *
     * **Note**: If index is not specified, the view's element is pushed as the last child
     * of the parent element.
     *
     * @private
     * @param {module:ui/view~View} view A new view added to the collection.
     * @param {Number} [index] An index the view holds in the collection. When not specified,
     * the view is added at the end.
     */
    _renderViewIntoCollectionParent(view, index) {
        if (!view.isRendered) {
            view.render();
        }
        if (view.element && this._parentElement) {
            this._parentElement.insertBefore(view.element, this._parentElement.children[index]);
        }
    }
}
// Check if all entries of the array are of `String` type.
//
// @private
// @param {Array} arr An array to be checked.
// @returns {Boolean}
function viewcollection_isStringArray(arr) {
    return arr.every(a => typeof a == 'string');
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/template.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/template
 */
/* global document */







const xhtmlNs = 'http://www.w3.org/1999/xhtml';
/**
 * A basic Template class. It renders a DOM HTML element or text from a
 * {@link module:ui/template~TemplateDefinition definition} and supports element attributes, children,
 * bindings to {@link module:utils/observablemixin~Observable observables} and DOM event propagation.
 *
 * A simple template can look like this:
 *
 *		const bind = Template.bind( observable, emitter );
 *
 *		new Template( {
 *			tag: 'p',
 *			attributes: {
 *				class: 'foo',
 *				style: {
 *					backgroundColor: 'yellow'
 *				}
 *			},
 *			on: {
 *				click: bind.to( 'clicked' )
 *			},
 *			children: [
 *				'A paragraph.'
 *			]
 *		} ).render();
 *
 * and it will render the following HTML element:
 *
 *		<p class="foo" style="background-color: yellow;">A paragraph.</p>
 *
 * Additionally, the `observable` will always fire `clicked` upon clicking `<p>` in the DOM.
 *
 * See {@link module:ui/template~TemplateDefinition} to know more about templates and complex
 * template definitions.
 *
* @mixes module:utils/emittermixin~EmitterMixin
 */
class Template extends Emitter {
    /**
     * Creates an instance of the {@link ~Template} class.
     *
     * @param {module:ui/template~TemplateDefinition} def The definition of the template.
     */
    constructor(def) {
        super();
        Object.assign(this, template_normalize(template_clone(def)));
        /**
         * Indicates whether this particular Template instance has been
         * {@link #render rendered}.
         *
         * @readonly
         * @protected
         * @member {Boolean}
         */
        this._isRendered = false;
        /**
         * The tag (`tagName`) of this template, e.g. `div`. It also indicates that the template
         * renders to an HTML element.
         *
         * @member {String} #tag
         */
        /**
         * The text of the template. It also indicates that the template renders to a DOM text node.
         *
         * @member {Array.<String|module:ui/template~TemplateValueSchema>} #text
         */
        /**
         * The attributes of the template, e.g. `{ id: [ 'ck-id' ] }`, corresponding with
         * the attributes of an HTML element.
         *
         * **Note**: This property only makes sense when {@link #tag} is defined.
         *
         * @member {Object} #attributes
         */
        /**
         * The children of the template. They can be either:
         * * independent instances of {@link ~Template} (sub–templates),
         * * native DOM Nodes.
         *
         * **Note**: This property only makes sense when {@link #tag} is defined.
         *
         * @member {Array.<module:ui/template~Template|Node>} #children
         */
        /**
         * The DOM event listeners of the template.
         *
         * @member {Object} #eventListeners
         */
        /**
         * The data used by the {@link #revert} method to restore a node to its original state.
         *
         * See: {@link #apply}.
         *
         * @readonly
         * @protected
         * @member {module:ui/template~RenderData}
         */
        this._revertData = null;
    }
    /**
     * Renders a DOM Node (an HTML element or text) out of the template.
     *
     *		const domNode = new Template( { ... } ).render();
     *
     * See: {@link #apply}.
     *
     * @returns {HTMLElement|Text}
     */
    render() {
        const node = this._renderNode({
            intoFragment: true
        });
        this._isRendered = true;
        return node;
    }
    /**
     * Applies the template to an existing DOM Node, either HTML element or text.
     *
     * **Note:** No new DOM nodes will be created. Applying extends:
     *
     * {@link module:ui/template~TemplateDefinition attributes},
     * {@link module:ui/template~TemplateDefinition event listeners}, and
     * `textContent` of {@link module:ui/template~TemplateDefinition children} only.
     *
     * **Note:** Existing `class` and `style` attributes are extended when a template
     * is applied to an HTML element, while other attributes and `textContent` are overridden.
     *
     * **Note:** The process of applying a template can be easily reverted using the
     * {@link module:ui/template~Template#revert} method.
     *
     *		const element = document.createElement( 'div' );
     *		const observable = new Model( { divClass: 'my-div' } );
     *		const emitter = Object.create( EmitterMixin );
     *		const bind = Template.bind( observable, emitter );
     *
     *		new Template( {
     *			attributes: {
     *				id: 'first-div',
     *				class: bind.to( 'divClass' )
     *			},
     *			on: {
     *				click: bind( 'elementClicked' ) // Will be fired by the observable.
     *			},
     *			children: [
     *				'Div text.'
     *			]
     *		} ).apply( element );
     *
     *		console.log( element.outerHTML ); // -> '<div id="first-div" class="my-div"></div>'
     *
     * @see module:ui/template~Template#render
     * @see module:ui/template~Template#revert
     * @param {Node} node Root node for the template to apply.
     */
    apply(node) {
        this._revertData = getEmptyRevertData();
        this._renderNode({
            node,
            intoFragment: false,
            isApplying: true,
            revertData: this._revertData
        });
        return node;
    }
    /**
     * Reverts a template {@link module:ui/template~Template#apply applied} to a DOM node.
     *
     * @param {Node} node The root node for the template to revert. In most of the cases, it is the
     * same node used by {@link module:ui/template~Template#apply}.
     */
    revert(node) {
        if (!this._revertData) {
            /**
             * Attempting to revert a template which has not been applied yet.
             *
             * @error ui-template-revert-not-applied
             */
            throw new CKEditorError('ui-template-revert-not-applied', [this, node]);
        }
        this._revertTemplateFromNode(node, this._revertData);
    }
    /**
     * Returns an iterator which traverses the template in search of {@link module:ui/view~View}
     * instances and returns them one by one.
     *
     *		const viewFoo = new View();
     *		const viewBar = new View();
     *		const viewBaz = new View();
     *		const template = new Template( {
     *			tag: 'div',
     *			children: [
     *				viewFoo,
     *				{
     *					tag: 'div',
     *					children: [
     *						viewBar
     *					]
     *				},
     *				viewBaz
     *			]
     *		} );
     *
     *		// Logs: viewFoo, viewBar, viewBaz
     *		for ( const view of template.getViews() ) {
     *			console.log( view );
     *		}
     *
     * @returns {Iterable.<module:ui/view~View>}
     */
    *getViews() {
        function* search(def) {
            if (def.children) {
                for (const child of def.children) {
                    if (isView(child)) {
                        yield child;
                    }
                    else if (isTemplate(child)) {
                        yield* search(child);
                    }
                }
            }
        }
        yield* search(this);
    }
    /**
     * An entry point to the interface which binds DOM nodes to
     * {@link module:utils/observablemixin~Observable observables}.
     * There are two types of bindings:
     *
     * * HTML element attributes or text `textContent` synchronized with attributes of an
     * {@link module:utils/observablemixin~Observable}. Learn more about {@link module:ui/template~BindChain#to}
     * and {@link module:ui/template~BindChain#if}.
     *
     *		const bind = Template.bind( observable, emitter );
     *
     *		new Template( {
     *			attributes: {
     *				// Binds the element "class" attribute to observable#classAttribute.
     *				class: bind.to( 'classAttribute' )
     *			}
     *		} ).render();
     *
     * * DOM events fired on HTML element propagated through
     * {@link module:utils/observablemixin~Observable}. Learn more about {@link module:ui/template~BindChain#to}.
     *
     *		const bind = Template.bind( observable, emitter );
     *
     *		new Template( {
     *			on: {
     *				// Will be fired by the observable.
     *				click: bind( 'elementClicked' )
     *			}
     *		} ).render();
     *
     * Also see {@link module:ui/view~View#bindTemplate}.
     *
     * @param {module:utils/observablemixin~Observable} observable An observable which provides boundable attributes.
     * @param {module:utils/emittermixin~Emitter} emitter An emitter that listens to observable attribute
     * changes or DOM Events (depending on the kind of the binding). Usually, a {@link module:ui/view~View} instance.
     * @returns {module:ui/template~BindChain}
     */
    static bind(observable, emitter) {
        return {
            to(eventNameOrFunctionOrAttribute, callback) {
                return new TemplateToBinding({
                    eventNameOrFunction: eventNameOrFunctionOrAttribute,
                    attribute: eventNameOrFunctionOrAttribute,
                    observable, emitter, callback
                });
            },
            if(attribute, valueIfTrue, callback) {
                return new TemplateIfBinding({
                    observable, emitter, attribute, valueIfTrue, callback
                });
            }
        };
    }
    /**
     * Extends an existing {@link module:ui/template~Template} instance with some additional content
     * from another {@link module:ui/template~TemplateDefinition}.
     *
     *		const bind = Template.bind( observable, emitter );
     *
     *		const template = new Template( {
     *			tag: 'p',
     *			attributes: {
     *				class: 'a',
     *				data-x: bind.to( 'foo' )
     *			},
     *			children: [
     *				{
     *					tag: 'span',
     *					attributes: {
     *						class: 'b'
     *					},
     *					children: [
     *						'Span'
     *					]
     *				}
     *			]
     *		 } );
     *
     *		// Instance-level extension.
     *		Template.extend( template, {
     *			attributes: {
     *				class: 'b',
     *				data-x: bind.to( 'bar' )
     *			},
     *			children: [
     *				{
     *					attributes: {
     *						class: 'c'
     *					}
     *				}
     *			]
     *		} );
     *
     *		// Child extension.
     *		Template.extend( template.children[ 0 ], {
     *			attributes: {
     *				class: 'd'
     *			}
     *		} );
     *
     * the `outerHTML` of `template.render()` is:
     *
     *		<p class="a b" data-x="{ observable.foo } { observable.bar }">
     *			<span class="b c d">Span</span>
     *		</p>
     *
     * @param {module:ui/template~Template} template An existing template instance to be extended.
     * @param {module:ui/template~TemplateDefinition} def Additional definition to be applied to a template.
     */
    static extend(template, def) {
        if (template._isRendered) {
            /**
             * Extending a template after rendering may not work as expected. To make sure
             * the {@link module:ui/template~Template.extend extending} works for an element,
             * make sure it happens before {@link #render} is called.
             *
             * @error template-extend-render
             */
            throw new CKEditorError('template-extend-render', [this, template]);
        }
        extendTemplate(template, template_normalize(template_clone(def)));
    }
    /**
     * Renders a DOM Node (either an HTML element or text) out of the template.
     *
     * @protected
     * @param {module:ui/template~RenderData} data Rendering data.
     */
    _renderNode(data) {
        let isInvalid;
        if (data.node) {
            // When applying, a definition cannot have "tag" and "text" at the same time.
            isInvalid = this.tag && this.text;
        }
        else {
            // When rendering, a definition must have either "tag" or "text": XOR( this.tag, this.text ).
            isInvalid = this.tag ? this.text : !this.text;
        }
        if (isInvalid) {
            /**
             * Node definition cannot have the "tag" and "text" properties at the same time.
             * Node definition must have either "tag" or "text" when rendering a new Node.
             *
             * @error ui-template-wrong-syntax
             */
            throw new CKEditorError('ui-template-wrong-syntax', this);
        }
        if (this.text) {
            return this._renderText(data);
        }
        else {
            return this._renderElement(data);
        }
    }
    /**
     * Renders an HTML element out of the template.
     *
     * @protected
     * @param {module:ui/template~RenderData} data Rendering data.
     */
    _renderElement(data) {
        let node = data.node;
        if (!node) {
            node = data.node = document.createElementNS(this.ns || xhtmlNs, this.tag);
        }
        this._renderAttributes(data);
        this._renderElementChildren(data);
        this._setUpListeners(data);
        return node;
    }
    /**
     * Renders a text node out of {@link module:ui/template~Template#text}.
     *
     * @protected
     * @param {module:ui/template~RenderData} data Rendering data.
     */
    _renderText(data) {
        let node = data.node;
        // Save the original textContent to revert it in #revert().
        if (node) {
            data.revertData.text = node.textContent;
        }
        else {
            node = data.node = document.createTextNode('');
        }
        // Check if this Text Node is bound to Observable. Cases:
        //
        //		text: [ Template.bind( ... ).to( ... ) ]
        //
        //		text: [
        //			'foo',
        //			Template.bind( ... ).to( ... ),
        //			...
        //		]
        //
        if (hasTemplateBinding(this.text)) {
            this._bindToObservable({
                schema: this.text,
                updater: getTextUpdater(node),
                data
            });
        }
        // Simply set text. Cases:
        //
        //		text: [ 'all', 'are', 'static' ]
        //
        //		text: [ 'foo' ]
        //
        else {
            node.textContent = this.text.join('');
        }
        return node;
    }
    /**
     * Renders HTML element attributes out of {@link module:ui/template~Template#attributes}.
     *
     * @protected
     * @param {module:ui/template~RenderData} data Rendering data.
     */
    _renderAttributes(data) {
        if (!this.attributes) {
            return;
        }
        const node = data.node;
        const revertData = data.revertData;
        for (const attrName in this.attributes) {
            // Current attribute value in DOM.
            const domAttrValue = node.getAttribute(attrName);
            // The value to be set.
            const attrValue = this.attributes[attrName];
            // Save revert data.
            if (revertData) {
                revertData.attributes[attrName] = domAttrValue;
            }
            // Detect custom namespace:
            //
            //		class: {
            //			ns: 'abc',
            //			value: Template.bind( ... ).to( ... )
            //		}
            //
            const attrNs = isNamespaced(attrValue) ? attrValue[0].ns : null;
            // Activate binding if one is found. Cases:
            //
            //		class: [
            //			Template.bind( ... ).to( ... )
            //		]
            //
            //		class: [
            //			'bar',
            //			Template.bind( ... ).to( ... ),
            //			'baz'
            //		]
            //
            //		class: {
            //			ns: 'abc',
            //			value: Template.bind( ... ).to( ... )
            //		}
            //
            if (hasTemplateBinding(attrValue)) {
                // Normalize attributes with additional data like namespace:
                //
                //		class: {
                //			ns: 'abc',
                //			value: [ ... ]
                //		}
                //
                const valueToBind = isNamespaced(attrValue) ? attrValue[0].value : attrValue;
                // Extend the original value of attributes like "style" and "class",
                // don't override them.
                if (revertData && shouldExtend(attrName)) {
                    valueToBind.unshift(domAttrValue);
                }
                this._bindToObservable({
                    schema: valueToBind,
                    updater: getAttributeUpdater(node, attrName, attrNs),
                    data
                });
            }
            // Style attribute could be an Object so it needs to be parsed in a specific way.
            //
            //		style: {
            //			width: '100px',
            //			height: Template.bind( ... ).to( ... )
            //		}
            //
            else if (attrName == 'style' && typeof attrValue[0] !== 'string') {
                this._renderStyleAttribute(attrValue[0], data);
            }
            // Otherwise simply set the static attribute:
            //
            //		class: [ 'foo' ]
            //
            //		class: [ 'all', 'are', 'static' ]
            //
            //		class: [
            //			{
            //				ns: 'abc',
            //				value: [ 'foo' ]
            //			}
            //		]
            //
            else {
                // Extend the original value of attributes like "style" and "class",
                // don't override them.
                if (revertData && domAttrValue && shouldExtend(attrName)) {
                    attrValue.unshift(domAttrValue);
                }
                const value = attrValue
                    // Retrieve "values" from:
                    //
                    //		class: [
                    //			{
                    //				ns: 'abc',
                    //				value: [ ... ]
                    //			}
                    //		]
                    //
                    .map((val) => val ? (val.value || val) : val)
                    // Flatten the array.
                    .reduce((prev, next) => prev.concat(next), [])
                    // Convert into string.
                    .reduce(arrayValueReducer, '');
                if (!isFalsy(value)) {
                    node.setAttributeNS(attrNs, attrName, value);
                }
            }
        }
    }
    /**
     * Renders the `style` attribute of an HTML element based on
     * {@link module:ui/template~Template#attributes}.
     *
     * A style attribute is an {Object} with static values:
     *
     *		attributes: {
     *			style: {
     *				color: 'red'
     *			}
     *		}
     *
     * or values bound to {@link module:ui/model~Model} properties:
     *
     *		attributes: {
     *			style: {
     *				color: bind.to( ... )
     *			}
     *		}
     *
     * Note: The `style` attribute is rendered without setting the namespace. It does not seem to be
     * needed.
     *
     * @private
     * @param {Object} styles Styles located in `attributes.style` of {@link module:ui/template~TemplateDefinition}.
     * @param {module:ui/template~RenderData} data Rendering data.
     */
    _renderStyleAttribute(styles, data) {
        const node = data.node;
        for (const styleName in styles) {
            const styleValue = styles[styleName];
            // Cases:
            //
            //		style: {
            //			color: bind.to( 'attribute' )
            //		}
            //
            if (hasTemplateBinding(styleValue)) {
                this._bindToObservable({
                    schema: [styleValue],
                    updater: getStyleUpdater(node, styleName),
                    data
                });
            }
            // Cases:
            //
            //		style: {
            //			color: 'red'
            //		}
            //
            else {
                node.style[styleName] = styleValue;
            }
        }
    }
    /**
     * Recursively renders HTML element's children from {@link module:ui/template~Template#children}.
     *
     * @protected
     * @param {module:ui/template~RenderData} data Rendering data.
     */
    _renderElementChildren(data) {
        const node = data.node;
        const container = data.intoFragment ? document.createDocumentFragment() : node;
        const isApplying = data.isApplying;
        let childIndex = 0;
        for (const child of this.children) {
            if (isViewCollection(child)) {
                if (!isApplying) {
                    child.setParent(node);
                    // Note: ViewCollection renders its children.
                    for (const view of child) {
                        container.appendChild(view.element);
                    }
                }
            }
            else if (isView(child)) {
                if (!isApplying) {
                    if (!child.isRendered) {
                        child.render();
                    }
                    container.appendChild(child.element);
                }
            }
            else if (isNode(child)) {
                container.appendChild(child);
            }
            else {
                if (isApplying) {
                    const revertData = data.revertData;
                    const childRevertData = getEmptyRevertData();
                    revertData.children.push(childRevertData);
                    child._renderNode({
                        intoFragment: false,
                        node: container.childNodes[childIndex++],
                        isApplying: true,
                        revertData: childRevertData
                    });
                }
                else {
                    container.appendChild(child.render());
                }
            }
        }
        if (data.intoFragment) {
            node.appendChild(container);
        }
    }
    /**
     * Activates `on` event listeners from the {@link module:ui/template~TemplateDefinition}
     * on an HTML element.
     *
     * @protected
     * @param {module:ui/template~RenderData} data Rendering data.
     */
    _setUpListeners(data) {
        if (!this.eventListeners) {
            return;
        }
        for (const key in this.eventListeners) {
            const revertBindings = this.eventListeners[key].map(schemaItem => {
                const [domEvtName, domSelector] = key.split('@');
                return schemaItem.activateDomEventListener(domEvtName, domSelector, data);
            });
            if (data.revertData) {
                data.revertData.bindings.push(revertBindings);
            }
        }
    }
    /**
     * For a given {@link module:ui/template~TemplateValueSchema} containing {@link module:ui/template~TemplateBinding}
     * activates the binding and sets its initial value.
     *
     * Note: {@link module:ui/template~TemplateValueSchema} can be for HTML element attributes or
     * text node `textContent`.
     *
     * @protected
     * @param {Object} options Binding options.
     * @param {module:ui/template~TemplateValueSchema} options.schema
     * @param {Function} options.updater A function which updates the DOM (like attribute or text).
     * @param {module:ui/template~RenderData} options.data Rendering data.
     */
    _bindToObservable({ schema, updater, data }) {
        const revertData = data.revertData;
        // Set initial values.
        syncValueSchemaValue(schema, updater, data);
        const revertBindings = schema
            // Filter "falsy" (false, undefined, null, '') value schema components out.
            .filter(item => !isFalsy(item))
            // Filter inactive bindings from schema, like static strings ('foo'), numbers (42), etc.
            .filter((item) => item.observable)
            // Once only the actual binding are left, let the emitter listen to observable change:attribute event.
            // TODO: Reduce the number of listeners attached as many bindings may listen
            // to the same observable attribute.
            .map(templateBinding => templateBinding.activateAttributeListener(schema, updater, data));
        if (revertData) {
            revertData.bindings.push(revertBindings);
        }
    }
    /**
     * Reverts {@link module:ui/template~RenderData#revertData template data} from a node to
     * return it to the original state.
     *
     * @protected
     * @param {HTMLElement|Text} node A node to be reverted.
     * @param {Object} revertData An object that stores information about what changes have been made by
     * {@link #apply} to the node. See {@link module:ui/template~RenderData#revertData} for more information.
     */
    _revertTemplateFromNode(node, revertData) {
        for (const binding of revertData.bindings) {
            // Each binding may consist of several observable+observable#attribute.
            // like the following has 2:
            //
            //		class: [
            //			'x',
            //			bind.to( 'foo' ),
            //			'y',
            //			bind.to( 'bar' )
            //		]
            //
            for (const revertBinding of binding) {
                revertBinding();
            }
        }
        if (revertData.text) {
            node.textContent = revertData.text;
            return;
        }
        const element = node;
        for (const attrName in revertData.attributes) {
            const attrValue = revertData.attributes[attrName];
            // When the attribute has **not** been set before #apply().
            if (attrValue === null) {
                element.removeAttribute(attrName);
            }
            else {
                element.setAttribute(attrName, attrValue);
            }
        }
        for (let i = 0; i < revertData.children.length; ++i) {
            this._revertTemplateFromNode(element.childNodes[i], revertData.children[i]);
        }
    }
}
/**
 * Describes a binding created by the {@link module:ui/template~Template.bind} interface.
 *
 * @protected
 * @internal
 */
class TemplateBinding {
    /**
     * Creates an instance of the {@link module:ui/template~TemplateBinding} class.
     *
     * @param {module:ui/template~TemplateDefinition} def The definition of the binding.
     */
    constructor(def) {
        this.attribute = def.attribute;
        this.observable = def.observable;
        this.emitter = def.emitter;
        this.callback = def.callback;
        /**
         * An observable instance of the binding. It either:
         *
         * * provides the attribute with the value,
         * * or passes the event when a corresponding DOM event is fired.
         *
         * @member {module:utils/observablemixin~ObservableMixin} module:ui/template~TemplateBinding#observable
         */
        /**
         * An {@link module:utils/emittermixin~Emitter} used by the binding to:
         *
         * * listen to the attribute change in the {@link module:ui/template~TemplateBinding#observable},
         * * or listen to the event in the DOM.
         *
         * @member {module:utils/emittermixin~EmitterMixin} module:ui/template~TemplateBinding#emitter
         */
        /**
         * The name of the {@link module:ui/template~TemplateBinding#observable observed attribute}.
         *
         * @member {String} module:ui/template~TemplateBinding#attribute
         */
        /**
         * A custom function to process the value of the {@link module:ui/template~TemplateBinding#attribute}.
         *
         * @member {Function} [module:ui/template~TemplateBinding#callback]
         */
    }
    /**
     * Returns the value of the binding. It is the value of the {@link module:ui/template~TemplateBinding#attribute} in
     * {@link module:ui/template~TemplateBinding#observable}. The value may be processed by the
     * {@link module:ui/template~TemplateBinding#callback}, if such has been passed to the binding.
     *
     * @param {Node} [node] A native DOM node, passed to the custom {@link module:ui/template~TemplateBinding#callback}.
     * @returns {*} The value of {@link module:ui/template~TemplateBinding#attribute} in
     * {@link module:ui/template~TemplateBinding#observable}.
     */
    getValue(node) {
        const value = this.observable[this.attribute];
        return this.callback ? this.callback(value, node) : value;
    }
    /**
     * Activates the listener which waits for changes of the {@link module:ui/template~TemplateBinding#attribute} in
     * {@link module:ui/template~TemplateBinding#observable}, then updates the DOM with the aggregated
     * value of {@link module:ui/template~TemplateValueSchema}.
     *
     * @param {module:ui/template~TemplateValueSchema} schema A full schema to generate an attribute or text in the DOM.
     * @param {Function} updater A DOM updater function used to update the native DOM attribute or text.
     * @param {module:ui/template~RenderData} data Rendering data.
     * @returns {Function} A function to sever the listener binding.
     */
    activateAttributeListener(schema, updater, data) {
        const callback = () => syncValueSchemaValue(schema, updater, data);
        this.emitter.listenTo(this.observable, `change:${this.attribute}`, callback);
        // Allows revert of the listener.
        return () => {
            this.emitter.stopListening(this.observable, `change:${this.attribute}`, callback);
        };
    }
}
/**
 * Describes either:
 *
 * * a binding to an {@link module:utils/observablemixin~Observable},
 * * or a native DOM event binding.
 *
 * It is created by the {@link module:ui/template~BindChain#to} method.
 *
 * @protected
 * @internal
 */
class TemplateToBinding extends TemplateBinding {
    constructor(def) {
        super(def);
        this.eventNameOrFunction = def.eventNameOrFunction;
    }
    /**
     * Activates the listener for the native DOM event, which when fired, is propagated by
     * the {@link module:ui/template~TemplateBinding#emitter}.
     *
     * @param {String} domEvtName The name of the native DOM event.
     * @param {String} domSelector The selector in the DOM to filter delegated events.
     * @param {module:ui/template~RenderData} data Rendering data.
     * @returns {Function} A function to sever the listener binding.
     */
    activateDomEventListener(domEvtName, domSelector, data) {
        const callback = (evt, domEvt) => {
            if (!domSelector || domEvt.target.matches(domSelector)) {
                if (typeof this.eventNameOrFunction == 'function') {
                    this.eventNameOrFunction(domEvt);
                }
                else {
                    this.observable.fire(this.eventNameOrFunction, domEvt);
                }
            }
        };
        this.emitter.listenTo(data.node, domEvtName, callback);
        // Allows revert of the listener.
        return () => {
            this.emitter.stopListening(data.node, domEvtName, callback);
        };
    }
}
/**
 * Describes a binding to {@link module:utils/observablemixin~ObservableMixin} created by the {@link module:ui/template~BindChain#if}
 * method.
 *
 * @protected
 * @internal
 */
class TemplateIfBinding extends TemplateBinding {
    constructor(def) {
        super(def);
        this.valueIfTrue = def.valueIfTrue;
    }
    /**
     * @inheritDoc
     */
    getValue(node) {
        const value = super.getValue(node);
        return isFalsy(value) ? false : (this.valueIfTrue || true);
    }
}
// Checks whether given {@link module:ui/template~TemplateValueSchema} contains a
// {@link module:ui/template~TemplateBinding}.
//
// @param {module:ui/template~TemplateValueSchema} schema
// @returns {Boolean}
function hasTemplateBinding(schema) {
    if (!schema) {
        return false;
    }
    // Normalize attributes with additional data like namespace:
    //
    //		class: {
    //			ns: 'abc',
    //			value: [ ... ]
    //		}
    //
    if (schema.value) {
        schema = schema.value;
    }
    if (Array.isArray(schema)) {
        return schema.some(hasTemplateBinding);
    }
    else if (schema instanceof TemplateBinding) {
        return true;
    }
    return false;
}
// Assembles the value using {@link module:ui/template~TemplateValueSchema} and stores it in a form of
// an Array. Each entry of the Array corresponds to one of {@link module:ui/template~TemplateValueSchema}
// items.
//
// @param {module:ui/template~TemplateValueSchema} schema
// @param {Node} node DOM Node updated when {@link module:utils/observablemixin~ObservableMixin} changes.
// @returns {Array}
function getValueSchemaValue(schema, node) {
    return schema.map(schemaItem => {
        // Process {@link module:ui/template~TemplateBinding} bindings.
        if (schemaItem instanceof TemplateBinding) {
            return schemaItem.getValue(node);
        }
        // All static values like strings, numbers, and "falsy" values (false, null, undefined, '', etc.) just pass.
        return schemaItem;
    });
}
// A function executed each time the bound Observable attribute changes, which updates the DOM with a value
// constructed from {@link module:ui/template~TemplateValueSchema}.
//
// @param {module:ui/template~TemplateValueSchema} schema
// @param {Function} updater A function which updates the DOM (like attribute or text).
// @param {Node} node DOM Node updated when {@link module:utils/observablemixin~ObservableMixin} changes.
function syncValueSchemaValue(schema, updater, { node }) {
    const values = getValueSchemaValue(schema, node);
    let value;
    // Check if schema is a single Template.bind.if, like:
    //
    //		class: Template.bind.if( 'foo' )
    //
    if (schema.length == 1 && schema[0] instanceof TemplateIfBinding) {
        value = values[0];
    }
    else {
        value = values.reduce(arrayValueReducer, '');
    }
    if (isFalsy(value)) {
        updater.remove();
    }
    else {
        updater.set(value);
    }
}
// Returns an object consisting of `set` and `remove` functions, which
// can be used in the context of DOM Node to set or reset `textContent`.
// @see module:ui/view~View#_bindToObservable
//
// @param {Node} node DOM Node to be modified.
// @returns {Object}
function getTextUpdater(node) {
    return {
        set(value) {
            node.textContent = value;
        },
        remove() {
            node.textContent = '';
        }
    };
}
// Returns an object consisting of `set` and `remove` functions, which
// can be used in the context of DOM Node to set or reset an attribute.
// @see module:ui/view~View#_bindToObservable
//
// @param {Node} node DOM Node to be modified.
// @param {String} attrName Name of the attribute to be modified.
// @param {String} [ns=null] Namespace to use.
// @returns {Object}
function getAttributeUpdater(el, attrName, ns) {
    return {
        set(value) {
            el.setAttributeNS(ns, attrName, value);
        },
        remove() {
            el.removeAttributeNS(ns, attrName);
        }
    };
}
// Returns an object consisting of `set` and `remove` functions, which
// can be used in the context of CSSStyleDeclaration to set or remove a style.
// @see module:ui/view~View#_bindToObservable
//
// @param {Node} node DOM Node to be modified.
// @param {String} styleName Name of the style to be modified.
// @returns {Object}
function getStyleUpdater(el, styleName) {
    return {
        set(value) {
            el.style[styleName] = value;
        },
        remove() {
            el.style[styleName] = null;
        }
    };
}
// Clones definition of the template.
//
// @param {module:ui/template~TemplateDefinition} def
// @returns {module:ui/template~TemplateDefinition}
function template_clone(def) {
    const clone = lodash_es_cloneDeepWith(def, value => {
        // Don't clone the `Template.bind`* bindings because of the references to Observable
        // and DomEmitterMixin instances inside, which would also be traversed and cloned by greedy
        // cloneDeepWith algorithm. There's no point in cloning Observable/DomEmitterMixins
        // along with the definition.
        //
        // Don't clone Template instances if provided as a child. They're simply #render()ed
        // and nothing should interfere.
        //
        // Also don't clone View instances if provided as a child of the Template. The template
        // instance will be extracted from the View during the normalization and there's no need
        // to clone it.
        if (value && (value instanceof TemplateBinding || isTemplate(value) || isView(value) || isViewCollection(value))) {
            return value;
        }
    });
    return clone;
}
// Normalizes given {@link module:ui/template~TemplateDefinition}.
//
// See:
//  * {@link normalizeAttributes}
//  * {@link normalizeListeners}
//  * {@link normalizePlainTextDefinition}
//  * {@link normalizeTextDefinition}
//
// @param {module:ui/template~TemplateDefinition} def
// @returns {module:ui/template~TemplateDefinition} Normalized definition.
function template_normalize(def) {
    if (typeof def == 'string') {
        def = normalizePlainTextDefinition(def);
    }
    else if (def.text) {
        normalizeTextDefinition(def);
    }
    if (def.on) {
        def.eventListeners = normalizeListeners(def.on);
        // Template mixes EmitterMixin, so delete #on to avoid collision.
        delete def.on;
    }
    if (!def.text) {
        if (def.attributes) {
            normalizeAttributes(def.attributes);
        }
        const children = [];
        if (def.children) {
            if (isViewCollection(def.children)) {
                children.push(def.children);
            }
            else {
                for (const child of def.children) {
                    if (isTemplate(child) || isView(child) || isNode(child)) {
                        children.push(child);
                    }
                    else {
                        children.push(new Template(child));
                    }
                }
            }
        }
        def.children = children;
    }
    return def;
}
// Normalizes "attributes" section of {@link module:ui/template~TemplateDefinition}.
//
//		attributes: {
//			a: 'bar',
//			b: {@link module:ui/template~TemplateBinding},
//			c: {
//				value: 'bar'
//			}
//		}
//
// becomes
//
//		attributes: {
//			a: [ 'bar' ],
//			b: [ {@link module:ui/template~TemplateBinding} ],
//			c: {
//				value: [ 'bar' ]
//			}
//		}
//
// @param {Object} attributes
function normalizeAttributes(attributes) {
    for (const a in attributes) {
        if (attributes[a].value) {
            attributes[a].value = toArray(attributes[a].value);
        }
        arrayify(attributes, a);
    }
}
// Normalizes "on" section of {@link module:ui/template~TemplateDefinition}.
//
//		on: {
//			a: 'bar',
//			b: {@link module:ui/template~TemplateBinding},
//			c: [ {@link module:ui/template~TemplateBinding}, () => { ... } ]
//		}
//
// becomes
//
//		on: {
//			a: [ 'bar' ],
//			b: [ {@link module:ui/template~TemplateBinding} ],
//			c: [ {@link module:ui/template~TemplateBinding}, () => { ... } ]
//		}
//
// @param {Object} listeners
// @returns {Object} Object containing normalized listeners.
function normalizeListeners(listeners) {
    for (const l in listeners) {
        arrayify(listeners, l);
    }
    return listeners;
}
// Normalizes "string" {@link module:ui/template~TemplateDefinition}.
//
//		"foo"
//
// becomes
//
//		{ text: [ 'foo' ] },
//
// @param {String} def
// @returns {module:ui/template~TemplateDefinition} Normalized template definition.
function normalizePlainTextDefinition(def) {
    return {
        text: [def]
    };
}
// Normalizes text {@link module:ui/template~TemplateDefinition}.
//
//		children: [
//			{ text: 'def' },
//			{ text: {@link module:ui/template~TemplateBinding} }
//		]
//
// becomes
//
//		children: [
//			{ text: [ 'def' ] },
//			{ text: [ {@link module:ui/template~TemplateBinding} ] }
//		]
//
// @param {module:ui/template~TemplateDefinition} def
function normalizeTextDefinition(def) {
    def.text = toArray(def.text);
}
// Wraps an entry in Object in an Array, if not already one.
//
//		{
//			x: 'y',
//			a: [ 'b' ]
//		}
//
// becomes
//
//		{
//			x: [ 'y' ],
//			a: [ 'b' ]
//		}
//
// @param {Object} obj
// @param {String} key
function arrayify(obj, key) {
    obj[key] = toArray(obj[key]);
}
// A helper which concatenates the value avoiding unwanted
// leading white spaces.
//
// @param {String} prev
// @param {String} cur
// @returns {String}
function arrayValueReducer(prev, cur) {
    if (isFalsy(cur)) {
        return prev;
    }
    else if (isFalsy(prev)) {
        return cur;
    }
    else {
        return `${prev} ${cur}`;
    }
}
// Extends one object defined in the following format:
//
//		{
//			key1: [Array1],
//			key2: [Array2],
//			...
//			keyN: [ArrayN]
//		}
//
// with another object of the same data format.
//
// @param {Object} obj Base object.
// @param {Object} ext Object extending base.
// @returns {String}
function extendObjectValueArray(obj, ext) {
    for (const a in ext) {
        if (obj[a]) {
            obj[a].push(...ext[a]);
        }
        else {
            obj[a] = ext[a];
        }
    }
}
// A helper for {@link module:ui/template~Template#extend}. Recursively extends {@link module:ui/template~Template} instance
// with content from {@link module:ui/template~TemplateDefinition}. See {@link module:ui/template~Template#extend} to learn more.
//
// @param {module:ui/template~Template} def A template instance to be extended.
// @param {module:ui/template~TemplateDefinition} def A definition which is to extend the template instance.
// @param {Object} Error context.
function extendTemplate(template, def) {
    if (def.attributes) {
        if (!template.attributes) {
            template.attributes = {};
        }
        extendObjectValueArray(template.attributes, def.attributes);
    }
    if (def.eventListeners) {
        if (!template.eventListeners) {
            template.eventListeners = {};
        }
        extendObjectValueArray(template.eventListeners, def.eventListeners);
    }
    if (def.text) {
        template.text.push(...def.text);
    }
    if (def.children && def.children.length) {
        if (template.children.length != def.children.length) {
            /**
             * The number of children in extended definition does not match.
             *
             * @error ui-template-extend-children-mismatch
             */
            throw new CKEditorError('ui-template-extend-children-mismatch', template);
        }
        let childIndex = 0;
        for (const childDef of def.children) {
            extendTemplate(template.children[childIndex++], childDef);
        }
    }
}
// Checks if value is "falsy".
// Note: 0 (Number) is not "falsy" in this context.
//
// @private
// @param {*} value Value to be checked.
function isFalsy(value) {
    return !value && value !== 0;
}
// Checks if the item is an instance of {@link module:ui/view~View}
//
// @private
// @param {*} value Value to be checked.
function isView(item) {
    return item instanceof src_view_View;
}
// Checks if the item is an instance of {@link module:ui/template~Template}
//
// @private
// @param {*} value Value to be checked.
function isTemplate(item) {
    return item instanceof Template;
}
// Checks if the item is an instance of {@link module:ui/viewcollection~ViewCollection}
//
// @private
// @param {*} value Value to be checked.
function isViewCollection(item) {
    return item instanceof ViewCollection;
}
// Checks if value array contains the one with namespace.
function isNamespaced(attrValue) {
    return lodash_es_isObject(attrValue[0]) && attrValue[0].ns;
}
// Creates an empty skeleton for {@link module:ui/template~Template#revert}
// data.
//
// @private
function getEmptyRevertData() {
    return {
        children: [],
        bindings: [],
        attributes: {}
    };
}
// Checks whether an attribute should be extended when
// {@link module:ui/template~Template#apply} is called.
//
// @private
// @param {String} attrName Attribute name to check.
function shouldExtend(attrName) {
    return attrName == 'class' || attrName == 'style';
}
/**
 * Tells {@link module:ui/template~Template#_renderNode} to render
 * children into `DocumentFragment` first and then append the fragment
 * to the parent element. It is a speed optimization.
 *
 * @member {Boolean} #intoFragment
 */
/**
 * A node which is being rendered.
 *
 * @member {HTMLElement|Text} #node
 */
/**
 * Indicates whether the {@module:ui/template~RenderNodeOptions#node} has
 * been provided by {@module:ui/template~Template#apply}.
 *
 * @member {Boolean} #isApplying
 */
/**
 * An object storing the data that helps {@module:ui/template~Template#revert}
 * bringing back an element to its initial state, i.e. before
 * {@module:ui/template~Template#apply} was called.
 *
 * @member {Object} #revertData
 */

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/globals/globals.css
var globals = __webpack_require__(6150);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/globals/globals.css

            

var globals_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

globals_options.insert = "head";
globals_options.singleton = true;

var globals_update = injectStylesIntoStyleTag_default()(globals/* default */.Z, globals_options);



/* harmony default export */ const globals_globals = (globals/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/view.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable @typescript-eslint/no-invalid-void-type, new-cap */
/**
 * @module ui/view
 */








/**
 * The basic view class, which represents an HTML element created out of a
 * {@link module:ui/view~View#template}. Views are building blocks of the user interface and handle
 * interaction
 *
 * Views {@link module:ui/view~View#registerChild aggregate} children in
 * {@link module:ui/view~View#createCollection collections} and manage the life cycle of DOM
 * listeners e.g. by handling rendering and destruction.
 *
 * See the {@link module:ui/template~TemplateDefinition} syntax to learn more about shaping view
 * elements, attributes and listeners.
 *
 *		class SampleView extends View {
 *			constructor( locale ) {
 *				super( locale );
 *
 *				const bind = this.bindTemplate;
 *
 *				// Views define their interface (state) using observable attributes.
 *				this.set( 'elementClass', 'bar' );
 *
 *				this.setTemplate( {
 *					tag: 'p',
 *
 *					// The element of the view can be defined with its children.
 *					children: [
 *						'Hello',
 *						{
 *							tag: 'b',
 *							children: [ 'world!' ]
 *						}
 *					],
 *					attributes: {
 *						class: [
 *							'foo',
 *
 *							// Observable attributes control the state of the view in DOM.
 *							bind.to( 'elementClass' )
 *						]
 *					},
 *					on: {
 *						// Views listen to DOM events and propagate them.
 *						click: bind.to( 'clicked' )
 *					}
 *				} );
 *			}
 *		}
 *
 *		const view = new SampleView( locale );
 *
 *		view.render();
 *
 *		// Append <p class="foo bar">Hello<b>world</b></p> to the <body>
 *		document.body.appendChild( view.element );
 *
 *		// Change the class attribute to <p class="foo baz">Hello<b>world</b></p>
 *		view.elementClass = 'baz';
 *
 *		// Respond to the "click" event in DOM by executing a custom action.
 *		view.on( 'clicked', () => {
 *			console.log( 'The view has been clicked!' );
 *		} );
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class src_view_View extends DomEmitterMixin(Observable) {
    /**
     * Creates an instance of the {@link module:ui/view~View} class.
     *
     * Also see {@link #render}.
     *
     * @param {module:utils/locale~Locale} [locale] The localization services instance.
     */
    constructor(locale) {
        super();
        /**
         * An HTML element of the view. `null` until {@link #render rendered}
         * from the {@link #template}.
         *
         *		class SampleView extends View {
         *			constructor() {
         *				super();
         *
         *				// A template instance the #element will be created from.
         *				this.setTemplate( {
         *					tag: 'p'
         *
         *					// ...
         *				} );
         *			}
         *		}
         *
         *		const view = new SampleView();
         *
         *		// Renders the #template.
         *		view.render();
         *
         *		// Append the HTML element of the view to <body>.
         *		document.body.appendChild( view.element );
         *
         * **Note**: The element of the view can also be assigned directly:
         *
         *		view.element = document.querySelector( '#my-container' );
         *
         * @member {HTMLElement}
         */
        this.element = null;
        /**
         * Set `true` when the view has already been {@link module:ui/view~View#render rendered}.
         *
         * @readonly
         * @member {Boolean} #isRendered
         */
        this.isRendered = false;
        /**
         * A set of tools to localize the user interface.
         *
         * Also see {@link module:core/editor/editor~Editor#locale}.
         *
         * @readonly
         * @member {module:utils/locale~Locale}
         */
        this.locale = locale;
        /**
         * Shorthand for {@link module:utils/locale~Locale#t}.
         *
         * Note: If {@link #locale} instance hasn't been passed to the view this method may not
         * be available.
         *
         * @see module:utils/locale~Locale#t
         * @method
         */
        this.t = locale && locale.t;
        /**
         * Collections registered with {@link #createCollection}.
         *
         * @protected
         * @member {Set.<module:ui/viewcollection~ViewCollection>}
         */
        this._viewCollections = new Collection();
        /**
         * A collection of view instances, which have been added directly
         * into the {@link module:ui/template~Template#children}.
         *
         * @protected
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this._unboundChildren = this.createCollection();
        // Pass parent locale to its children.
        this._viewCollections.on('add', (evt, collection) => {
            collection.locale = locale;
            collection.t = locale && locale.t;
        });
        /**
         * Template of this view. It provides the {@link #element} representing
         * the view in DOM, which is {@link #render rendered}.
         *
         * @member {module:ui/template~Template} #template
         */
        /**
         * Cached {@link module:ui/template~BindChain bind chain} object created by the
         * {@link #template}. See {@link #bindTemplate}.
         *
         * @private
         * @member {Object} #_bindTemplate
         */
        this.decorate('render');
    }
    /**
     * Shorthand for {@link module:ui/template~Template.bind}, a binding
     * {@link module:ui/template~BindChain interface} pre–configured for the view instance.
     *
     * It provides {@link module:ui/template~BindChain#to `to()`} and
     * {@link module:ui/template~BindChain#if `if()`} methods that initialize bindings with
     * observable attributes and attach DOM listeners.
     *
     *		class SampleView extends View {
     *			constructor( locale ) {
     *				super( locale );
     *
     *				const bind = this.bindTemplate;
     *
     *				// These {@link module:utils/observablemixin~Observable observable} attributes will control
     *				// the state of the view in DOM.
     *				this.set( {
     *					elementClass: 'foo',
     *				 	isEnabled: true
     *				 } );
     *
     *				this.setTemplate( {
     *					tag: 'p',
     *
     *					attributes: {
     *						// The class HTML attribute will follow elementClass
     *						// and isEnabled view attributes.
     *						class: [
     *							bind.to( 'elementClass' )
     *							bind.if( 'isEnabled', 'present-when-enabled' )
     *						]
     *					},
     *
     *					on: {
     *						// The view will fire the "clicked" event upon clicking <p> in DOM.
     *						click: bind.to( 'clicked' )
     *					}
     *				} );
     *			}
     *		}
     *
     * @method #bindTemplate
     */
    get bindTemplate() {
        if (this._bindTemplate) {
            return this._bindTemplate;
        }
        return (this._bindTemplate = Template.bind(this, this));
    }
    /**
     * Creates a new collection of views, which can be used as
     * {@link module:ui/template~Template#children} of this view.
     *
     *		class SampleView extends View {
     *			constructor( locale ) {
     *				super( locale );
     *
     *				const child = new ChildView( locale );
     *				this.items = this.createCollection( [ child ] );
     *
     *				this.setTemplate( {
     *					tag: 'p',
     *
     *					// `items` collection will render here.
     *					children: this.items
     *				} );
     *			}
     *		}
     *
     *		const view = new SampleView( locale );
     *		view.render();
     *
     *		// It will append <p><child#element></p> to the <body>.
     *		document.body.appendChild( view.element );
     *
     * @param {Iterable.<module:ui/view~View>} [views] Initial views of the collection.
     * @returns {module:ui/viewcollection~ViewCollection} A new collection of view instances.
     */
    createCollection(views) {
        const collection = new ViewCollection(views);
        this._viewCollections.add(collection);
        return collection;
    }
    /**
     * Registers a new child view under the view instance. Once registered, a child
     * view is managed by its parent, including {@link #render rendering}
     * and {@link #destroy destruction}.
     *
     * To revert this, use {@link #deregisterChild}.
     *
     *		class SampleView extends View {
     *			constructor( locale ) {
     *				super( locale );
     *
     *				this.childA = new SomeChildView( locale );
     *				this.childB = new SomeChildView( locale );
     *
     *				this.setTemplate( { tag: 'p' } );
     *
     *				// Register the children.
     *				this.registerChild( [ this.childA, this.childB ] );
     *			}
     *
     *			render() {
     *				super.render();
     *
     *				this.element.appendChild( this.childA.element );
     *				this.element.appendChild( this.childB.element );
     *			}
     *		}
     *
     *		const view = new SampleView( locale );
     *
     *		view.render();
     *
     *		// Will append <p><childA#element><b></b><childB#element></p>.
     *		document.body.appendChild( view.element );
     *
     * **Note**: There's no need to add child views if they're already referenced in the
     * {@link #template}:
     *
     *		class SampleView extends View {
     *			constructor( locale ) {
     *				super( locale );
     *
     *				this.childA = new SomeChildView( locale );
     *				this.childB = new SomeChildView( locale );
     *
     *				this.setTemplate( {
     *					tag: 'p',
     *
     *					// These children will be added automatically. There's no
     *					// need to call {@link #registerChild} for any of them.
     *					children: [ this.childA, this.childB ]
     *				} );
     *			}
     *
     *			// ...
     *		}
     *
     * @param {module:ui/view~View|Iterable.<module:ui/view~View>} children Children views to be registered.
     */
    registerChild(children) {
        if (!isIterable(children)) {
            children = [children];
        }
        for (const child of children) {
            this._unboundChildren.add(child);
        }
    }
    /**
     * The opposite of {@link #registerChild}. Removes a child view from this view instance.
     * Once removed, the child is no longer managed by its parent, e.g. it can safely
     * become a child of another parent view.
     *
     * @see #registerChild
     * @param {module:ui/view~View|Iterable.<module:ui/view~View>} children Child views to be removed.
     */
    deregisterChild(children) {
        if (!isIterable(children)) {
            children = [children];
        }
        for (const child of children) {
            this._unboundChildren.remove(child);
        }
    }
    /**
     * Sets the {@link #template} of the view with with given definition.
     *
     * A shorthand for:
     *
     *		view.setTemplate( definition );
     *
     * @param {module:ui/template~TemplateDefinition} definition Definition of view's template.
     */
    setTemplate(definition) {
        this.template = new Template(definition);
    }
    /**
     * {@link module:ui/template~Template.extend Extends} the {@link #template} of the view with
     * with given definition.
     *
     * A shorthand for:
     *
     *		Template.extend( view.template, definition );
     *
     * **Note**: Is requires the {@link #template} to be already set. See {@link #setTemplate}.
     *
     * @param {module:ui/template~TemplateDefinition} definition Definition which
     * extends the {@link #template}.
     */
    extendTemplate(definition) {
        Template.extend(this.template, definition);
    }
    /**
     * Recursively renders the view.
     *
     * Once the view is rendered:
     * * the {@link #element} becomes an HTML element out of {@link #template},
     * * the {@link #isRendered} flag is set `true`.
     *
     * **Note**: The children of the view:
     * * defined directly in the {@link #template}
     * * residing in collections created by the {@link #createCollection} method,
     * * and added by {@link #registerChild}
     * are also rendered in the process.
     *
     * In general, `render()` method is the right place to keep the code which refers to the
     * {@link #element} and should be executed at the very beginning of the view's life cycle.
     *
     * It is possible to {@link module:ui/template~Template.extend} the {@link #template} before
     * the view is rendered. To allow an early customization of the view (e.g. by its parent),
     * such references should be done in `render()`.
     *
     *		class SampleView extends View {
     *			constructor() {
     *				this.setTemplate( {
     *					// ...
     *				} );
     *			},
     *
     *			render() {
     *				// View#element becomes available.
     *				super.render();
     *
     *				// The "scroll" listener depends on #element.
     *				this.listenTo( window, 'scroll', () => {
     *					// A reference to #element would render the #template and make it non-extendable.
     *					if ( window.scrollY > 0 ) {
     *						this.element.scrollLeft = 100;
     *					} else {
     *						this.element.scrollLeft = 0;
     *					}
     *				} );
     *			}
     *		}
     *
     *		const view = new SampleView();
     *
     *		// Let's customize the view before it gets rendered.
     *		view.extendTemplate( {
     *			attributes: {
     *				class: [
     *					'additional-class'
     *				]
     *			}
     *		} );
     *
     *		// Late rendering allows customization of the view.
     *		view.render();
     */
    render() {
        if (this.isRendered) {
            /**
             * This View has already been rendered.
             *
             * @error ui-view-render-already-rendered
             */
            throw new CKEditorError('ui-view-render-already-rendered', this);
        }
        // Render #element of the view.
        if (this.template) {
            this.element = this.template.render();
            // Auto–register view children from #template.
            this.registerChild(this.template.getViews());
        }
        this.isRendered = true;
    }
    /**
     * Recursively destroys the view instance and child views added by {@link #registerChild} and
     * residing in collections created by the {@link #createCollection}.
     *
     * Destruction disables all event listeners:
     * * created on the view, e.g. `view.on( 'event', () => {} )`,
     * * defined in the {@link #template} for DOM events.
     */
    destroy() {
        this.stopListening();
        this._viewCollections.map(c => c.destroy());
        // Template isn't obligatory for views.
        if (this.template && this.template._revertData) {
            this.template.revert(this.element);
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/getpositionedancestor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/getpositionedancestor
 */

/**
 * For a given element, returns the nearest ancestor element which CSS position is not "static".
 *
 * @param {HTMLElement} [element] The native DOM element to be checked.
 * @returns {HTMLElement|null}
 */
function getPositionedAncestor(element) {
    if (!element || !element.parentNode) {
        return null;
    }
    if (element.offsetParent === dom_global.document.body) {
        return null;
    }
    return element.offsetParent;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/position.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/position
 */





// @if CK_DEBUG_POSITION // import { RectDrawer } from '@ckeditor/ckeditor5-minimap/src/utils';
/**
 * Calculates the `position: absolute` coordinates of a given element so it can be positioned with respect to the
 * target in the visually most efficient way, taking various restrictions like viewport or limiter geometry
 * into consideration.
 *
 *		// The element which is to be positioned.
 *		const element = document.body.querySelector( '#toolbar' );
 *
 *		// A target to which the element is positioned relatively.
 *		const target = document.body.querySelector( '#container' );
 *
 *		// Finding the optimal coordinates for the positioning.
 *		const { left, top, name } = getOptimalPosition( {
 *			element: element,
 *			target: target,
 *
 * 			// The algorithm will chose among these positions to meet the requirements such
 * 			// as "limiter" element or "fitInViewport", set below. The positions are considered
 * 			// in the order of the array.
 *			positions: [
 *				//
 *			 	//	[ Target ]
 *				//	+-----------------+
 *				//	|     Element     |
 *				//	+-----------------+
 *				//
 *				targetRect => ( {
 *					top: targetRect.bottom,
 *					left: targetRect.left,
 *					name: 'mySouthEastPosition'
 *				} ),
 *
 *				//
 *				//	+-----------------+
 *				//	|     Element     |
 *				//	+-----------------+
 *				//	[ Target ]
 *				//
 *				( targetRect, elementRect ) => ( {
 *					top: targetRect.top - elementRect.height,
 *					left: targetRect.left,
 *					name: 'myNorthEastPosition'
 *				} )
 *			],
 *
 *			// Find a position such guarantees the element remains within visible boundaries of <body>.
 *			limiter: document.body,
 *
 *			// Find a position such guarantees the element remains within visible boundaries of the browser viewport.
 *			fitInViewport: true
 *		} );
 *
 *		// The best position which fits into document.body and the viewport. May be useful
 *		// to set proper class on the `element`.
 *		console.log( name ); // -> "myNorthEastPosition"
 *
 *		// Using the absolute coordinates which has been found to position the element
 *		// as in the diagram depicting the "myNorthEastPosition" position.
 *		element.style.top = top;
 *		element.style.left = left;
 *
 * @param {module:utils/dom/position~Options} options The input data and configuration of the helper.
 * @returns {module:utils/dom/position~Position}
 */
function position_getOptimalPosition({ element, target, positions, limiter, fitInViewport, viewportOffsetConfig }) {
    // If the {@link module:utils/dom/position~Options#target} is a function, use what it returns.
    // https://github.com/ckeditor/ckeditor5-utils/issues/157
    if (lodash_es_isFunction(target)) {
        target = target();
    }
    // If the {@link module:utils/dom/position~Options#limiter} is a function, use what it returns.
    // https://github.com/ckeditor/ckeditor5-ui/issues/260
    if (lodash_es_isFunction(limiter)) {
        limiter = limiter();
    }
    const positionedElementAncestor = getPositionedAncestor(element);
    const elementRect = new rect_Rect(element);
    const targetRect = new rect_Rect(target);
    let bestPosition;
    // @if CK_DEBUG_POSITION // RectDrawer.clear();
    // @if CK_DEBUG_POSITION // RectDrawer.draw( targetRect, { outlineWidth: '5px' }, 'Target' );
    const viewportRect = fitInViewport && getConstrainedViewportRect(viewportOffsetConfig) || null;
    const positionOptions = { targetRect, elementRect, positionedElementAncestor, viewportRect };
    // If there are no limits, just grab the very first position and be done with that drama.
    if (!limiter && !fitInViewport) {
        bestPosition = new PositionObject(positions[0], positionOptions);
    }
    else {
        const limiterRect = limiter && new rect_Rect(limiter).getVisible();
        // @if CK_DEBUG_POSITION // if ( viewportRect ) {
        // @if CK_DEBUG_POSITION //		RectDrawer.draw( viewportRect, { outlineWidth: '5px' }, 'Viewport' );
        // @if CK_DEBUG_POSITION // }
        // @if CK_DEBUG_POSITION // if ( limiter ) {
        // @if CK_DEBUG_POSITION // 	RectDrawer.draw( limiterRect, { outlineWidth: '5px', outlineColor: 'green' }, 'Visible limiter' );
        // @if CK_DEBUG_POSITION // }
        Object.assign(positionOptions, { limiterRect, viewportRect });
        // If there's no best position found, i.e. when all intersections have no area because
        // rects have no width or height, then just use the first available position.
        bestPosition = getBestPosition(positions, positionOptions) || new PositionObject(positions[0], positionOptions);
    }
    return bestPosition;
}
// Returns a viewport `Rect` shrunk by the viewport offset config from all sides.
//
// @private
// @param {Object} An object containing viewportOffset config.
// @returns {module:utils/dom/rect~Rect} A shrunken rect of the viewport.
function getConstrainedViewportRect(viewportOffsetConfig) {
    viewportOffsetConfig = Object.assign({ top: 0, bottom: 0, left: 0, right: 0 }, viewportOffsetConfig);
    const viewportRect = new rect_Rect(dom_global.window);
    viewportRect.top += viewportOffsetConfig.top;
    viewportRect.height -= viewportOffsetConfig.top;
    viewportRect.bottom -= viewportOffsetConfig.bottom;
    viewportRect.height -= viewportOffsetConfig.bottom;
    return viewportRect;
}
// For a given array of positioning functions, returns such that provides the best
// fit of the `elementRect` into the `limiterRect` and `viewportRect`.
//
// @private
//
// @param {module:utils/dom/position~Options#positions} positions Functions returning
// {@link module:utils/dom/position~Position}to be checked, in the order of preference.
// @param {Object} options
// @param {module:utils/dom/rect~Rect} options.elementRect The positioned element rect.
// @param {module:utils/dom/rect~Rect} options.targetRect The target element rect.
// @param {module:utils/dom/rect~Rect} options.viewportRect The viewport rect.
// @param {module:utils/dom/rect~Rect} [options.limiterRect] The limiter rect.
// @param {HTMLElement|null} [options.positionedElementAncestor] Nearest element ancestor element which CSS position is not "static".
//
// @returns {module:utils/dom/position~Position|null} An array containing the name of the position and it's rect.
function getBestPosition(positions, options) {
    const { elementRect } = options;
    // This is when element is fully visible.
    const elementRectArea = elementRect.getArea();
    const positionInstances = positions
        .map(positioningFunction => new PositionObject(positioningFunction, options))
        // Some positioning functions may return `null` if they don't want to participate.
        .filter(position => !!position.name);
    let maxFitFactor = 0;
    let bestPosition = null;
    for (const position of positionInstances) {
        const { limiterIntersectionArea, viewportIntersectionArea } = position;
        // If a such position is found that element is fully contained by the limiter then, obviously,
        // there will be no better one, so finishing.
        if (limiterIntersectionArea === elementRectArea) {
            return position;
        }
        // To maximize both viewport and limiter intersection areas we use distance on _viewportIntersectionArea
        // and _limiterIntersectionArea plane (without sqrt because we are looking for max value).
        const fitFactor = viewportIntersectionArea ** 2 + limiterIntersectionArea ** 2;
        if (fitFactor > maxFitFactor) {
            maxFitFactor = fitFactor;
            bestPosition = position;
        }
    }
    return bestPosition;
}
// For a given absolute Rect coordinates object and a positioned element ancestor, it updates its
// coordinates that make up for the position and the scroll of the ancestor.
//
// This is necessary because while Rects (and DOMRects) are relative to the browser's viewport, their coordinates
// are used in real–life to position elements with `position: absolute`, which are scoped by any positioned
// (and scrollable) ancestors.
//
// @private
//
// @param {module:utils/dom/rect~Rect} rect A rect with absolute rect coordinates.
// @param {HTMLElement} positionedElementAncestor An ancestor element that should be considered.
function shiftRectToCompensatePositionedAncestor(rect, positionedElementAncestor) {
    const ancestorPosition = getRectForAbsolutePositioning(new rect_Rect(positionedElementAncestor));
    const ancestorBorderWidths = getBorderWidths(positionedElementAncestor);
    let moveX = 0;
    let moveY = 0;
    // (https://github.com/ckeditor/ckeditor5-ui-default/issues/126)
    // If there's some positioned ancestor of the panel, then its `Rect` must be taken into
    // consideration. `Rect` is always relative to the viewport while `position: absolute` works
    // with respect to that positioned ancestor.
    moveX -= ancestorPosition.left;
    moveY -= ancestorPosition.top;
    // (https://github.com/ckeditor/ckeditor5-utils/issues/139)
    // If there's some positioned ancestor of the panel, not only its position must be taken into
    // consideration (see above) but also its internal scrolls. Scroll have an impact here because `Rect`
    // is relative to the viewport (it doesn't care about scrolling), while `position: absolute`
    // must compensate that scrolling.
    moveX += positionedElementAncestor.scrollLeft;
    moveY += positionedElementAncestor.scrollTop;
    // (https://github.com/ckeditor/ckeditor5-utils/issues/139)
    // If there's some positioned ancestor of the panel, then its `Rect` includes its CSS `borderWidth`
    // while `position: absolute` positioning does not consider it.
    // E.g. `{ position: absolute, top: 0, left: 0 }` means upper left corner of the element,
    // not upper-left corner of its border.
    moveX -= ancestorBorderWidths.left;
    moveY -= ancestorBorderWidths.top;
    rect.moveBy(moveX, moveY);
}
// DOMRect (also Rect) works in a scroll–independent geometry but `position: absolute` doesn't.
// This function converts Rect to `position: absolute` coordinates.
//
// @private
// @param {module:utils/dom/rect~Rect} rect A rect to be converted.
// @returns {module:utils/dom/rect~Rect} Object containing `left` and `top` properties, in absolute coordinates.
function getRectForAbsolutePositioning(rect) {
    const { scrollX, scrollY } = dom_global.window;
    return rect.clone().moveBy(scrollX, scrollY);
}
// A position class which instances are created and used by the {@link module:utils/dom/position~getOptimalPosition} helper.
//
// {@link module:utils/dom/position~Position#top} and {@link module:utils/dom/position~Position#left} properties of the position instance
// translate directly to the `top` and `left` properties in CSS "`position: absolute` coordinate system". If set on the positioned element
// in DOM, they will make it display it in the right place in the viewport.
// @private
// @implements {Position}
class PositionObject {
    // Creates an instance of the {@link module:utils/dom/position~PositionObject} class.
    //
    // @param {module:utils/dom/position~PositioningFunction} positioningFunction function The function that defines the expected
    // coordinates the positioned element should move to.
    // @param {Object} [options] options object.
    // @param {module:utils/dom/rect~Rect} options.elementRect The positioned element rect.
    // @param {module:utils/dom/rect~Rect} options.targetRect The target element rect.
    // @param {module:utils/dom/rect~Rect|null} options.viewportRect The viewport rect.
    // @param {module:utils/dom/rect~Rect} [options.limiterRect] The limiter rect.
    // @param {HTMLElement|null} [options.positionedElementAncestor] Nearest element ancestor element which CSS position is not "static".
    constructor(positioningFunction, options) {
        const positioningFunctionOutput = positioningFunction(options.targetRect, options.elementRect, options.viewportRect);
        // Nameless position for a function that didn't participate.
        if (!positioningFunctionOutput) {
            return;
        }
        const { left, top, name, config } = positioningFunctionOutput;
        this.name = name;
        this.config = config;
        this._positioningFunctionCorrdinates = { left, top };
        this._options = options;
    }
    // The left value in pixels in the CSS `position: absolute` coordinate system.
    // Set it on the positioned element in DOM to move it to the position.
    //
    // @readonly
    // @type {Number}
    get left() {
        return this._absoluteRect.left;
    }
    // The top value in pixels in the CSS `position: absolute` coordinate system.
    // Set it on the positioned element in DOM to move it to the position.
    //
    // @readonly
    // @type {Number}
    get top() {
        return this._absoluteRect.top;
    }
    // An intersection area between positioned element and limiter within viewport constraints.
    //
    // @readonly
    // @type {Number}
    get limiterIntersectionArea() {
        const limiterRect = this._options.limiterRect;
        if (limiterRect) {
            const viewportRect = this._options.viewportRect;
            if (viewportRect) {
                // Consider only the part of the limiter which is visible in the viewport. So the limiter is getting limited.
                const limiterViewportIntersectRect = limiterRect.getIntersection(viewportRect);
                if (limiterViewportIntersectRect) {
                    // If the limiter is within the viewport, then check the intersection between that part of the
                    // limiter and actual position.
                    return limiterViewportIntersectRect.getIntersectionArea(this._rect);
                }
            }
            else {
                return limiterRect.getIntersectionArea(this._rect);
            }
        }
        return 0;
    }
    // An intersection area between positioned element and viewport.
    //
    // @readonly
    // @type {Number}
    get viewportIntersectionArea() {
        const viewportRect = this._options.viewportRect;
        if (viewportRect) {
            return viewportRect.getIntersectionArea(this._rect);
        }
        return 0;
    }
    // An already positioned element rect. A clone of the element rect passed to the constructor
    // but placed in the viewport according to the positioning function.
    //
    // @private
    // @readonly
    // @type {module:utils/dom/rect~Rect}
    get _rect() {
        if (this._cachedRect) {
            return this._cachedRect;
        }
        this._cachedRect = this._options.elementRect.clone().moveTo(this._positioningFunctionCorrdinates.left, this._positioningFunctionCorrdinates.top);
        return this._cachedRect;
    }
    // An already absolutely positioned element rect. See ({@link #_rect}).
    //
    // @private
    // @readonly
    // @type {module:utils/dom/rect~Rect}
    get _absoluteRect() {
        if (this._cachedAbsoluteRect) {
            return this._cachedAbsoluteRect;
        }
        this._cachedAbsoluteRect = getRectForAbsolutePositioning(this._rect);
        if (this._options.positionedElementAncestor) {
            shiftRectToCompensatePositionedAncestor(this._cachedAbsoluteRect, this._options.positionedElementAncestor);
        }
        return this._cachedAbsoluteRect;
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/balloonpanel.css
var balloonpanel = __webpack_require__(8245);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/balloonpanel.css

            

var balloonpanel_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

balloonpanel_options.insert = "head";
balloonpanel_options.singleton = true;

var balloonpanel_update = injectStylesIntoStyleTag_default()(balloonpanel/* default */.Z, balloonpanel_options);



/* harmony default export */ const panel_balloonpanel = (balloonpanel/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/panel/balloon/balloonpanelview
 */







const toPx = toUnit('px');
const defaultLimiterElement = dom_global.document.body;
/**
 * The balloon panel view class.
 *
 * A floating container which can
 * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView#pin pin} to any
 * {@link module:utils/dom/position~Options#target target} in the DOM and remain in that position
 * e.g. when the web page is scrolled.
 *
 * The balloon panel can be used to display contextual, non-blocking UI like forms, toolbars and
 * the like in its {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView#content} view
 * collection.
 *
 * There is a number of {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}
 * that the balloon can use, automatically switching from one to another when the viewport space becomes
 * scarce to keep the balloon visible to the user as long as it is possible. The balloon will also
 * accept any custom position set provided by the user compatible with the
 * {@link module:utils/dom/position~Options options}.
 *
 *		const panel = new BalloonPanelView( locale );
 *		const childView = new ChildView();
 *		const positions = BalloonPanelView.defaultPositions;
 *
 *		panel.render();
 *
 *		// Add a child view to the panel's content collection.
 *		panel.content.add( childView );
 *
 *		// Start pinning the panel to an element with the "target" id DOM.
 *		// The balloon will remain pinned until unpin() is called.
 *		panel.pin( {
 *			target: document.querySelector( '#target' ),
 *			positions: [
 *				positions.northArrowSouth,
 *				positions.southArrowNorth
 *			]
 *		} );
 *
 * @extends module:ui/view~View
 */
class balloonpanelview_BalloonPanelView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * The absolute top position of the balloon panel in pixels.
         *
         * @observable
         * @default 0
         * @member {Number} #top
         */
        this.set('top', 0);
        /**
         * The absolute left position of the balloon panel in pixels.
         *
         * @observable
         * @default 0
         * @member {Number} #left
         */
        this.set('left', 0);
        /**
         * The balloon panel's current position. The position name is reflected in the CSS class set
         * to the balloon, i.e. `.ck-balloon-panel_arrow_nw` for the "arrow_nw" position. The class
         * controls the minor aspects of the balloon's visual appearance like the placement
         * of an {@link #withArrow arrow}. To support a new position, an additional CSS must be created.
         *
         * Default position names correspond with
         * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
         *
         * See the {@link #attachTo} and {@link #pin} methods to learn about custom balloon positions.
         *
         * @observable
         * @default 'arrow_nw'
         * @member {'arrow_nw'|'arrow_ne'|'arrow_sw'|'arrow_se'} #position
         */
        this.set('position', 'arrow_nw');
        /**
         * Controls whether the balloon panel is visible or not.
         *
         * @observable
         * @default false
         * @member {Boolean} #isVisible
         */
        this.set('isVisible', false);
        /**
         * Controls whether the balloon panel has an arrow. The presence of the arrow
         * is reflected in the `ck-balloon-panel_with-arrow` CSS class.
         *
         * @observable
         * @default true
         * @member {Boolean} #withArrow
         */
        this.set('withArrow', true);
        /**
         * An additional CSS class added to the {@link #element}.
         *
         * @observable
         * @member {String} #class
         */
        this.set('class', undefined);
        /**
         * A callback that starts pinning the panel when {@link #isVisible} gets
         * `true`. Used by {@link #pin}.
         *
         * @private
         * @member {Function} #_pinWhenIsVisibleCallback
         */
        this._pinWhenIsVisibleCallback = null;
        /**
         * A collection of the child views that creates the balloon panel contents.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.content = this.createCollection();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-balloon-panel',
                    bind.to('position', value => `ck-balloon-panel_${value}`),
                    bind.if('isVisible', 'ck-balloon-panel_visible'),
                    bind.if('withArrow', 'ck-balloon-panel_with-arrow'),
                    bind.to('class')
                ],
                style: {
                    top: bind.to('top', toPx),
                    left: bind.to('left', toPx)
                }
            },
            children: this.content
        });
    }
    /**
     * Shows the panel.
     *
     * See {@link #isVisible}.
     */
    show() {
        this.isVisible = true;
    }
    /**
     * Hides the panel.
     *
     * See {@link #isVisible}.
     */
    hide() {
        this.isVisible = false;
    }
    /**
     * Attaches the panel to a specified {@link module:utils/dom/position~Options#target} with a
     * smart positioning heuristics that chooses from available positions to make sure the panel
     * is visible to the user i.e. within the limits of the viewport.
     *
     * This method accepts configuration {@link module:utils/dom/position~Options options}
     * to set the `target`, optional `limiter` and `positions` the balloon should choose from.
     *
     *		const panel = new BalloonPanelView( locale );
     *		const positions = BalloonPanelView.defaultPositions;
     *
     *		panel.render();
     *
     *		// Attach the panel to an element with the "target" id DOM.
     *		panel.attachTo( {
     *			target: document.querySelector( '#target' ),
     *			positions: [
     *				positions.northArrowSouth,
     *				positions.southArrowNorth
     *			]
     *		} );
     *
     * **Note**: Attaching the panel will also automatically {@link #show} it.
     *
     * **Note**: An attached panel will not follow its target when the window is scrolled or resized.
     * See the {@link #pin} method for a more permanent positioning strategy.
     *
     * @param {module:utils/dom/position~Options} options Positioning options compatible with
     * {@link module:utils/dom/position~getOptimalPosition}. Default `positions` array is
     * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
     */
    attachTo(options) {
        this.show();
        const defaultPositions = balloonpanelview_BalloonPanelView.defaultPositions;
        const positionOptions = Object.assign({}, {
            element: this.element,
            positions: [
                defaultPositions.southArrowNorth,
                defaultPositions.southArrowNorthMiddleWest,
                defaultPositions.southArrowNorthMiddleEast,
                defaultPositions.southArrowNorthWest,
                defaultPositions.southArrowNorthEast,
                defaultPositions.northArrowSouth,
                defaultPositions.northArrowSouthMiddleWest,
                defaultPositions.northArrowSouthMiddleEast,
                defaultPositions.northArrowSouthWest,
                defaultPositions.northArrowSouthEast,
                defaultPositions.viewportStickyNorth
            ],
            limiter: defaultLimiterElement,
            fitInViewport: true
        }, options);
        const optimalPosition = balloonpanelview_BalloonPanelView._getOptimalPosition(positionOptions);
        // Usually browsers make some problems with super accurate values like 104.345px
        // so it is better to use int values.
        const left = parseInt(optimalPosition.left);
        const top = parseInt(optimalPosition.top);
        const position = optimalPosition.name;
        const config = optimalPosition.config || {};
        const { withArrow = true } = config;
        this.top = top;
        this.left = left;
        this.position = position;
        this.withArrow = withArrow;
    }
    /**
     * Works the same way as the {@link #attachTo} method except that the position of the panel is
     * continuously updated when:
     *
     * * any ancestor of the {@link module:utils/dom/position~Options#target}
     * or {@link module:utils/dom/position~Options#limiter} is scrolled,
     * * the browser window gets resized or scrolled.
     *
     * Thanks to that, the panel always sticks to the {@link module:utils/dom/position~Options#target}
     * and is immune to the changing environment.
     *
     *		const panel = new BalloonPanelView( locale );
     *		const positions = BalloonPanelView.defaultPositions;
     *
     *		panel.render();
     *
     *		// Pin the panel to an element with the "target" id DOM.
     *		panel.pin( {
     *			target: document.querySelector( '#target' ),
     *			positions: [
     *				positions.northArrowSouth,
     *				positions.southArrowNorth
     *			]
     *		} );
     *
     * To leave the pinned state, use the {@link #unpin} method.
     *
     * **Note**: Pinning the panel will also automatically {@link #show} it.
     *
     * @param {module:utils/dom/position~Options} options Positioning options compatible with
     * {@link module:utils/dom/position~getOptimalPosition}. Default `positions` array is
     * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
     */
    pin(options) {
        this.unpin();
        this._pinWhenIsVisibleCallback = () => {
            if (this.isVisible) {
                this._startPinning(options);
            }
            else {
                this._stopPinning();
            }
        };
        this._startPinning(options);
        // Control the state of the listeners depending on whether the panel is visible
        // or not.
        // TODO: Use on() (https://github.com/ckeditor/ckeditor5-utils/issues/144).
        this.listenTo(this, 'change:isVisible', this._pinWhenIsVisibleCallback);
    }
    /**
     * Stops pinning the panel, as set up by {@link #pin}.
     */
    unpin() {
        if (this._pinWhenIsVisibleCallback) {
            // Deactivate listeners attached by pin().
            this._stopPinning();
            // Deactivate the panel pin() control logic.
            // TODO: Use off() (https://github.com/ckeditor/ckeditor5-utils/issues/144).
            this.stopListening(this, 'change:isVisible', this._pinWhenIsVisibleCallback);
            this._pinWhenIsVisibleCallback = null;
            this.hide();
        }
    }
    /**
     * Starts managing the pinned state of the panel. See {@link #pin}.
     *
     * @private
     * @param {module:utils/dom/position~Options} options Positioning options compatible with
     * {@link module:utils/dom/position~getOptimalPosition}.
     */
    _startPinning(options) {
        this.attachTo(options);
        const targetElement = getDomElement(options.target);
        const limiterElement = options.limiter ? getDomElement(options.limiter) : defaultLimiterElement;
        // Then we need to listen on scroll event of eny element in the document.
        this.listenTo(dom_global.document, 'scroll', (evt, domEvt) => {
            const scrollTarget = domEvt.target;
            // The position needs to be updated if the positioning target is within the scrolled element.
            const isWithinScrollTarget = targetElement && scrollTarget.contains(targetElement);
            // The position needs to be updated if the positioning limiter is within the scrolled element.
            const isLimiterWithinScrollTarget = limiterElement && scrollTarget.contains(limiterElement);
            // The positioning target and/or limiter can be a Rect, object etc..
            // There's no way to optimize the listener then.
            if (isWithinScrollTarget || isLimiterWithinScrollTarget || !targetElement || !limiterElement) {
                this.attachTo(options);
            }
        }, { useCapture: true });
        // We need to listen on window resize event and update position.
        this.listenTo(dom_global.window, 'resize', () => {
            this.attachTo(options);
        });
    }
    /**
     * Stops managing the pinned state of the panel. See {@link #pin}.
     *
     * @private
     */
    _stopPinning() {
        this.stopListening(dom_global.document, 'scroll');
        this.stopListening(dom_global.window, 'resize');
    }
}
/**
 * A side offset of the arrow tip from the edge of the balloon. Controlled by CSS.
 *
 *		 ┌───────────────────────┐
 *		 │                       │
 *		 │         Balloon       │
 *		 │         Content       │
 *		 │                       │
 *		 └──+    +───────────────┘
 *		 |   \  /
 *		 |    \/
 *		>┼─────┼< ─────────────────────── side offset
 *
 *
 * @default 25
 * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowSideOffset
 */
balloonpanelview_BalloonPanelView.arrowSideOffset = 25;
/**
 * A height offset of the arrow from the edge of the balloon. Controlled by CSS.
 *
 *		 ┌───────────────────────┐
 *		 │                       │
 *		 │         Balloon       │
 *		 │         Content       │      ╱-- arrow height offset
 *		 │                       │      V
 *		 └──+    +───────────────┘ --- ─┼───────
 *		     \  /                       │
 *		      \/                        │
 *		────────────────────────────────┼───────
 *		                                ^
 *
 *
 *		>┼────┼<  arrow height offset
 *		 │    │
 *		 │    ┌────────────────────────┐
 *		 │    │                        │
 *		 │   ╱                         │
 *		 │ ╱            Balloon        │
 *		 │ ╲            Content        │
 *		 │   ╲                         │
 *		 │    │                        │
 *		 │    └────────────────────────┘
 *
 *
 * @default 10
 * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowHeightOffset
*/
balloonpanelview_BalloonPanelView.arrowHeightOffset = 10;
/**
 * A vertical offset of the balloon panel from the edge of the viewport if sticky.
 * It helps in accessing toolbar buttons underneath the balloon panel.
 *
 *		  ┌───────────────────────────────────────────────────┐
 *		  │                      Target                       │
 *		  │                                                   │
 *		  │                            /── vertical offset    │
 *		┌─────────────────────────────V─────────────────────────┐
 *		│ Toolbar            ┌─────────────┐                    │
 *		├────────────────────│   Balloon   │────────────────────┤
 *		│ │                  └─────────────┘                  │ │
 *		│ │                                                   │ │
 *		│ │                                                   │ │
 *		│ │                                                   │ │
 *		│ └───────────────────────────────────────────────────┘ │
 *		│                        Viewport                       │
 *		└───────────────────────────────────────────────────────┘
 *
 * @default 20
 * @member {Number} module:ui/panel/balloon/balloonpanelview~BalloonPanelView.stickyVerticalOffset
 */
balloonpanelview_BalloonPanelView.stickyVerticalOffset = 20;
/**
 * Function used to calculate the optimal position for the balloon.
 *
 * @protected
 * @member {Function} module:ui/panel/balloon/balloonpanelview~BalloonPanelView._getOptimalPosition
 */
balloonpanelview_BalloonPanelView._getOptimalPosition = position_getOptimalPosition;
/**
 * A default set of positioning functions used by the balloon panel view
 * when attaching using the {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView#attachTo} method.
 *
 * The available positioning functions are as follows:
 *
 *
 *
 * **North west**
 *
 * * `northWestArrowSouthWest`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		 V
 *		 [ Target ]
 *
 * * `northWestArrowSouthMiddleWest`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		    V
 *		    [ Target ]
 *
 * * `northWestArrowSouth`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		         V
 *		         [ Target ]
 *
 * * `northWestArrowSouthMiddleEast`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		             V
 *		             [ Target ]
 *
 * * `northWestArrowSouthEast`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		                 V
 *		                 [ Target ]
 *
 *
 *
 * **North**
 *
 * * `northArrowSouthWest`
 *
 *		    +-----------------+
 *		    |     Balloon     |
 *		    +-----------------+
 *		     V
 *		[ Target ]
 *
 * * `northArrowSouthMiddleWest`
 *
 *		 +-----------------+
 *		 |     Balloon     |
 *		 +-----------------+
 *		     V
 *		[ Target ]
 *
 * * `northArrowSouth`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		         V
 *		    [ Target ]
 *
 * * `northArrowSouthMiddleEast`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		             V
 *		        [ Target ]
 *
 * * `northArrowSouthEast`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		                V
 *		           [ Target ]
 *
 * **North east**
 *
 * * `northEastArrowSouthWest`
 *
 *		        +-----------------+
 *		        |     Balloon     |
 *		        +-----------------+
 *		         V
 *		[ Target ]
 *
 *
 * * `northEastArrowSouthMiddleWest`
 *
 *		     +-----------------+
 *		     |     Balloon     |
 *		     +-----------------+
 *		         V
 *		[ Target ]
 *
 * * `northEastArrowSouth`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		         V
 *		[ Target ]
 *
 * * `northEastArrowSouthMiddleEast`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		             V
 *		    [ Target ]
 *
 * * `northEastArrowSouthEast`
 *
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *		                 V
 *		        [ Target ]
 *
 *
 *
 * **South**
 *
 *
 * * `southArrowNorthWest`
 *
 *		[ Target ]
 *		     ^
 *		    +-----------------+
 *		    |     Balloon     |
 *		    +-----------------+
 *
 * * `southArrowNorthMiddleWest`
 *
 *		   [ Target ]
 *		        ^
 *		    +-----------------+
 *		    |     Balloon     |
 *		    +-----------------+
 *
 * * `southArrowNorth`
 *
 *		    [ Target ]
 *		         ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 * * `southArrowNorthMiddleEast`
 *
 *		            [ Target ]
 *		                 ^
 *		   +-----------------+
 *		   |     Balloon     |
 *		   +-----------------+
 *
 * * `southArrowNorthEast`
 *
 *		            [ Target ]
 *		                 ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 *
 *
 * **South west**
 *
 * * `southWestArrowNorthWest`
 *
 *		 [ Target ]
 *		 ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 * * `southWestArrowNorthMiddleWest`
 *
 *		     [ Target ]
 *		     ^
 *		 +-----------------+
 *		 |     Balloon     |
 *		 +-----------------+
 *
 * * `southWestArrowNorth`
 *
 *		         [ Target ]
 *		         ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 * * `southWestArrowNorthMiddleEast`
 *
 *		              [ Target ]
 *		              ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 * * `southWestArrowNorthEast`
 *
 *		                 [ Target ]
 *		                 ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 *
 *
 * **South east**
 *
 * * `southEastArrowNorthWest`
 *
 *		[ Target ]
 *		         ^
 *		        +-----------------+
 *		        |     Balloon     |
 *		        +-----------------+
 * * `southEastArrowNorthMiddleWest`
 *
 *		   [ Target ]
 *		            ^
 *		        +-----------------+
 *		        |     Balloon     |
 *		        +-----------------+
 *
 * * `southEastArrowNorth`
 *
 *		[ Target ]
 *		         ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 * * `southEastArrowNorthMiddleEast`
 *
 *		     [ Target ]
 *		              ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 * * `southEastArrowNorthEast`
 *
 *		        [ Target ]
 *		                 ^
 *		+-----------------+
 *		|     Balloon     |
 *		+-----------------+
 *
 *
 *
 * **West**
 *
 * * `westArrowEast`
 *
 *		+-----------------+
 *		|     Balloon     |>[ Target ]
 *		+-----------------+
 *
 * **East**
 *
 * * `eastArrowWest`
 *
 *		           +-----------------+
 *		[ Target ]<|     Balloon     |
 *		           +-----------------+
 *
 *
 *
 * **Sticky**
 *
 * * `viewportStickyNorth`
 *
 *		    +---------------------------+
 *		    |        [ Target ]         |
 *		    |                           |
 *		+-----------------------------------+
 *		|   |    +-----------------+    |   |
 *		|   |    |     Balloon     |    |   |
 *		|   |    +-----------------+    |   |
 *		|   |                           |   |
 *		|   |                           |   |
 *		|   |                           |   |
 *		|   |                           |   |
 *		|   +---------------------------+   |
 *		|             Viewport              |
 *		+-----------------------------------+
 *
 * See {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView#attachTo}.
 *
 * Positioning functions must be compatible with {@link module:utils/dom/position~Position}.
 *
 * Default positioning functions with customized offsets can be generated using
 * {@link module:ui/panel/balloon/balloonpanelview~generatePositions}.
 *
 * The name that the position function returns will be reflected in the balloon panel's class that
 * controls the placement of the "arrow". See {@link #position} to learn more.
 *
 * @member {Object.<String,module:utils/dom/position~PositioningFunction>}
 * module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions
 */
balloonpanelview_BalloonPanelView.defaultPositions = generatePositions();
// Returns the DOM element for given object or null, if there is none,
// e.g. when the passed object is a Rect instance or so.
//
// @private
// @param {*} object
// @returns {HTMLElement|null}
function getDomElement(object) {
    if (lodash_es_isElement(object)) {
        return object;
    }
    if (isRange(object)) {
        return object.commonAncestorContainer;
    }
    if (typeof object == 'function') {
        return getDomElement(object());
    }
    return null;
}
/**
 * Returns available {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
 * {@link module:utils/dom/position~PositioningFunction positioning functions} adjusted by the specific offsets.
 *
 * @protected
 * @param {Object} [options] Options to generate positions. If not specified, this helper will simply return
 * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.defaultPositions}.
 * @param {Number} [options.sideOffset] A custom side offset (in pixels) of each position. If
 * not specified, {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowSideOffset the default value}
 * will be used.
 * @param {Number} [options.heightOffset] A custom height offset (in pixels) of each position. If
 * not specified, {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.arrowHeightOffset the default value}
 * will be used.
 * @param {Number} [options.stickyVerticalOffset] A custom offset (in pixels) of the `viewportStickyNorth` positioning function.
 * If not specified, {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView.stickyVerticalOffset the default value}
 * will be used.
 * @param {Object} [options.config] Additional configuration of the balloon balloon panel view.
 * Currently only {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView#withArrow} is supported. Learn more
 * about {@link module:utils/dom/position~PositioningFunction positioning functions}.
 * @returns {Object.<String,module:utils/dom/position~PositioningFunction>}
 */
function generatePositions(options = {}) {
    const { sideOffset = balloonpanelview_BalloonPanelView.arrowSideOffset, heightOffset = balloonpanelview_BalloonPanelView.arrowHeightOffset, stickyVerticalOffset = balloonpanelview_BalloonPanelView.stickyVerticalOffset, config } = options;
    return {
        // ------- North west
        northWestArrowSouthWest: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left - sideOffset,
            name: 'arrow_sw',
            ...(config && { config })
        }),
        northWestArrowSouthMiddleWest: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left - (balloonRect.width * .25) - sideOffset,
            name: 'arrow_smw',
            ...(config && { config })
        }),
        northWestArrowSouth: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left - balloonRect.width / 2,
            name: 'arrow_s',
            ...(config && { config })
        }),
        northWestArrowSouthMiddleEast: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left - (balloonRect.width * .75) + sideOffset,
            name: 'arrow_sme',
            ...(config && { config })
        }),
        northWestArrowSouthEast: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left - balloonRect.width + sideOffset,
            name: 'arrow_se',
            ...(config && { config })
        }),
        // ------- North
        northArrowSouthWest: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left + targetRect.width / 2 - sideOffset,
            name: 'arrow_sw',
            ...(config && { config })
        }),
        northArrowSouthMiddleWest: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left + targetRect.width / 2 - (balloonRect.width * .25) - sideOffset,
            name: 'arrow_smw',
            ...(config && { config })
        }),
        northArrowSouth: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
            name: 'arrow_s',
            ...(config && { config })
        }),
        northArrowSouthMiddleEast: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left + targetRect.width / 2 - (balloonRect.width * .75) + sideOffset,
            name: 'arrow_sme',
            ...(config && { config })
        }),
        northArrowSouthEast: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.left + targetRect.width / 2 - balloonRect.width + sideOffset,
            name: 'arrow_se',
            ...(config && { config })
        }),
        // ------- North east
        northEastArrowSouthWest: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.right - sideOffset,
            name: 'arrow_sw',
            ...(config && { config })
        }),
        northEastArrowSouthMiddleWest: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.right - (balloonRect.width * .25) - sideOffset,
            name: 'arrow_smw',
            ...(config && { config })
        }),
        northEastArrowSouth: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.right - balloonRect.width / 2,
            name: 'arrow_s',
            ...(config && { config })
        }),
        northEastArrowSouthMiddleEast: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.right - (balloonRect.width * .75) + sideOffset,
            name: 'arrow_sme',
            ...(config && { config })
        }),
        northEastArrowSouthEast: (targetRect, balloonRect) => ({
            top: getNorthTop(targetRect, balloonRect),
            left: targetRect.right - balloonRect.width + sideOffset,
            name: 'arrow_se',
            ...(config && { config })
        }),
        // ------- South west
        southWestArrowNorthWest: targetRect => ({
            top: getSouthTop(targetRect),
            left: targetRect.left - sideOffset,
            name: 'arrow_nw',
            ...(config && { config })
        }),
        southWestArrowNorthMiddleWest: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left - (balloonRect.width * .25) - sideOffset,
            name: 'arrow_nmw',
            ...(config && { config })
        }),
        southWestArrowNorth: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left - balloonRect.width / 2,
            name: 'arrow_n',
            ...(config && { config })
        }),
        southWestArrowNorthMiddleEast: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left - (balloonRect.width * .75) + sideOffset,
            name: 'arrow_nme',
            ...(config && { config })
        }),
        southWestArrowNorthEast: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left - balloonRect.width + sideOffset,
            name: 'arrow_ne',
            ...(config && { config })
        }),
        // ------- South
        southArrowNorthWest: targetRect => ({
            top: getSouthTop(targetRect),
            left: targetRect.left + targetRect.width / 2 - sideOffset,
            name: 'arrow_nw',
            ...(config && { config })
        }),
        southArrowNorthMiddleWest: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left + targetRect.width / 2 - (balloonRect.width * 0.25) - sideOffset,
            name: 'arrow_nmw',
            ...(config && { config })
        }),
        southArrowNorth: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
            name: 'arrow_n',
            ...(config && { config })
        }),
        southArrowNorthMiddleEast: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left + targetRect.width / 2 - (balloonRect.width * 0.75) + sideOffset,
            name: 'arrow_nme',
            ...(config && { config })
        }),
        southArrowNorthEast: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.left + targetRect.width / 2 - balloonRect.width + sideOffset,
            name: 'arrow_ne',
            ...(config && { config })
        }),
        // ------- South east
        southEastArrowNorthWest: targetRect => ({
            top: getSouthTop(targetRect),
            left: targetRect.right - sideOffset,
            name: 'arrow_nw',
            ...(config && { config })
        }),
        southEastArrowNorthMiddleWest: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.right - (balloonRect.width * .25) - sideOffset,
            name: 'arrow_nmw',
            ...(config && { config })
        }),
        southEastArrowNorth: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.right - balloonRect.width / 2,
            name: 'arrow_n',
            ...(config && { config })
        }),
        southEastArrowNorthMiddleEast: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.right - (balloonRect.width * .75) + sideOffset,
            name: 'arrow_nme',
            ...(config && { config })
        }),
        southEastArrowNorthEast: (targetRect, balloonRect) => ({
            top: getSouthTop(targetRect),
            left: targetRect.right - balloonRect.width + sideOffset,
            name: 'arrow_ne',
            ...(config && { config })
        }),
        // ------- West
        westArrowEast: (targetRect, balloonRect) => ({
            top: targetRect.top + targetRect.height / 2 - balloonRect.height / 2,
            left: targetRect.left - balloonRect.width - heightOffset,
            name: 'arrow_e',
            ...(config && { config })
        }),
        // ------- East
        eastArrowWest: (targetRect, balloonRect) => ({
            top: targetRect.top + targetRect.height / 2 - balloonRect.height / 2,
            left: targetRect.right + heightOffset,
            name: 'arrow_w',
            ...(config && { config })
        }),
        // ------- Sticky
        viewportStickyNorth: (targetRect, balloonRect, viewportRect) => {
            if (!targetRect.getIntersection(viewportRect)) {
                return null;
            }
            return {
                top: viewportRect.top + stickyVerticalOffset,
                left: targetRect.left + targetRect.width / 2 - balloonRect.width / 2,
                name: 'arrowless',
                config: {
                    withArrow: false,
                    ...config
                }
            };
        }
    };
    // Returns the top coordinate for positions starting with `north*`.
    //
    // @private
    // @param {utils/dom/rect~Rect} targetRect A rect of the target.
    // @param {utils/dom/rect~Rect} elementRect A rect of the balloon.
    // @returns {Number}
    function getNorthTop(targetRect, balloonRect) {
        return targetRect.top - balloonRect.height - heightOffset;
    }
    // Returns the top coordinate for positions starting with `south*`.
    //
    // @private
    // @param {utils/dom/rect~Rect} targetRect A rect of the target.
    // @param {utils/dom/rect~Rect} elementRect A rect of the balloon.
    // @returns {Number}
    function getSouthTop(targetRect) {
        return targetRect.bottom + heightOffset;
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/tooltip/tooltip.css
var tooltip = __webpack_require__(9948);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/tooltip/tooltip.css

            

var tooltip_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tooltip_options.insert = "head";
tooltip_options.singleton = true;

var tooltip_update = injectStylesIntoStyleTag_default()(tooltip/* default */.Z, tooltip_options);



/* harmony default export */ const tooltip_tooltip = (tooltip/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/tooltipmanager.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/tooltipmanager
 */






const BALLOON_CLASS = 'ck-tooltip';
/**
 * A tooltip manager class for the UI of the editor.
 *
 * **Note**: Most likely you do not have to use the `TooltipManager` API listed below in order to display tooltips. Popular
 * {@glink framework/guides/architecture/ui-library UI components} support tooltips out-of-the-box via observable properties
 * (see {@link module:ui/button/buttonview~ButtonView#tooltip} and {@link module:ui/button/buttonview~ButtonView#tooltipPosition}).
 *
 * # Displaying tooltips
 *
 * To display a tooltip, set `data-cke-tooltip-text` attribute on any DOM element:
 *
 *		domElement.dataset.ckeTooltipText = 'My tooltip';
 *
 * The tooltip will show up whenever the user moves the mouse over the element or the element gets focus in DOM.
 *
 * # Positioning tooltips
 *
 * To change the position of the tooltip, use the `data-cke-tooltip-position` attribute (`s`, `se`, `sw`, `n`, `e`, or `w`):
 *
 *		domElement.dataset.ckeTooltipText = 'Tooltip to the north';
 *		domElement.dataset.ckeTooltipPosition = 'n';
 *
 * # Disabling tooltips
 *
 * In order to disable the tooltip  temporarily, use the `data-cke-tooltip-disabled` attribute:
 *
 *		domElement.dataset.ckeTooltipText = 'Disabled. For now.';
 *		domElement.dataset.ckeTooltipDisabled = 'true';
 *
 *
 * # Styling tooltips
 *
 * By default, the tooltip has `.ck-tooltip` class and its text inner `.ck-tooltip__text`.
 *
 * If your tooltip requires custom styling, using `data-cke-tooltip-class` attribute will add additional class to the balloon
 * displaying the tooltip:
 *
 *		domElement.dataset.ckeTooltipText = 'Tooltip with a red text';
 *		domElement.dataset.ckeTooltipClass = 'my-class';
 *
 *		.ck.ck-tooltip.my-class { color: red }
 *
 * **Note**: This class is a singleton. All editor instances re-use the same instance loaded by
 * {@link module:core/editor/editorui~EditorUI} of the first editor.
 *
 * @mixes module:utils/domemittermixin~DomEmitterMixin
 */
class TooltipManager extends emittermixin_Emitter {
    /**
     * Creates an instance of the tooltip manager.
     *
     * @param {module:core/editor/editor~Editor} editor
     */
    constructor(editor) {
        super();
        TooltipManager._editors.add(editor);
        // TooltipManager must be a singleton. Multiple instances would mean multiple tooltips attached
        // to the same DOM element with data-cke-tooltip-* attributes.
        if (TooltipManager._instance) {
            return TooltipManager._instance;
        }
        TooltipManager._instance = this;
        /**
         * The view rendering text of the tooltip.
         *
         * @readonly
         * @member {module:ui/view~View} #tooltipTextView
         */
        this.tooltipTextView = new src_view_View(editor.locale);
        this.tooltipTextView.set('text', '');
        this.tooltipTextView.setTemplate({
            tag: 'span',
            attributes: {
                class: [
                    'ck',
                    'ck-tooltip__text'
                ]
            },
            children: [
                {
                    text: this.tooltipTextView.bindTemplate.to('text')
                }
            ]
        });
        /**
         * The instance of the balloon panel that renders and positions the tooltip.
         *
         * @readonly
         * @member {module:ui/panel/balloon/balloonpanelview~BalloonPanelView} #balloonPanelView
         */
        this.balloonPanelView = new balloonpanelview_BalloonPanelView(editor.locale);
        this.balloonPanelView.class = BALLOON_CLASS;
        this.balloonPanelView.content.add(this.tooltipTextView);
        /**
         * An instance of the resize observer that keeps track on target element visibility,
         * when it hides the tooltip should also disappear.
         *
         * {@link module:core/editor/editorconfig~EditorConfig#balloonToolbar configuration}.
         *
         * @protected
         * @member {module:utils/dom/resizeobserver~ResizeObserver|null}
         *
         */
        this._resizeObserver = null;
        /**
         * Stores the reference to the DOM element the tooltip is attached to. `null` when there's no tooltip
         * in the UI.
         *
         * @private
         * @readonly
         * @member {HTMLElement|null} #_currentElementWithTooltip
         */
        this._currentElementWithTooltip = null;
        /**
         * Stores the current tooltip position. `null` when there's no tooltip in the UI.
         *
         * @private
         * @readonly
         * @member {String|null} #_currentTooltipPosition
         */
        this._currentTooltipPosition = null;
        /**
         * A debounced version of {@link #_pinTooltip}. Tooltips show with a delay to avoid flashing and
         * to improve the UX.
         *
         * @private
         * @readonly
         * @member {Function} #_pinTooltipDebounced
         */
        this._pinTooltipDebounced = lodash_es_debounce(this._pinTooltip, 600);
        this.listenTo(dom_global.document, 'mouseenter', this._onEnterOrFocus.bind(this), { useCapture: true });
        this.listenTo(dom_global.document, 'mouseleave', this._onLeaveOrBlur.bind(this), { useCapture: true });
        this.listenTo(dom_global.document, 'focus', this._onEnterOrFocus.bind(this), { useCapture: true });
        this.listenTo(dom_global.document, 'blur', this._onLeaveOrBlur.bind(this), { useCapture: true });
        this.listenTo(dom_global.document, 'scroll', this._onScroll.bind(this), { useCapture: true });
        // Because this class is a singleton, its only instance is shared across all editors and connects them through the reference.
        // This causes issues with the ContextWatchdog. When an error is thrown in one editor, the watchdog traverses the references
        // and (because of shared tooltip manager) figures that the error affects all editors and restarts them all.
        // This flag, excludes tooltip manager instance from the traversal and brings ContextWatchdog back to normal.
        // More in https://github.com/ckeditor/ckeditor5/issues/12292.
        this._watchdogExcluded = true;
    }
    /**
     * Destroys the tooltip manager.
     *
     * **Note**: The manager singleton cannot be destroyed until all editors that use it are destroyed.
     *
     * @param {module:core/editor/editor~Editor} editor The editor the manager was created for.
     */
    destroy(editor) {
        const editorBodyViewCollection = editor.ui.view && editor.ui.view.body;
        TooltipManager._editors.delete(editor);
        this.stopListening(editor.ui);
        // Prevent the balloon panel from being destroyed in the EditorUI#destroy() cascade. It should be destroyed along
        // with the last editor only (https://github.com/ckeditor/ckeditor5/issues/12602).
        if (editorBodyViewCollection && editorBodyViewCollection.has(this.balloonPanelView)) {
            editorBodyViewCollection.remove(this.balloonPanelView);
        }
        if (!TooltipManager._editors.size) {
            this._unpinTooltip();
            this.balloonPanelView.destroy();
            this.stopListening();
            TooltipManager._instance = null;
        }
    }
    /**
     * Returns {@link #balloonPanelView} {@link module:utils/dom/position~PositioningFunction positioning functions} for a given position
     * name.
     *
     * @static
     * @param {String} position Name of the position (`s`, `se`, `sw`, `n`, `e`, or `w`).
     * @returns {Array.<module:utils/dom/position~PositioningFunction>} Positioning functions to be used by the {@link #balloonPanelView}.
     */
    static getPositioningFunctions(position) {
        const defaultPositions = TooltipManager.defaultBalloonPositions;
        return {
            // South is most popular. We can use positioning heuristics to avoid clipping by the viewport with the sane fallback.
            s: [
                defaultPositions.southArrowNorth,
                defaultPositions.southArrowNorthEast,
                defaultPositions.southArrowNorthWest
            ],
            n: [defaultPositions.northArrowSouth],
            e: [defaultPositions.eastArrowWest],
            w: [defaultPositions.westArrowEast],
            sw: [defaultPositions.southArrowNorthEast],
            se: [defaultPositions.southArrowNorthWest]
        }[position];
    }
    /**
     * Handles displaying tooltips on `mouseenter` and `focus` in DOM.
     *
     * @private
     * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
     * @param {Event} domEvent The DOM event.
     */
    _onEnterOrFocus(evt, { target }) {
        const elementWithTooltipAttribute = getDescendantWithTooltip(target);
        // Abort when there's no descendant needing tooltip.
        if (!elementWithTooltipAttribute) {
            return;
        }
        // Abort to avoid flashing when, for instance:
        // * a tooltip is displayed for a focused element, then the same element gets mouseentered,
        // * a tooltip is displayed for an element via mouseenter, then the focus moves to the same element.
        if (elementWithTooltipAttribute === this._currentElementWithTooltip) {
            return;
        }
        this._unpinTooltip();
        this._pinTooltipDebounced(elementWithTooltipAttribute, getTooltipData(elementWithTooltipAttribute));
    }
    /**
     * Handles hiding tooltips on `mouseleave` and `blur` in DOM.
     *
     * @private
     * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
     * @param {Event} domEvent The DOM event.
     */
    _onLeaveOrBlur(evt, { target, relatedTarget }) {
        if (evt.name === 'mouseleave') {
            // Don't act when the event does not concern a DOM element (e.g. a mouseleave out of an entire document),
            if (!lodash_es_isElement(target)) {
                return;
            }
            // If a tooltip is currently visible, don't act for a targets other than the one it is attached to.
            // For instance, a random mouseleave far away in the page should not unpin the tooltip that was pinned because
            // of a previous focus. Only leaving the same element should hide the tooltip.
            if (this._currentElementWithTooltip && target !== this._currentElementWithTooltip) {
                return;
            }
            const descendantWithTooltip = getDescendantWithTooltip(target);
            const relatedDescendantWithTooltip = getDescendantWithTooltip(relatedTarget);
            // Unpin when the mouse was leaving element with a tooltip to a place which does not have or has a different tooltip.
            // Note that this should happen whether the tooltip is already visible or not, for instance, it could be invisible but queued
            // (debounced): it should get canceled.
            if (descendantWithTooltip && descendantWithTooltip !== relatedDescendantWithTooltip) {
                this._unpinTooltip();
            }
        }
        else {
            // If a tooltip is currently visible, don't act for a targets other than the one it is attached to.
            // For instance, a random blur in the web page should not unpin the tooltip that was pinned because of a previous mouseenter.
            if (this._currentElementWithTooltip && target !== this._currentElementWithTooltip) {
                return;
            }
            // Note that unpinning should happen whether the tooltip is already visible or not, for instance, it could be invisible but
            // queued (debounced): it should get canceled (e.g. quick focus then quick blur using the keyboard).
            this._unpinTooltip();
        }
    }
    /**
     * Handles hiding tooltips on `scroll` in DOM.
     *
     * @private
     * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
     * @param {Event} domEvent The DOM event.
     */
    _onScroll(evt, { target }) {
        // No tooltip, no reason to react on scroll.
        if (!this._currentElementWithTooltip) {
            return;
        }
        // When scrolling a container that has both the balloon and the current element (common ancestor), the balloon can remain
        // visible (e.g. scrolling ≤body>). Otherwise, to avoid glitches (clipping, lagging) better just hide the tooltip.
        // Also, don't do anything when scrolling an unrelated DOM element that has nothing to do with the current element and the balloon.
        if (target.contains(this.balloonPanelView.element) && target.contains(this._currentElementWithTooltip)) {
            return;
        }
        this._unpinTooltip();
    }
    /**
     * Pins the tooltip to a specific DOM element.
     *
     * @private
     * @param {Element} targetDomElement
     * @param {Object} options
     * @param {String} options.text Text of the tooltip to display.
     * @param {String} options.position The position of the tooltip.
     * @param {String} options.cssClass Additional CSS class of the balloon with the tooltip.
     */
    _pinTooltip(targetDomElement, { text, position, cssClass }) {
        // Use the body collection of the first editor.
        const bodyViewCollection = first_first(TooltipManager._editors.values()).ui.view.body;
        if (!bodyViewCollection.has(this.balloonPanelView)) {
            bodyViewCollection.add(this.balloonPanelView);
        }
        this.tooltipTextView.text = text;
        this.balloonPanelView.pin({
            target: targetDomElement,
            positions: TooltipManager.getPositioningFunctions(position)
        });
        this._resizeObserver = new resizeobserver_ResizeObserver(targetDomElement, () => {
            // The ResizeObserver will call its callback when the target element hides and the tooltip
            // should also disappear (https://github.com/ckeditor/ckeditor5/issues/12492).
            if (!isVisible(targetDomElement)) {
                this._unpinTooltip();
            }
        });
        this.balloonPanelView.class = [BALLOON_CLASS, cssClass]
            .filter(className => className)
            .join(' ');
        // Start responding to changes in editor UI or content layout. For instance, when collaborators change content
        // and a contextual toolbar attached to a content starts to move (and so should move the tooltip).
        // Note: Using low priority to let other listeners that position contextual toolbars etc. to react first.
        for (const editor of TooltipManager._editors) {
            this.listenTo(editor.ui, 'update', this._updateTooltipPosition.bind(this), { priority: 'low' });
        }
        this._currentElementWithTooltip = targetDomElement;
        this._currentTooltipPosition = position;
    }
    /**
     * Unpins the tooltip and cancels all queued pinning.
     *
     * @private
     */
    _unpinTooltip() {
        this._pinTooltipDebounced.cancel();
        this.balloonPanelView.unpin();
        for (const editor of TooltipManager._editors) {
            this.stopListening(editor.ui, 'update');
        }
        this._currentElementWithTooltip = null;
        this._currentTooltipPosition = null;
        if (this._resizeObserver) {
            this._resizeObserver.destroy();
        }
    }
    /**
     * Updates the position of the tooltip so it stays in sync with the element it is pinned to.
     *
     * Hides the tooltip when the element is no longer visible in DOM.
     *
     * @private
     */
    _updateTooltipPosition() {
        // This could happen if the tooltip was attached somewhere in a contextual content toolbar and the toolbar
        // disappeared (e.g. removed an image).
        if (!isVisible(this._currentElementWithTooltip)) {
            this._unpinTooltip();
            return;
        }
        this.balloonPanelView.pin({
            target: this._currentElementWithTooltip,
            positions: TooltipManager.getPositioningFunctions(this._currentTooltipPosition)
        });
    }
}
/**
 * A set of default {@link module:utils/dom/position~PositioningFunction positioning functions} used by the `TooltipManager`
 * to pin tooltips in different positions.
 *
 * @member {Object.<String,module:utils/dom/position~PositioningFunction>}
 * module:ui/tooltipmanager~TooltipManager.defaultBalloonPositions
 */
TooltipManager.defaultBalloonPositions = generatePositions({
    heightOffset: 5,
    sideOffset: 13
});
/**
 * A set of editors the single tooltip manager instance must listen to.
 * This is mostly to handle `EditorUI#update` listeners from individual editors.
 *
 * @private
 * @member {Set.<module:core/editor/editor~Editor>} module:ui/tooltipmanager~TooltipManager._editors
 */
TooltipManager._editors = new Set();
/**
 * A reference to the `TooltipManager` instance. The class is a singleton and as such,
 * successive attempts at creating instances should return this instance.
 *
 * @private
 * @member {module:ui/tooltipmanager~TooltipManager} module:ui/tooltipmanager~TooltipManager._instance
 */
TooltipManager._instance = null;
function getDescendantWithTooltip(element) {
    if (!lodash_es_isElement(element)) {
        return null;
    }
    return element.closest('[data-cke-tooltip-text]:not([data-cke-tooltip-disabled])');
}
function getTooltipData(element) {
    return {
        text: element.dataset.ckeTooltipText,
        position: (element.dataset.ckeTooltipPosition || 's'),
        cssClass: element.dataset.ckeTooltipClass || ''
    };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/editor/editorui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */





/**
 * A class providing the minimal interface that is required to successfully bootstrap any editor UI.
 *
 * @mixes module:utils/emittermixin~EmitterMixin
 */
class EditorUI extends Observable {
    /**
     * Creates an instance of the editor UI class.
     *
     * @param {module:core/editor/editor~Editor} editor The editor instance.
     */
    constructor(editor) {
        super();
        /**
         * The editor that the UI belongs to.
         *
         * @readonly
         * @member {module:core/editor/editor~Editor} #editor
         */
        this.editor = editor;
        /**
         * An instance of the {@link module:ui/componentfactory~ComponentFactory}, a registry used by plugins
         * to register factories of specific UI components.
         *
         * @readonly
         * @member {module:ui/componentfactory~ComponentFactory} #componentFactory
         */
        this.componentFactory = new ComponentFactory(editor);
        /**
         * Stores the information about the editor UI focus and propagates it so various plugins and components
         * are unified as a focus group.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker} #focusTracker
         */
        this.focusTracker = new FocusTracker();
        /**
         * Manages the tooltips displayed on mouseover and focus across the UI.
         *
         * @readonly
         * @member {module:ui/tooltipmanager~TooltipManager}
         */
        this.tooltipManager = new TooltipManager(editor);
        /**
         * Stores viewport offsets from every direction.
         *
         * Viewport offset can be used to constrain balloons or other UI elements into an element smaller than the viewport.
         * This can be useful if there are any other absolutely positioned elements that may interfere with editor UI.
         *
         * Example `editor.ui.viewportOffset` returns:
         *
         * ```js
         * {
         * 	top: 50,
         * 	right: 50,
         * 	bottom: 50,
         * 	left: 50
         * }
         * ```
         *
         * This property can be overriden after editor already being initialized:
         *
         * ```js
         * editor.ui.viewportOffset = {
         * 	top: 100,
         * 	right: 0,
         * 	bottom: 0,
         * 	left: 0
         * };
         * ```
         *
         * @observable
         * @member {Object} #viewportOffset
         */
        this.set('viewportOffset', this._readViewportOffsetFromConfig());
        /**
         * Indicates the UI is ready. Set `true` after {@link #event:ready} event is fired.
         *
         * @readonly
         * @default false
         * @member {Boolean} #isReady
         */
        this.isReady = false;
        this.once('ready', () => {
            this.isReady = true;
        });
        /**
         * Stores all editable elements used by the editor instance.
         *
         * @private
         * @member {Map.<String,HTMLElement>}
         */
        this._editableElementsMap = new Map();
        /**
         * All available & focusable toolbars.
         *
         * @private
         * @type {Array.<module:core/editor/editorui~FocusableToolbarDefinition>}
         */
        this._focusableToolbarDefinitions = [];
        // Informs UI components that should be refreshed after layout change.
        this.listenTo(editor.editing.view.document, 'layoutChanged', () => this.update());
        this._initFocusTracking();
    }
    /**
     * The main (outermost) DOM element of the editor UI.
     *
     * For example, in {@link module:editor-classic/classiceditor~ClassicEditor} it is a `<div>` which
     * wraps the editable element and the toolbar. In {@link module:editor-inline/inlineeditor~InlineEditor}
     * it is the editable element itself (as there is no other wrapper). However, in
     * {@link module:editor-decoupled/decouplededitor~DecoupledEditor} it is set to `null` because this editor does not
     * come with a single "main" HTML element (its editable element and toolbar are separate).
     *
     * This property can be understood as a shorthand for retrieving the element that a specific editor integration
     * considers to be its main DOM element.
     *
     * @readonly
     * @member {HTMLElement|null} #element
     */
    get element() {
        return null;
    }
    /**
     * Fires the {@link module:core/editor/editorui~EditorUI#event:update `update`} event.
     *
     * This method should be called when the editor UI (e.g. positions of its balloons) needs to be updated due to
     * some environmental change which CKEditor 5 is not aware of (e.g. resize of a container in which it is used).
     */
    update() {
        this.fire('update');
    }
    /**
     * Destroys the UI.
     */
    destroy() {
        this.stopListening();
        this.focusTracker.destroy();
        this.tooltipManager.destroy(this.editor);
        // Clean–up the references to the CKEditor instance stored in the native editable DOM elements.
        for (const domElement of this._editableElementsMap.values()) {
            domElement.ckeditorInstance = null;
        }
        this._editableElementsMap = new Map();
        this._focusableToolbarDefinitions = [];
    }
    /**
     * Stores the native DOM editable element used by the editor under a unique name.
     *
     * Also, registers the element in the editor to maintain the accessibility of the UI. When the user is editing text in a focusable
     * editable area, they can use the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke to navigate over editor toolbars. See {@link #addToolbar}.
     *
     * @param {String} rootName The unique name of the editable element.
     * @param {HTMLElement} domElement The native DOM editable element.
     */
    setEditableElement(rootName, domElement) {
        this._editableElementsMap.set(rootName, domElement);
        // Put a reference to the CKEditor instance in the editable native DOM element.
        // It helps 3rd–party software (browser extensions, other libraries) access and recognize
        // CKEditor 5 instances (editing roots) and use their API (there is no global editor
        // instance registry).
        if (!domElement.ckeditorInstance) {
            domElement.ckeditorInstance = this.editor;
        }
        // Register the element so it becomes available for Alt+F10 and Esc navigation.
        this.focusTracker.add(domElement);
        const setUpKeystrokeHandler = () => {
            // The editing view of the editor is already listening to keystrokes from DOM roots (see: KeyObserver).
            // Do not duplicate listeners.
            if (this.editor.editing.view.getDomRoot(rootName)) {
                return;
            }
            this.editor.keystrokes.listenTo(domElement);
        };
        // For editable elements set by features after EditorUI is ready (e.g. source editing).
        if (this.isReady) {
            setUpKeystrokeHandler();
        }
        // For editable elements set while the editor is being created (e.g. DOM roots).
        else {
            this.once('ready', setUpKeystrokeHandler);
        }
    }
    /**
     * Returns the editable editor element with the given name or null if editable does not exist.
     *
     * @param {String} [rootName=main] The editable name.
     * @returns {HTMLElement|undefined}
     */
    getEditableElement(rootName = 'main') {
        return this._editableElementsMap.get(rootName);
    }
    /**
     * Returns array of names of all editor editable elements.
     *
     * @returns {Iterable.<String>}
     */
    getEditableElementsNames() {
        return this._editableElementsMap.keys();
    }
    /**
     * Adds a toolbar to the editor UI. Used primarily to maintain the accessibility of the UI.
     *
     * Focusable toolbars can be accessed (focused) by users by pressing the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke.
     * Successive keystroke presses navigate over available toolbars.
     *
     * @param {module:ui/toolbar/toolbarview~ToolbarView} toolbarView A instance of the toolbar to be registered.
     * @param {Object} [options]
     * @param {Boolean} [options.isContextual] Set `true` if the toolbar is attached to the content of the editor. Such toolbar takes
     * a precedence over other toolbars when a user pressed <kbd>Alt</kbd> + <kbd>F10</kbd>.
     * @param {Function} [options.beforeFocus] Specify a callback executed before the toolbar instance DOM element gains focus
     * upon the <kbd>Alt</kbd> + <kbd>F10</kbd> keystroke.
     * @param {Function} [options.afterBlur] Specify a callback executed after the toolbar instance DOM element loses focus upon
     * <kbd>Esc</kbd> keystroke but before the focus goes back to the {@link #setEditableElement editable element}.
     */
    addToolbar(toolbarView, options = {}) {
        if (toolbarView.isRendered) {
            this.focusTracker.add(toolbarView.element);
            this.editor.keystrokes.listenTo(toolbarView.element);
        }
        else {
            toolbarView.once('render', () => {
                this.focusTracker.add(toolbarView.element);
                this.editor.keystrokes.listenTo(toolbarView.element);
            });
        }
        this._focusableToolbarDefinitions.push({ toolbarView, options });
    }
    /**
     * Stores all editable elements used by the editor instance.
     *
     * @protected
     * @deprecated
     * @member {Map.<String,HTMLElement>}
     */
    get _editableElements() {
        /**
         * The {@link module:core/editor/editorui~EditorUI#_editableElements `EditorUI#_editableElements`} property has been
         * deprecated and will be removed in the near future. Please use {@link #setEditableElement `setEditableElement()`} and
         * {@link #getEditableElement `getEditableElement()`} methods instead.
         *
         * @error editor-ui-deprecated-editable-elements
         * @param {module:core/editor/editorui~EditorUI} editorUI Editor UI instance the deprecated property belongs to.
         */
        console.warn('editor-ui-deprecated-editable-elements: ' +
            'The EditorUI#_editableElements property has been deprecated and will be removed in the near future.', { editorUI: this });
        return this._editableElementsMap;
    }
    /**
     * Returns viewport offsets object:
     *
     * ```js
     * {
     * 	top: Number,
     * 	right: Number,
     * 	bottom: Number,
     * 	left: Number
     * }
     * ```
     *
     * Only top property is currently supported.
     *
     * @private
     * @return {Object}
     */
    _readViewportOffsetFromConfig() {
        const editor = this.editor;
        const viewportOffsetConfig = editor.config.get('ui.viewportOffset');
        if (viewportOffsetConfig) {
            return viewportOffsetConfig;
        }
        // Not present in EditorConfig type, because it's legacy. Hence the `as` expression.
        const legacyOffsetConfig = editor.config.get('toolbar.viewportTopOffset');
        // Fall back to deprecated toolbar config.
        if (legacyOffsetConfig) {
            /**
             * The {@link module:core/editor/editorconfig~EditorConfig#toolbar `EditorConfig#toolbar.viewportTopOffset`}
             * property has been deprecated and will be removed in the near future. Please use
             * {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset`} instead.
             *
             * @error editor-ui-deprecated-viewport-offset-config
             */
            console.warn('editor-ui-deprecated-viewport-offset-config: ' +
                'The `toolbar.vieportTopOffset` configuration option is deprecated. ' +
                'It will be removed from future CKEditor versions. Use `ui.viewportOffset.top` instead.');
            return { top: legacyOffsetConfig };
        }
        // More keys to come in the future.
        return { top: 0 };
    }
    /**
     * Starts listening for <kbd>Alt</kbd> + <kbd>F10</kbd> and <kbd>Esc</kbd> keystrokes in the context of focusable
     * {@link #setEditableElement editable elements} and {@link #addToolbar toolbars}
     * to allow users navigate across the UI.
     *
     * @private
     */
    _initFocusTracking() {
        const editor = this.editor;
        const editingView = editor.editing.view;
        let lastFocusedForeignElement;
        let candidateDefinitions;
        // Focus the next focusable toolbar on <kbd>Alt</kbd> + <kbd>F10</kbd>.
        editor.keystrokes.set('Alt+F10', (data, cancel) => {
            const focusedElement = this.focusTracker.focusedElement;
            // Focus moved out of a DOM element that
            // * is not a toolbar,
            // * does not belong to the editing view (e.g. source editing).
            if (Array.from(this._editableElementsMap.values()).includes(focusedElement) &&
                !Array.from(editingView.domRoots.values()).includes(focusedElement)) {
                lastFocusedForeignElement = focusedElement;
            }
            const currentFocusedToolbarDefinition = this._getCurrentFocusedToolbarDefinition();
            // * When focusing a toolbar for the first time, set the array of definitions for successive presses of Alt+F10.
            // This ensures, the navigation works always the same and no pair of toolbars takes over
            // (e.g. image and table toolbars when a selected image is inside a cell).
            // * It could be that the focus went to the toolbar by clicking a toolbar item (e.g. a dropdown). In this case,
            // there were no candidates so they must be obtained (#12339).
            if (!currentFocusedToolbarDefinition || !candidateDefinitions) {
                candidateDefinitions = this._getFocusableCandidateToolbarDefinitions();
            }
            // In a single Alt+F10 press, check all candidates but if none were focused, don't go any further.
            // This prevents an infinite loop.
            for (let i = 0; i < candidateDefinitions.length; i++) {
                const candidateDefinition = candidateDefinitions.shift();
                // Put the first definition to the back of the array. This allows circular navigation over all toolbars
                // on successive presses of Alt+F10.
                candidateDefinitions.push(candidateDefinition);
                // Don't focus the same toolbar again. If you did, this would move focus from the nth focused toolbar item back to the
                // first item as per ToolbarView#focus() if the user navigated inside the toolbar.
                if (candidateDefinition !== currentFocusedToolbarDefinition &&
                    this._focusFocusableCandidateToolbar(candidateDefinition)) {
                    // Clean up after a current visible toolbar when switching to the next one.
                    if (currentFocusedToolbarDefinition && currentFocusedToolbarDefinition.options.afterBlur) {
                        currentFocusedToolbarDefinition.options.afterBlur();
                    }
                    break;
                }
            }
            cancel();
        });
        // Blur the focused toolbar on <kbd>Esc</kbd> and bring the focus back to its origin.
        editor.keystrokes.set('Esc', (data, cancel) => {
            const focusedToolbarDef = this._getCurrentFocusedToolbarDefinition();
            if (!focusedToolbarDef) {
                return;
            }
            // Bring focus back to where it came from before focusing the toolbar:
            // 1. If it came from outside the engine view (e.g. source editing), move it there.
            if (lastFocusedForeignElement) {
                lastFocusedForeignElement.focus();
                lastFocusedForeignElement = null;
            }
            // 2. There are two possibilities left:
            //   2.1. It could be that the focus went from an editable element in the view (root or nested).
            //   2.2. It could be the focus went straight to the toolbar before even focusing the editing area.
            // In either case, just focus the view editing. The focus will land where it belongs.
            else {
                editor.editing.view.focus();
            }
            // Clean up after the toolbar if there is anything to do there.
            if (focusedToolbarDef.options.afterBlur) {
                focusedToolbarDef.options.afterBlur();
            }
            cancel();
        });
    }
    /**
     * Returns definitions of toolbars that could potentially be focused, sorted by their importance for the user.
     *
     * Focusable toolbars candidates are either:
     * * already visible,
     * * have `beforeFocus()` set in their {@link module:core/editor/editorui~FocusableToolbarDefinition definition} that suggests that
     * they might show up when called. Keep in mind that determining whether a toolbar will show up (and become focusable) is impossible
     * at this stage because it depends on its implementation, that in turn depends on the editing context (selection).
     *
     * **Note**: Contextual toolbars take precedence over regular toolbars.
     *
     * @private
     * @returns {Array.<module:core/editor/editorui~FocusableToolbarDefinition>}
     */
    _getFocusableCandidateToolbarDefinitions() {
        const definitions = [];
        for (const toolbarDef of this._focusableToolbarDefinitions) {
            const { toolbarView, options } = toolbarDef;
            if (isVisible(toolbarView.element) || options.beforeFocus) {
                definitions.push(toolbarDef);
            }
        }
        // Contextual and already visible toolbars have higher priority. If both are true, the toolbar will always focus first.
        // For instance, a selected widget toolbar vs inline editor toolbar: both are visible but the widget toolbar is contextual.
        definitions.sort((defA, defB) => getToolbarDefinitionWeight(defA) - getToolbarDefinitionWeight(defB));
        return definitions;
    }
    /**
     * Returns a definition of the toolbar that is currently visible and focused (one of its children has focus).
     *
     * `null` is returned when no toolbar is currently focused.
     *
     * @private
     * @returns {module:core/editor/editorui~FocusableToolbarDefinition|null}
     */
    _getCurrentFocusedToolbarDefinition() {
        for (const definition of this._focusableToolbarDefinitions) {
            if (definition.toolbarView.element && definition.toolbarView.element.contains(this.focusTracker.focusedElement)) {
                return definition;
            }
        }
        return null;
    }
    /**
     * Focuses a focusable toolbar candidate using its definition.
     *
     * @private
     * @param {module:core/editor/editorui~FocusableToolbarDefinition} candidateToolbarDefinition A definition of the toolbar to focus.
     * @returns {Boolean} `true` when the toolbar candidate was focused. `false` otherwise.
     */
    _focusFocusableCandidateToolbar(candidateToolbarDefinition) {
        const { toolbarView, options: { beforeFocus } } = candidateToolbarDefinition;
        if (beforeFocus) {
            beforeFocus();
        }
        // If it didn't show up after beforeFocus(), it's not focusable at all.
        if (!isVisible(toolbarView.element)) {
            return false;
        }
        toolbarView.focus();
        return true;
    }
}
/**
 * An instance of a focusable toolbar view.
 *
 * @member {module:ui/toolbar/toolbarview~ToolbarView} #toolbarView
 */
/**
 * Options of a focusable toolbar view:
 *
 * * `isContextual`: Marks the higher priority toolbar. For example when there are 2 visible toolbars,
 * it allows to distinguish which toolbar should be focused first after the `alt+f10` keystroke
 * * `beforeFocus`: A callback executed before the `ToolbarView` gains focus upon the `Alt+F10` keystroke.
 * * `afterBlur`: A callback executed after `ToolbarView` loses focus upon `Esc` keystroke but before the focus goes back to the `origin`.
 *
 * @member {Object} #options
 */
// Returns a number (weight) for a toolbar definition. Visible toolbars have a higher priority and so do
// contextual toolbars (displayed in the context of a content, for instance, an image toolbar).
//
// A standard invisible toolbar is the heaviest. A visible contextual toolbar is the lightest.
//
// @private
// @param {module:core/editor/editorui~FocusableToolbarDefinition} toolbarDef A toolbar definition to be weighted.
// @returns {Number}
function getToolbarDefinitionWeight(toolbarDef) {
    const { toolbarView, options } = toolbarDef;
    let weight = 10;
    // Prioritize already visible toolbars. They should get focused first.
    if (isVisible(toolbarView.element)) {
        weight--;
    }
    // Prioritize contextual toolbars. They are displayed at the selection.
    if (options.isContextual) {
        weight--;
    }
    return weight;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/editor/utils/attachtoform.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */


/**
 * @module core/editor/utils/attachtoform
 */
/**
 * Checks if the editor is initialized on a `<textarea>` element that belongs to a form. If yes, it updates the editor's element
 * content before submitting the form.
 *
 * This helper requires the {@link module:core/editor/utils/elementapimixin~ElementApi ElementApi interface}.
 *
 * @param {module:core/editor/editor~Editor} editor Editor instance.
 */
function attachToForm(editor) {
    if (!lodash_es_isFunction(editor.updateSourceElement)) {
        /**
         * The editor passed to `attachToForm()` must implement the
         * {@link module:core/editor/utils/elementapimixin~ElementApi} interface.
         *
         * @error attachtoform-missing-elementapi-interface
         */
        throw new CKEditorError('attachtoform-missing-elementapi-interface', editor);
    }
    const sourceElement = editor.sourceElement;
    // Only when replacing a textarea which is inside of a form element.
    if (isTextArea(sourceElement) && sourceElement.form) {
        let originalSubmit;
        const form = sourceElement.form;
        const onSubmit = () => editor.updateSourceElement();
        // Replace the original form#submit() to call a custom submit function first.
        // Check if #submit is a function because the form might have an input named "submit".
        if (lodash_es_isFunction(form.submit)) {
            originalSubmit = form.submit;
            form.submit = () => {
                onSubmit();
                originalSubmit.apply(form);
            };
        }
        // Update the replaced textarea with data before each form#submit event.
        form.addEventListener('submit', onSubmit);
        // Remove the submit listener and revert the original submit method on
        // editor#destroy.
        editor.on('destroy', () => {
            form.removeEventListener('submit', onSubmit);
            if (originalSubmit) {
                form.submit = originalSubmit;
            }
        });
    }
}
function isTextArea(sourceElement) {
    return !!sourceElement && sourceElement.tagName.toLowerCase() === 'textarea';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/editor/utils/dataapimixin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/editor/utils/dataapimixin
 */
/**
 * Implementation of the {@link module:core/editor/utils/dataapimixin~DataApi}.
 *
 * @mixin DataApiMixin
 * @implements module:core/editor/utils/dataapimixin~DataApi
 */
function DataApiMixin(base) {
    class Mixin extends base {
        setData(data) {
            this.data.set(data);
        }
        getData(options) {
            return this.data.get(options);
        }
    }
    return Mixin;
}
// Backward compatibility with `mix`.
{
    const mixin = DataApiMixin(Object);
    DataApiMixin.setData = mixin.prototype.setData;
    DataApiMixin.getData = mixin.prototype.getData;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-utils/src/dom/setdatainelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module utils/dom/setdatainelement
 */
/* globals HTMLTextAreaElement */
/**
 * Sets data in a given element.
 *
 * @param {HTMLElement} el The element in which the data will be set.
 * @param {String} data The data string.
 */
function setDataInElement(el, data) {
    if (el instanceof HTMLTextAreaElement) {
        el.value = data;
    }
    el.innerHTML = data;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/editor/utils/elementapimixin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* eslint-disable @typescript-eslint/explicit-module-boundary-types */


/**
 * @module core/editor/utils/elementapimixin
 */
/**
 * Implementation of the {@link module:core/editor/utils/elementapimixin~ElementApi}.
 *
 * @mixin ElementApiMixin
 * @implements module:core/editor/utils/elementapimixin~ElementApi
 */
function ElementApiMixin(base) {
    class Mixin extends base {
        updateSourceElement(data = this.data.get()) {
            if (!this.sourceElement) {
                /**
                 * Cannot update the source element of a detached editor.
                 *
                 * The {@link ~ElementApi#updateSourceElement `updateSourceElement()`} method cannot be called if you did not
                 * pass an element to `Editor.create()`.
                 *
                 * @error editor-missing-sourceelement
                 */
                throw new CKEditorError('editor-missing-sourceelement', this);
            }
            const shouldUpdateSourceElement = this.config.get('updateSourceElementOnDestroy');
            const isSourceElementTextArea = this.sourceElement instanceof HTMLTextAreaElement;
            // The data returned by the editor might be unsafe, so we want to prevent rendering
            // unsafe content inside the source element different than <textarea>, which is considered
            // secure. This behaviour could be changed by setting the `updateSourceElementOnDestroy`
            // configuration option to `true`.
            if (!shouldUpdateSourceElement && !isSourceElementTextArea) {
                setDataInElement(this.sourceElement, '');
                return;
            }
            setDataInElement(this.sourceElement, data);
        }
    }
    return Mixin;
}
// Backward compatibility with `mix`.
ElementApiMixin.updateSourceElement = ElementApiMixin(Object).prototype.updateSourceElement;

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/editor/utils/securesourceelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module core/editor/utils/securesourceelement
 */
/**
 * Marks the source element on which the editor was initialized. This prevents other editor instances from using this element.
 *
 * Running multiple editor instances on the same source element causes various issues and it is
 * crucial this helper is called as soon as the source element is known to prevent collisions.
 *
 * @param {module:core/editor/editor~Editor} editor Editor instance.
 */
function secureSourceElement(editor) {
    const sourceElement = editor.sourceElement;
    // If the editor was initialized without specifying an element, we don't need to secure anything.
    if (!sourceElement) {
        return;
    }
    if (sourceElement.ckeditorInstance) {
        /**
         * A DOM element used to create the editor (e.g.
         * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`})
         * has already been used to create another editor instance. Make sure each editor is
         * created with an unique DOM element.
         *
         * @error editor-source-element-already-used
         * @param {HTMLElement} element DOM element that caused the collision.
         */
        throw new CKEditorError('editor-source-element-already-used', editor);
    }
    sourceElement.ckeditorInstance = editor;
    editor.once('destroy', () => {
        delete sourceElement.ckeditorInstance;
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/pendingactions.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core/pendingactions
 */




/**
 * The list of pending editor actions.
 *
 * This plugin should be used to synchronise plugins that execute long-lasting actions
 * (e.g. file upload) with the editor integration. It gives the developer who integrates the editor
 * an easy way to check if there are any actions pending whenever such information is needed.
 * All plugins that register a pending action also provide a message about the action that is ongoing
 * which can be displayed to the user. This lets them decide if they want to interrupt the action or wait.
 *
 * Adding and updating a pending action:
 *
 * 		const pendingActions = editor.plugins.get( 'PendingActions' );
 * 		const action = pendingActions.add( 'Upload in progress: 0%.' );
 *
 *		// You can update the message:
 * 		action.message = 'Upload in progress: 10%.';
 *
 * Removing a pending action:
 *
 * 		const pendingActions = editor.plugins.get( 'PendingActions' );
 * 		const action = pendingActions.add( 'Unsaved changes.' );
 *
 * 		pendingActions.remove( action );
 *
 * Getting pending actions:
 *
 * 		const pendingActions = editor.plugins.get( 'PendingActions' );
 *
 * 		const action1 = pendingActions.add( 'Action 1' );
 * 		const action2 = pendingActions.add( 'Action 2' );
 *
 * 		pendingActions.first; // Returns action1
 * 		Array.from( pendingActions ); // Returns [ action1, action2 ]
 *
 * This plugin is used by features like {@link module:upload/filerepository~FileRepository} to register their ongoing actions
 * and by features like {@link module:autosave/autosave~Autosave} to detect whether there are any ongoing actions.
 * Read more about saving the data in the {@glink installation/advanced/saving-data Saving and getting data} guide.
 *
 * @extends module:core/contextplugin~ContextPlugin
 */
class PendingActions extends ContextPlugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'PendingActions';
    }
    /**
     * @inheritDoc
     */
    init() {
        /**
         * Defines whether there is any registered pending action.
         *
         * @readonly
         * @observable
         * @member {Boolean} #hasAny
         */
        this.set('hasAny', false);
        /**
         * A list of pending actions.
         *
         * @private
         * @type {module:utils/collection~Collection}
         */
        this._actions = new Collection({ idProperty: '_id' });
        this._actions.delegate('add', 'remove').to(this);
    }
    /**
     * Adds an action to the list of pending actions.
     *
     * This method returns an action object with an observable message property.
     * The action object can be later used in the {@link #remove} method. It also allows you to change the message.
     *
     * @param {String} message The action message.
     * @returns {Object} An observable object that represents a pending action.
     */
    add(message) {
        if (typeof message !== 'string') {
            /**
             * The message must be a string.
             *
             * @error pendingactions-add-invalid-message
             */
            throw new CKEditorError('pendingactions-add-invalid-message', this);
        }
        const action = new Observable();
        action.set('message', message);
        this._actions.add(action);
        this.hasAny = true;
        return action;
    }
    /**
     * Removes an action from the list of pending actions.
     *
     * @param {Object} action An action object.
     */
    remove(action) {
        this._actions.remove(action);
        this.hasAny = !!this._actions.length;
    }
    /**
     * Returns the first action from the list or null when list is empty
     *
     * returns {Object|null} The pending action object.
     */
    get first() {
        return this._actions.get(0);
    }
    /**
     * Iterable interface.
     *
     * @returns {Iterable.<*>}
     */
    [Symbol.iterator]() {
        return this._actions[Symbol.iterator]();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/cancel.svg
/* harmony default export */ const cancel = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.591 10.177 4.243 4.242a1 1 0 0 1-1.415 1.415l-4.242-4.243-4.243 4.243a1 1 0 0 1-1.414-1.415l4.243-4.242L4.52 5.934A1 1 0 0 1 5.934 4.52l4.243 4.243 4.242-4.243a1 1 0 1 1 1.415 1.414l-4.243 4.243z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/caption.svg
/* harmony default export */ const caption = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 16h9a1 1 0 0 1 0 2H2a1 1 0 0 1 0-2z\"/><path d=\"M17 1a2 2 0 0 1 2 2v9a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h14zm0 1.5H3a.5.5 0 0 0-.492.41L2.5 3v9a.5.5 0 0 0 .41.492L3 12.5h14a.5.5 0 0 0 .492-.41L17.5 12V3a.5.5 0 0 0-.41-.492L17 2.5z\" fill-opacity=\".6\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/check.svg
/* harmony default export */ const check = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6.972 16.615a.997.997 0 0 1-.744-.292l-4.596-4.596a1 1 0 1 1 1.414-1.414l3.926 3.926 9.937-9.937a1 1 0 0 1 1.414 1.415L7.717 16.323a.997.997 0 0 1-.745.292z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/cog.svg
/* harmony default export */ const cog = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.333 2 .19 2.263a5.899 5.899 0 0 1 1.458.604L14.714 3.4 16.6 5.286l-1.467 1.733c.263.452.468.942.605 1.46L18 8.666v2.666l-2.263.19a5.899 5.899 0 0 1-.604 1.458l1.467 1.733-1.886 1.886-1.733-1.467a5.899 5.899 0 0 1-1.46.605L11.334 18H8.667l-.19-2.263a5.899 5.899 0 0 1-1.458-.604L5.286 16.6 3.4 14.714l1.467-1.733a5.899 5.899 0 0 1-.604-1.458L2 11.333V8.667l2.262-.189a5.899 5.899 0 0 1 .605-1.459L3.4 5.286 5.286 3.4l1.733 1.467a5.899 5.899 0 0 1 1.46-.605L8.666 2h2.666zM10 6.267a3.733 3.733 0 1 0 0 7.466 3.733 3.733 0 0 0 0-7.466z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/eraser.svg
/* harmony default export */ const eraser = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m8.636 9.531-2.758 3.94a.5.5 0 0 0 .122.696l3.224 2.284h1.314l2.636-3.736L8.636 9.53zm.288 8.451L5.14 15.396a2 2 0 0 1-.491-2.786l6.673-9.53a2 2 0 0 1 2.785-.49l3.742 2.62a2 2 0 0 1 .491 2.785l-7.269 10.053-2.147-.066z\"/><path d=\"M4 18h5.523v-1H4zm-2 0h1v-1H2z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/low-vision.svg
/* harmony default export */ const low_vision = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M5.085 6.22 2.943 4.078a.75.75 0 1 1 1.06-1.06l2.592 2.59A11.094 11.094 0 0 1 10 5.068c4.738 0 8.578 3.101 8.578 5.083 0 1.197-1.401 2.803-3.555 3.887l1.714 1.713a.75.75 0 0 1-.09 1.138.488.488 0 0 1-.15.084.75.75 0 0 1-.821-.16L6.17 7.304c-.258.11-.51.233-.757.365l6.239 6.24-.006.005.78.78c-.388.094-.78.166-1.174.215l-1.11-1.11h.011L4.55 8.197a7.2 7.2 0 0 0-.665.514l-.112.098 4.897 4.897-.005.006 1.276 1.276a10.164 10.164 0 0 1-1.477-.117l-.479-.479-.009.009-4.863-4.863-.022.031a2.563 2.563 0 0 0-.124.2c-.043.077-.08.158-.108.241a.534.534 0 0 0-.028.133.29.29 0 0 0 .008.072.927.927 0 0 0 .082.226c.067.133.145.26.234.379l3.242 3.365.025.01.59.623c-3.265-.918-5.59-3.155-5.59-4.668 0-1.194 1.448-2.838 3.663-3.93zm7.07.531a4.632 4.632 0 0 1 1.108 5.992l.345.344.046-.018a9.313 9.313 0 0 0 2-1.112c.256-.187.5-.392.727-.613.137-.134.27-.277.392-.431.072-.091.141-.185.203-.286.057-.093.107-.19.148-.292a.72.72 0 0 0 .036-.12.29.29 0 0 0 .008-.072.492.492 0 0 0-.028-.133.999.999 0 0 0-.036-.096 2.165 2.165 0 0 0-.071-.145 2.917 2.917 0 0 0-.125-.2 3.592 3.592 0 0 0-.263-.335 5.444 5.444 0 0 0-.53-.523 7.955 7.955 0 0 0-1.054-.768 9.766 9.766 0 0 0-1.879-.891c-.337-.118-.68-.219-1.027-.301zm-2.85.21-.069.002a.508.508 0 0 0-.254.097.496.496 0 0 0-.104.679.498.498 0 0 0 .326.199l.045.005c.091.003.181.003.272.012a2.45 2.45 0 0 1 2.017 1.513c.024.061.043.125.069.185a.494.494 0 0 0 .45.287h.008a.496.496 0 0 0 .35-.158.482.482 0 0 0 .13-.335.638.638 0 0 0-.048-.219 3.379 3.379 0 0 0-.36-.723 3.438 3.438 0 0 0-2.791-1.543l-.028-.001h-.013z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/image.svg
/* harmony default export */ const icons_image = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6.91 10.54c.26-.23.64-.21.88.03l3.36 3.14 2.23-2.06a.64.64 0 0 1 .87 0l2.52 2.97V4.5H3.2v10.12l3.71-4.08zm10.27-7.51c.6 0 1.09.47 1.09 1.05v11.84c0 .59-.49 1.06-1.09 1.06H2.79c-.6 0-1.09-.47-1.09-1.06V4.08c0-.58.49-1.05 1.1-1.05h14.38zm-5.22 5.56a1.96 1.96 0 1 1 3.4-1.96 1.96 1.96 0 0 1-3.4 1.96z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/align-bottom.svg
/* harmony default export */ const align_bottom = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m9.239 13.938-2.88-1.663a.75.75 0 0 1 .75-1.3L9 12.067V4.75a.75.75 0 1 1 1.5 0v7.318l1.89-1.093a.75.75 0 0 1 .75 1.3l-2.879 1.663a.752.752 0 0 1-.511.187.752.752 0 0 1-.511-.187zM4.25 17a.75.75 0 1 1 0-1.5h10.5a.75.75 0 0 1 0 1.5H4.25z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/align-middle.svg
/* harmony default export */ const align_middle = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M9.75 11.875a.752.752 0 0 1 .508.184l2.883 1.666a.75.75 0 0 1-.659 1.344l-.091-.044-1.892-1.093.001 4.318a.75.75 0 1 1-1.5 0v-4.317l-1.89 1.092a.75.75 0 0 1-.75-1.3l2.879-1.663a.752.752 0 0 1 .51-.187zM15.25 9a.75.75 0 1 1 0 1.5H4.75a.75.75 0 1 1 0-1.5h10.5zM9.75.375a.75.75 0 0 1 .75.75v4.318l1.89-1.093.092-.045a.75.75 0 0 1 .659 1.344l-2.883 1.667a.752.752 0 0 1-.508.184.752.752 0 0 1-.511-.187L6.359 5.65a.75.75 0 0 1 .75-1.299L9 5.442V1.125a.75.75 0 0 1 .75-.75z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/align-top.svg
/* harmony default export */ const align_top = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m10.261 7.062 2.88 1.663a.75.75 0 0 1-.75 1.3L10.5 8.933v7.317a.75.75 0 1 1-1.5 0V8.932l-1.89 1.093a.75.75 0 0 1-.75-1.3l2.879-1.663a.752.752 0 0 1 .511-.187.752.752 0 0 1 .511.187zM15.25 4a.75.75 0 1 1 0 1.5H4.75a.75.75 0 0 1 0-1.5h10.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/align-left.svg
/* harmony default export */ const align_left = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 3.75c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm0 8c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm0 4c0 .414.336.75.75.75h9.929a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm0-8c0 .414.336.75.75.75h9.929a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/align-center.svg
/* harmony default export */ const align_center = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 3.75c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm0 8c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm2.286 4c0 .414.336.75.75.75h9.928a.75.75 0 1 0 0-1.5H5.036a.75.75 0 0 0-.75.75zm0-8c0 .414.336.75.75.75h9.928a.75.75 0 1 0 0-1.5H5.036a.75.75 0 0 0-.75.75z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/align-right.svg
/* harmony default export */ const align_right = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M18 3.75a.75.75 0 0 1-.75.75H2.75a.75.75 0 1 1 0-1.5h14.5a.75.75 0 0 1 .75.75zm0 8a.75.75 0 0 1-.75.75H2.75a.75.75 0 1 1 0-1.5h14.5a.75.75 0 0 1 .75.75zm0 4a.75.75 0 0 1-.75.75H7.321a.75.75 0 1 1 0-1.5h9.929a.75.75 0 0 1 .75.75zm0-8a.75.75 0 0 1-.75.75H7.321a.75.75 0 1 1 0-1.5h9.929a.75.75 0 0 1 .75.75z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/align-justify.svg
/* harmony default export */ const align_justify = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 3.75c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm0 8c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm0 4c0 .414.336.75.75.75h9.929a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm0-8c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-left.svg
/* harmony default export */ const object_left = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path opacity=\".5\" d=\"M2 3h16v1.5H2zm0 12h16v1.5H2z\"/><path d=\"M12.003 7v5.5a1 1 0 0 1-1 1H2.996a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h8.007a1 1 0 0 1 1 1zm-1.506.5H3.5V12h6.997V7.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-center.svg
/* harmony default export */ const object_center = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path opacity=\".5\" d=\"M2 3h16v1.5H2zm0 12h16v1.5H2z\"/><path d=\"M15.003 7v5.5a1 1 0 0 1-1 1H5.996a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h8.007a1 1 0 0 1 1 1zm-1.506.5H6.5V12h6.997V7.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-right.svg
/* harmony default export */ const object_right = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path opacity=\".5\" d=\"M2 3h16v1.5H2zm0 12h16v1.5H2z\"/><path d=\"M18.003 7v5.5a1 1 0 0 1-1 1H8.996a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h8.007a1 1 0 0 1 1 1zm-1.506.5H9.5V12h6.997V7.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-full-width.svg
/* harmony default export */ const object_full_width = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path opacity=\".5\" d=\"M2 3h16v1.5H2zm0 12h16v1.5H2z\"/><path d=\"M18 7v5.5a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h14a1 1 0 0 1 1 1zm-1.505.5H3.504V12h12.991V7.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-inline.svg
/* harmony default export */ const object_inline = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path opacity=\".5\" d=\"M2 3h16v1.5H2zm11.5 9H18v1.5h-4.5zM2 15h16v1.5H2z\"/><path d=\"M12.003 7v5.5a1 1 0 0 1-1 1H2.996a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h8.007a1 1 0 0 1 1 1zm-1.506.5H3.5V12h6.997V7.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-inline-left.svg
/* harmony default export */ const object_inline_left = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path opacity=\".5\" d=\"M2 3h16v1.5H2zm11.5 9H18v1.5h-4.5zm0-3H18v1.5h-4.5zm0-3H18v1.5h-4.5zM2 15h16v1.5H2z\"/><path d=\"M12.003 7v5.5a1 1 0 0 1-1 1H2.996a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h8.007a1 1 0 0 1 1 1zm-1.506.5H3.5V12h6.997V7.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-inline-right.svg
/* harmony default export */ const object_inline_right = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path opacity=\".5\" d=\"M2 3h16v1.5H2zm0 12h16v1.5H2zm0-9h5v1.5H2zm0 3h5v1.5H2zm0 3h5v1.5H2z\"/><path d=\"M18.003 7v5.5a1 1 0 0 1-1 1H8.996a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h8.007a1 1 0 0 1 1 1zm-1.506.5H9.5V12h6.997V7.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-size-full.svg
/* harmony default export */ const object_size_full = ("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\"><path d=\"M2.5 17v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zM1 15.5v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm0-2v1h-1v-1h1zm-19 0v1H0v-1h1zM14.5 2v1h-1V2h1zm2 0v1h-1V2h1zm2 0v1h-1V2h1zm-8 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm8 0v1h-1V2h1zm-10 0v1h-1V2h1z\"/><path d=\"M18.095 2H1.905C.853 2 0 2.895 0 4v12c0 1.105.853 2 1.905 2h16.19C19.147 18 20 17.105 20 16V4c0-1.105-.853-2-1.905-2zm0 1.5c.263 0 .476.224.476.5v12c0 .276-.213.5-.476.5H1.905a.489.489 0 0 1-.476-.5V4c0-.276.213-.5.476-.5h16.19z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-size-large.svg
/* harmony default export */ const object_size_large = ("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\"><path d=\"M2.5 17v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zM1 15.5v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm0-2v1h-1v-1h1zm-19 0v1H0v-1h1zM14.5 2v1h-1V2h1zm2 0v1h-1V2h1zm2 0v1h-1V2h1zm-8 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm8 0v1h-1V2h1zm-10 0v1h-1V2h1z\"/><path d=\"M13 6H2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2zm0 1.5a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5H2a.5.5 0 0 1-.5-.5V8a.5.5 0 0 1 .5-.5h11z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-size-small.svg
/* harmony default export */ const object_size_small = ("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\"><path d=\"M2.5 17v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zM1 15.5v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm0-2v1h-1v-1h1zm-19 0v1H0v-1h1zM14.5 2v1h-1V2h1zm2 0v1h-1V2h1zm2 0v1h-1V2h1zm-8 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm8 0v1h-1V2h1zm-10 0v1h-1V2h1z\"/><path d=\"M7 10H2a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h5a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2zm0 1.5a.5.5 0 0 1 .5.5v4a.5.5 0 0 1-.5.5H2a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 .5-.5h5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/object-size-medium.svg
/* harmony default export */ const object_size_medium = ("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\"><path d=\"M2.5 17v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zm2 0v1h-1v-1h1zM1 15.5v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm-19-2v1H0v-1h1zm19 0v1h-1v-1h1zm0-2v1h-1v-1h1zm-19 0v1H0v-1h1zM14.5 2v1h-1V2h1zm2 0v1h-1V2h1zm2 0v1h-1V2h1zm-8 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm-2 0v1h-1V2h1zm8 0v1h-1V2h1zm-10 0v1h-1V2h1z\"/><path d=\"M10 8H2a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2zm0 1.5a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-.5.5H2a.5.5 0 0 1-.5-.5v-6a.5.5 0 0 1 .5-.5h8z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/pencil.svg
/* harmony default export */ const pencil = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m7.3 17.37-.061.088a1.518 1.518 0 0 1-.934.535l-4.178.663-.806-4.153a1.495 1.495 0 0 1 .187-1.058l.056-.086L8.77 2.639c.958-1.351 2.803-1.076 4.296-.03 1.497 1.047 2.387 2.693 1.433 4.055L7.3 17.37zM9.14 4.728l-5.545 8.346 3.277 2.294 5.544-8.346L9.14 4.728zM6.07 16.512l-3.276-2.295.53 2.73 2.746-.435zM9.994 3.506 13.271 5.8c.316-.452-.16-1.333-1.065-1.966-.905-.634-1.895-.78-2.212-.328zM8 18.5 9.375 17H19v1.5H8z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/pilcrow.svg
/* harmony default export */ const icons_pilcrow = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6.999 2H15a1 1 0 0 1 0 2h-1.004v13a1 1 0 1 1-2 0V4H8.999v13a1 1 0 1 1-2 0v-7A4 4 0 0 1 3 6a4 4 0 0 1 3.999-4z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/quote.svg
/* harmony default export */ const quote = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3 10.423a6.5 6.5 0 0 1 6.056-6.408l.038.67C6.448 5.423 5.354 7.663 5.22 10H9c.552 0 .5.432.5.986v4.511c0 .554-.448.503-1 .503h-5c-.552 0-.5-.449-.5-1.003v-4.574zm8 0a6.5 6.5 0 0 1 6.056-6.408l.038.67c-2.646.739-3.74 2.979-3.873 5.315H17c.552 0 .5.432.5.986v4.511c0 .554-.448.503-1 .503h-5c-.552 0-.5-.449-.5-1.003v-4.574z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/three-vertical-dots.svg
/* harmony default export */ const three_vertical_dots = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><circle cx=\"9.5\" cy=\"4.5\" r=\"1.5\"/><circle cx=\"9.5\" cy=\"10.5\" r=\"1.5\"/><circle cx=\"9.5\" cy=\"16.5\" r=\"1.5\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/bold.svg
/* harmony default export */ const bold = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10.187 17H5.773c-.637 0-1.092-.138-1.364-.415-.273-.277-.409-.718-.409-1.323V4.738c0-.617.14-1.062.419-1.332.279-.27.73-.406 1.354-.406h4.68c.69 0 1.288.041 1.793.124.506.083.96.242 1.36.478.341.197.644.447.906.75a3.262 3.262 0 0 1 .808 2.162c0 1.401-.722 2.426-2.167 3.075C15.05 10.175 16 11.315 16 13.01a3.756 3.756 0 0 1-2.296 3.504 6.1 6.1 0 0 1-1.517.377c-.571.073-1.238.11-2 .11zm-.217-6.217H7v4.087h3.069c1.977 0 2.965-.69 2.965-2.072 0-.707-.256-1.22-.768-1.537-.512-.319-1.277-.478-2.296-.478zM7 5.13v3.619h2.606c.729 0 1.292-.067 1.69-.2a1.6 1.6 0 0 0 .91-.765c.165-.267.247-.566.247-.897 0-.707-.26-1.176-.778-1.409-.519-.232-1.31-.348-2.375-.348H7z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/paragraph.svg
/* harmony default export */ const paragraph = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10.5 5.5H7v5h3.5a2.5 2.5 0 1 0 0-5zM5 3h6.5v.025a5 5 0 0 1 0 9.95V13H7v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/plus.svg
/* harmony default export */ const plus = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10 2a1 1 0 0 0-1 1v6H3a1 1 0 1 0 0 2h6v6a1 1 0 1 0 2 0v-6h6a1 1 0 1 0 0-2h-6V3a1 1 0 0 0-1-1Z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/text.svg
/* harmony default export */ const icons_text = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#a)\"><path d=\"M9.816 11.5 7.038 4.785 4.261 11.5h5.555Zm.62 1.5H3.641l-1.666 4.028H.312l5.789-14h1.875l5.789 14h-1.663L10.436 13Z\"/><path clip-rule=\"evenodd\" d=\"m12.09 17-.534-1.292.848-1.971.545 1.319L12.113 17h-.023Zm1.142-5.187.545 1.319L15.5 9.13l1.858 4.316h-3.45l.398.965h3.467L18.887 17H20l-3.873-9h-1.254l-1.641 3.813Z\"/></g><defs><clipPath id=\"a\"><path d=\"M0 0h20v20H0z\"/></clipPath></defs></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/theme/icons/importexport.svg
/* harmony default export */ const importexport = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><g clip-path=\"url(#a)\"><path clip-rule=\"evenodd\" d=\"M19 4.5 14 0H3v12.673l.868-1.041c.185-.222.4-.402.632-.54V1.5h8v5h5v7.626a2.24 2.24 0 0 1 1.5.822V4.5ZM14 5V2l3.3 3H14Zm-3.692 12.5c.062.105.133.206.213.303L11.52 19H8v-.876a2.243 2.243 0 0 0 1.82-.624h.488Zm7.518-.657a.75.75 0 0 0-1.152-.96L15.5 17.29V12H14v5.29l-1.174-1.408a.75.75 0 0 0-1.152.96l2.346 2.816a.95.95 0 0 0 1.46 0l2.346-2.815Zm-15.056-.38a.75.75 0 0 1-.096-1.056l2.346-2.815a.95.95 0 0 1 1.46 0l2.346 2.815a.75.75 0 1 1-1.152.96L6.5 14.96V20H5v-5.04l-1.174 1.408a.75.75 0 0 1-1.056.096Z\"/></g><defs><clipPath id=\"a\"><path d=\"M0 0h20v20H0z\"/></clipPath></defs></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-core/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module core
 */














































const icons = {
    bold: bold,
    cancel: cancel,
    caption: caption,
    check: check,
    cog: cog,
    eraser: eraser,
    image: icons_image,
    lowVision: low_vision,
    importExport: importexport,
    paragraph: paragraph,
    plus: plus,
    text: icons_text,
    alignBottom: align_bottom,
    alignMiddle: align_middle,
    alignTop: align_top,
    alignLeft: align_left,
    alignCenter: align_center,
    alignRight: align_right,
    alignJustify: align_justify,
    objectLeft: object_inline_left,
    objectCenter: object_center,
    objectRight: object_inline_right,
    objectFullWidth: object_full_width,
    objectInline: object_inline,
    objectBlockLeft: object_left,
    objectBlockRight: object_right,
    objectSizeFull: object_size_full,
    objectSizeLarge: object_size_large,
    objectSizeSmall: object_size_small,
    objectSizeMedium: object_size_medium,
    pencil: pencil,
    pilcrow: icons_pilcrow,
    quote: quote,
    threeVerticalDots: three_vertical_dots
};

;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/core.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/core
 */


;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/utils
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/bindings/clickoutsidehandler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/bindings/clickoutsidehandler
 */
/* global document */
/**
 * Handles clicking **outside** of a specified set of elements, then fires an action.
 *
 * **Note**: Actually, the action is executed upon `mousedown`, not `click`. It prevents
 * certain issues when the user keeps holding the mouse button and the UI cannot react
 * properly.
 *
 * @param {Object} options Configuration options.
 * @param {module:utils/dom/emittermixin~Emitter} options.emitter The emitter to which this behavior
 * should be added.
 * @param {Function} options.activator Function returning a `Boolean`, to determine whether the handler is active.
 * @param {Array.<HTMLElement>} options.contextElements HTML elements that determine the scope of the
 * handler. Clicking any of them or their descendants will **not** fire the callback.
 * @param {Function} options.callback An action executed by the handler.
 */
function clickoutsidehandler_clickOutsideHandler({ emitter, activator, callback, contextElements }) {
    emitter.listenTo(document, 'mousedown', (evt, domEvt) => {
        if (!activator()) {
            return;
        }
        // Check if `composedPath` is `undefined` in case the browser does not support native shadow DOM.
        // Can be removed when all supported browsers support native shadow DOM.
        const path = typeof domEvt.composedPath == 'function' ? domEvt.composedPath() : [];
        for (const contextElement of contextElements) {
            if (contextElement.contains(domEvt.target) || path.includes(contextElement)) {
                return;
            }
        }
        callback();
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/bindings/injectcsstransitiondisabler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/bindings/injectcsstransitiondisabler
 */
/**
 * A decorator that brings the possibility to temporarily disable CSS transitions using
 * {@link module:ui/view~View} methods. It is helpful when, for instance, the transitions should not happen
 * when the view is first displayed but they should work normal in other cases.
 *
 * The methods to control the CSS transitions are:
 * * `disableCssTransitions()` – Adds the `.ck-transitions-disabled` class to the
 * {@link module:ui/view~View#element view element}.
 * * `enableCssTransitions()` – Removes the `.ck-transitions-disabled` class from the
 * {@link module:ui/view~View#element view element}.
 *
 * **Note**: This helper extends the {@link module:ui/view~View#template template} and must be used **after**
 * {@link module:ui/view~View#setTemplate} is called:
 *
 *		import injectCssTransitionDisabler from '@ckeditor/ckeditor5-ui/src/bindings/injectcsstransitiondisabler';
 *
 *		class MyView extends View {
 *			constructor() {
 *				super();
 *
 *				// ...
 *
 *				this.setTemplate( { ... } );
 *
 *				// ...
 *
 *				injectCssTransitionDisabler( this );
 *
 *				// ...
 *			}
 *		}
 *
 * The usage comes down to:
 *
 *		const view = new MyView();
 *
 *		// ...
 *
 *		view.disableCssTransitions();
 *		view.show();
 *		view.enableCssTransitions();
 *
 * @param {module:ui/view~View} view View instance that should get this functionality.
 */
function injectCssTransitionDisabler(view) {
    const decorated = view;
    decorated.set('_isCssTransitionsDisabled', false);
    decorated.disableCssTransitions = () => {
        decorated._isCssTransitionsDisabled = true;
    };
    decorated.enableCssTransitions = () => {
        decorated._isCssTransitionsDisabled = false;
    };
    decorated.extendTemplate({
        attributes: {
            class: [
                decorated.bindTemplate.if('_isCssTransitionsDisabled', 'ck-transitions-disabled')
            ]
        }
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/bindings/submithandler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/bindings/submithandler
 */
/**
 * A handler useful for {@link module:ui/view~View views} working as HTML forms. It intercepts a native DOM
 * `submit` event, prevents the default web browser behavior (navigation and page reload) and
 * fires the `submit` event on a view instead. Such a custom event can be then used by any
 * {@link module:utils/dom/emittermixin~Emitter emitter}, e.g. to serialize the form data.
 *
 *		import submitHandler from '@ckeditor/ckeditor5-ui/src/bindings/submithandler';
 *
 *		// ...
 *
 *		class AnyFormView extends View {
 *			constructor() {
 *				super();
 *
 *				// ...
 *
 *				submitHandler( {
 *					view: this
 *				} );
 *			}
 *		}
 *
 *		// ...
 *
 *		const view = new AnyFormView();
 *
 *		// A sample listener attached by an emitter working with the view.
 *		this.listenTo( view, 'submit', () => {
 *			saveTheFormData();
 *			hideTheForm();
 *		} );
 *
 * @param {Object} [options] Configuration options.
 * @param {module:ui/view~View} options.view The view which DOM `submit` events should be handled.
 */
function submitHandler({ view }) {
    view.listenTo(view.element, 'submit', (evt, domEvt) => {
        domEvt.preventDefault();
        view.fire('submit');
    }, { useCapture: true });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/bindings/addkeyboardhandlingforgrid.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/bindings/addkeyboardhandlingforgrid
 */
/**
 * A helper that adds a keyboard navigation support (arrow up/down/left/right) for grids.
 *
 * @param {Object} options Configuration options.
 * @param {module:utils/keystrokehandler~KeystrokeHandler} options.keystrokeHandler Keystroke handler to register navigation with arrow
 * keys.
 * @param {module:utils/focustracker~FocusTracker} options.focusTracker A focus tracker for grid elements.
 * @param {module:ui/viewcollection~ViewCollection} options.gridItems A collection of grid items.
 * @param {Number|Function} options.numberOfColumns Number of columns in the grid. Can be specified as a function that returns
 * the number (e.g. for responsive grids).
 */
function addKeyboardHandlingForGrid({ keystrokeHandler, focusTracker, gridItems, numberOfColumns }) {
    const getNumberOfColumns = typeof numberOfColumns === 'number' ? () => numberOfColumns : numberOfColumns;
    keystrokeHandler.set('arrowright', getGridItemFocuser((focusedElementIndex, gridItems) => {
        if (focusedElementIndex === gridItems.length - 1) {
            return 0;
        }
        else {
            return focusedElementIndex + 1;
        }
    }));
    keystrokeHandler.set('arrowleft', getGridItemFocuser((focusedElementIndex, gridItems) => {
        if (focusedElementIndex === 0) {
            return gridItems.length - 1;
        }
        else {
            return focusedElementIndex - 1;
        }
    }));
    keystrokeHandler.set('arrowup', getGridItemFocuser((focusedElementIndex, gridItems) => {
        let nextIndex = focusedElementIndex - getNumberOfColumns();
        if (nextIndex < 0) {
            nextIndex = focusedElementIndex + getNumberOfColumns() * Math.floor(gridItems.length / getNumberOfColumns());
            if (nextIndex > gridItems.length - 1) {
                nextIndex -= getNumberOfColumns();
            }
        }
        return nextIndex;
    }));
    keystrokeHandler.set('arrowdown', getGridItemFocuser((focusedElementIndex, gridItems) => {
        let nextIndex = focusedElementIndex + getNumberOfColumns();
        if (nextIndex > gridItems.length - 1) {
            nextIndex = focusedElementIndex % getNumberOfColumns();
        }
        return nextIndex;
    }));
    function getGridItemFocuser(getIndexToFocus) {
        return (evt) => {
            const focusedElement = gridItems.find(item => item.element === focusTracker.focusedElement);
            const focusedElementIndex = gridItems.getIndex(focusedElement);
            const nextIndexToFocus = getIndexToFocus(focusedElementIndex, gridItems);
            gridItems.get(nextIndexToFocus).focus();
            evt.stopPropagation();
            evt.preventDefault();
        };
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/editorui/bodycollection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/editorui/bodycollection
 */
/* globals document */



/**
 * This is a special {@link module:ui/viewcollection~ViewCollection} dedicated to elements that are detached
 * from the DOM structure of the editor, like panels, icons, etc.
 *
 * The body collection is available in the {@link module:ui/editorui/editoruiview~EditorUIView#body `editor.ui.view.body`} property.
 * Any plugin can add a {@link module:ui/view~View view} to this collection.
 * These views will render in a container placed directly in the `<body>` element.
 * The editor will detach and destroy this collection when the editor will be {@link module:core/editor/editor~Editor#destroy destroyed}.
 *
 * If you need to control the life cycle of the body collection on your own, you can create your own instance of this class.
 *
 * A body collection will render itself automatically in the DOM body element as soon as you call {@link ~BodyCollection#attachToDom}.
 * If you create multiple body collections, this class will create a special wrapper element in the DOM to limit the number of
 * elements created directly in the body and remove it when the last body collection will be
 * {@link ~BodyCollection#detachFromDom detached}.
 *
 * @extends module:ui/viewcollection~ViewCollection
 */
class BodyCollection extends ViewCollection {
    /**
     * Creates a new instance of the {@link module:ui/editorui/bodycollection~BodyCollection}.
     *
     * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor editor's locale} instance.
     * @param {Iterable.<module:ui/view~View>} [initialItems] The initial items of the collection.
     */
    constructor(locale, initialItems = []) {
        super(initialItems);
        /**
         * The {@link module:core/editor/editor~Editor#locale editor's locale} instance.
         * See the view {@link module:ui/view~View#locale locale} property.
         *
         * @member {module:utils/locale~Locale}
         */
        this.locale = locale;
    }
    /**
     * Attaches the body collection to the DOM body element. You need to execute this method to render the content of
     * the body collection.
     */
    attachToDom() {
        /**
         * The element holding elements of the body region.
         *
         * @protected
         * @member {HTMLElement} #_bodyCollectionContainer
         */
        this._bodyCollectionContainer = new Template({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-reset_all',
                    'ck-body',
                    'ck-rounded-corners'
                ],
                dir: this.locale.uiLanguageDirection
            },
            children: this
        }).render();
        let wrapper = document.querySelector('.ck-body-wrapper');
        if (!wrapper) {
            wrapper = createElement(document, 'div', { class: 'ck-body-wrapper' });
            document.body.appendChild(wrapper);
        }
        wrapper.appendChild(this._bodyCollectionContainer);
    }
    /**
     * Detaches the collection from the DOM structure. Use this method when you do not need to use the body collection
     * anymore to clean-up the DOM structure.
     */
    detachFromDom() {
        super.destroy();
        if (this._bodyCollectionContainer) {
            this._bodyCollectionContainer.remove();
        }
        const wrapper = document.querySelector('.ck-body-wrapper');
        if (wrapper && wrapper.childElementCount == 0) {
            wrapper.remove();
        }
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/icon/icon.css
var icon_icon = __webpack_require__(1174);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/icon/icon.css

            

var icon_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

icon_options.insert = "head";
icon_options.singleton = true;

var icon_update = injectStylesIntoStyleTag_default()(icon_icon/* default */.Z, icon_options);



/* harmony default export */ const components_icon_icon = (icon_icon/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/icon/iconview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* global DOMParser */
/**
 * @module ui/icon/iconview
 */


/**
 * The icon view class.
 *
 * @extends module:ui/view~View
 */
class IconView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor() {
        super();
        const bind = this.bindTemplate;
        /**
         * The SVG source of the icon.
         *
         * @observable
         * @member {String} #content
         */
        this.set('content', '');
        /**
         * This attribute specifies the boundaries to which the
         * icon content should stretch.
         *
         * @observable
         * @default '0 0 20 20'
         * @member {String} #viewBox
         */
        this.set('viewBox', '0 0 20 20');
        /**
         * The fill color of the child `path.ck-icon__fill`.
         *
         * @observable
         * @default ''
         * @member {String} #fillColor
         */
        this.set('fillColor', '');
        /**
         * When set true (default), all parts of the icon inherit the fill color from the CSS `color` property of the
         * icon's DOM parent.
         *
         * This effectively makes the icon monochromatic and allows it to change its fill color dynamically, for instance,
         * when a {@link module:ui/button/buttonview~ButtonView} displays an icon and it switches between different states
         * (pushed, hovered, etc.) the icon will follow along.
         *
         * **Note**: For the monochromatic icon to render properly, it must be made up of shapes that can be filled
         * with color instead of, for instance, paths with strokes. Be sure to use the *outline stroke* tool
         * (the name could be different in your vector graphics editor) before exporting your icon. Also, remove any
         * excess `fill="..."` attributes that could break the color inheritance.
         *
         * **Note**: If you want to preserve the original look of your icon and disable dynamic color inheritance,
         * set this flag to `false`.
         *
         * @observable
         * @default true
         * @member {Boolean} #isColorInherited
         */
        this.set('isColorInherited', true);
        this.setTemplate({
            tag: 'svg',
            ns: 'http://www.w3.org/2000/svg',
            attributes: {
                class: [
                    'ck',
                    'ck-icon',
                    // Exclude icon internals from the CSS reset to allow rich (non-monochromatic) icons
                    // (https://github.com/ckeditor/ckeditor5/issues/12599).
                    'ck-reset_all-excluded',
                    // The class to remove the dynamic color inheritance is toggleable
                    // (https://github.com/ckeditor/ckeditor5/issues/12599).
                    bind.if('isColorInherited', 'ck-icon_inherit-color')
                ],
                viewBox: bind.to('viewBox')
            }
        });
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this._updateXMLContent();
        this._colorFillPaths();
        // This is a hack for lack of innerHTML binding.
        // See: https://github.com/ckeditor/ckeditor5-ui/issues/99.
        this.on('change:content', () => {
            this._updateXMLContent();
            this._colorFillPaths();
        });
        this.on('change:fillColor', () => {
            this._colorFillPaths();
        });
    }
    /**
     * Updates the {@link #element} with the value of {@link #content}.
     *
     * @private
     */
    _updateXMLContent() {
        if (this.content) {
            const parsed = new DOMParser().parseFromString(this.content.trim(), 'image/svg+xml');
            const svg = parsed.querySelector('svg');
            const viewBox = svg.getAttribute('viewBox');
            if (viewBox) {
                this.viewBox = viewBox;
            }
            // Preserve presentational attributes of the <svg> element from the source.
            // They can affect rendering of the entire icon (https://github.com/ckeditor/ckeditor5/issues/12597).
            for (const { name, value } of Array.from(svg.attributes)) {
                if (IconView.presentationalAttributeNames.includes(name)) {
                    this.element.setAttribute(name, value);
                }
            }
            while (this.element.firstChild) {
                this.element.removeChild(this.element.firstChild);
            }
            while (svg.childNodes.length > 0) {
                this.element.appendChild(svg.childNodes[0]);
            }
        }
    }
    /**
     * Fills all child `path.ck-icon__fill` with the `#fillColor`.
     *
     * @private
     */
    _colorFillPaths() {
        if (this.fillColor) {
            this.element.querySelectorAll('.ck-icon__fill').forEach(path => {
                path.style.fill = this.fillColor;
            });
        }
    }
}
/**
 * A list of presentational attributes that can be set on the `<svg>` element and should be preserved
 * when the icon {@link module:ui/icon/iconview~IconView#content content} is loaded.
 *
 * See the [specification](https://www.w3.org/TR/SVG/styling.html#TermPresentationAttribute) to learn more.
 *
 * @protected
 * @member {Array.<String>} module:ui/icon/iconview~IconView.presentationalAttributeNames
 */
IconView.presentationalAttributeNames = [
    'alignment-baseline', 'baseline-shift', 'clip-path', 'clip-rule', 'color', 'color-interpolation',
    'color-interpolation-filters', 'color-rendering', 'cursor', 'direction', 'display', 'dominant-baseline', 'fill', 'fill-opacity',
    'fill-rule', 'filter', 'flood-color', 'flood-opacity', 'font-family', 'font-size', 'font-size-adjust', 'font-stretch', 'font-style',
    'font-variant', 'font-weight', 'image-rendering', 'letter-spacing', 'lighting-color', 'marker-end', 'marker-mid', 'marker-start',
    'mask', 'opacity', 'overflow', 'paint-order', 'pointer-events', 'shape-rendering', 'stop-color', 'stop-opacity', 'stroke',
    'stroke-dasharray', 'stroke-dashoffset', 'stroke-linecap', 'stroke-linejoin', 'stroke-miterlimit', 'stroke-opacity', 'stroke-width',
    'text-anchor', 'text-decoration', 'text-overflow', 'text-rendering', 'transform', 'unicode-bidi', 'vector-effect',
    'visibility', 'white-space', 'word-spacing', 'writing-mode'
];

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/button/button.css
var button_button = __webpack_require__(4499);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/button/button.css

            

var button_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

button_options.insert = "head";
button_options.singleton = true;

var button_update = injectStylesIntoStyleTag_default()(button_button/* default */.Z, button_options);



/* harmony default export */ const components_button_button = (button_button/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/button/buttonview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/button/buttonview
 */






/**
 * The button view class.
 *
 *		const view = new ButtonView();
 *
 *		view.set( {
 *			label: 'A button',
 *			keystroke: 'Ctrl+B',
 *			tooltip: true,
 *			withText: true
 *		} );
 *
 *		view.render();
 *
 *		document.body.append( view.element );
 *
 * @extends module:ui/view~View
 * @implements module:ui/button/button~Button
 */
class buttonview_ButtonView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        const ariaLabelUid = uid();
        // Implement the Button interface.
        this.set('class', undefined);
        this.set('labelStyle', undefined);
        this.set('icon', undefined);
        this.set('isEnabled', true);
        this.set('isOn', false);
        this.set('isVisible', true);
        this.set('isToggleable', false);
        this.set('keystroke', undefined);
        this.set('label', undefined);
        this.set('tabindex', -1);
        this.set('tooltip', false);
        this.set('tooltipPosition', 's');
        this.set('type', 'button');
        this.set('withText', false);
        this.set('withKeystroke', false);
        /**
         * Collection of the child views inside of the button {@link #element}.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.children = this.createCollection();
        /**
         * Label of the button view. It is configurable using the {@link #label label attribute}.
         *
         * @readonly
         * @member {module:ui/view~View} #labelView
         */
        this.labelView = this._createLabelView(ariaLabelUid);
        /**
         * The icon view of the button. Will be added to {@link #children} when the
         * {@link #icon icon attribute} is defined.
         *
         * @readonly
         * @member {module:ui/icon/iconview~IconView} #iconView
         */
        this.iconView = new IconView();
        this.iconView.extendTemplate({
            attributes: {
                class: 'ck-button__icon'
            }
        });
        /**
         * A view displaying the keystroke of the button next to the {@link #labelView label}.
         * Added to {@link #children} when the {@link #withKeystroke `withKeystroke` attribute}
         * is defined.
         *
         * @readonly
         * @member {module:ui/view/view~View} #keystrokeView
         */
        this.keystrokeView = this._createKeystrokeView();
        /**
         * Tooltip of the button bound to the template.
         *
         * @see #tooltip
         * @see #_getTooltipString
         * @private
         * @observable
         * @member {String|undefined} #_tooltipString
         */
        this.bind('_tooltipString').to(this, 'tooltip', this, 'label', this, 'keystroke', this._getTooltipString.bind(this));
        const template = {
            tag: 'button',
            attributes: {
                class: [
                    'ck',
                    'ck-button',
                    bind.to('class'),
                    bind.if('isEnabled', 'ck-disabled', value => !value),
                    bind.if('isVisible', 'ck-hidden', value => !value),
                    bind.to('isOn', value => value ? 'ck-on' : 'ck-off'),
                    bind.if('withText', 'ck-button_with-text'),
                    bind.if('withKeystroke', 'ck-button_with-keystroke')
                ],
                type: bind.to('type', value => value ? value : 'button'),
                tabindex: bind.to('tabindex'),
                'aria-labelledby': `ck-editor__aria-label_${ariaLabelUid}`,
                'aria-disabled': bind.if('isEnabled', true, value => !value),
                'aria-pressed': bind.to('isOn', value => this.isToggleable ? String(!!value) : false),
                'data-cke-tooltip-text': bind.to('_tooltipString'),
                'data-cke-tooltip-position': bind.to('tooltipPosition')
            },
            children: this.children,
            on: {
                click: bind.to(evt => {
                    // We can't make the button disabled using the disabled attribute, because it won't be focusable.
                    // Though, shouldn't this condition be moved to the button controller?
                    if (this.isEnabled) {
                        this.fire('execute');
                    }
                    else {
                        // Prevent the default when button is disabled, to block e.g.
                        // automatic form submitting. See ckeditor/ckeditor5-link#74.
                        evt.preventDefault();
                    }
                })
            }
        };
        // On Safari we have to force the focus on a button on click as it's the only browser
        // that doesn't do that automatically. See #12115.
        if (src_env.isSafari) {
            template.on.mousedown = bind.to(evt => {
                this.focus();
                evt.preventDefault();
            });
        }
        this.setTemplate(template);
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        if (this.icon) {
            this.iconView.bind('content').to(this, 'icon');
            this.children.add(this.iconView);
        }
        this.children.add(this.labelView);
        if (this.withKeystroke && this.keystroke) {
            this.children.add(this.keystrokeView);
        }
    }
    /**
     * Focuses the {@link #element} of the button.
     */
    focus() {
        this.element.focus();
    }
    /**
     * Creates a label view instance and binds it with button attributes.
     *
     * @private
     * @param {String} ariaLabelUid The aria label UID.
     * @returns {module:ui/view~View}
     */
    _createLabelView(ariaLabelUid) {
        const labelView = new src_view_View();
        const bind = this.bindTemplate;
        labelView.setTemplate({
            tag: 'span',
            attributes: {
                class: [
                    'ck',
                    'ck-button__label'
                ],
                style: bind.to('labelStyle'),
                id: `ck-editor__aria-label_${ariaLabelUid}`
            },
            children: [
                {
                    text: this.bindTemplate.to('label')
                }
            ]
        });
        return labelView;
    }
    /**
     * Creates a view that displays a keystroke next to a {@link #labelView label }
     * and binds it with button attributes.
     *
     * @private
     * @returns {module:ui/view~View}
     */
    _createKeystrokeView() {
        const keystrokeView = new src_view_View();
        keystrokeView.setTemplate({
            tag: 'span',
            attributes: {
                class: [
                    'ck',
                    'ck-button__keystroke'
                ]
            },
            children: [
                {
                    text: this.bindTemplate.to('keystroke', text => getEnvKeystrokeText(text))
                }
            ]
        });
        return keystrokeView;
    }
    /**
     * Gets the text for the tooltip from the combination of
     * {@link #tooltip}, {@link #label} and {@link #keystroke} attributes.
     *
     * @private
     * @see #tooltip
     * @see #_tooltipString
     * @param {Boolean|String|Function} tooltip Button tooltip.
     * @param {String} label Button label.
     * @param {String} keystroke Button keystroke.
     * @returns {String}
     */
    _getTooltipString(tooltip, label, keystroke) {
        if (tooltip) {
            if (typeof tooltip == 'string') {
                return tooltip;
            }
            else {
                if (keystroke) {
                    keystroke = getEnvKeystrokeText(keystroke);
                }
                if (tooltip instanceof Function) {
                    return tooltip(label, keystroke);
                }
                else {
                    return `${label}${keystroke ? ` (${keystroke})` : ''}`;
                }
            }
        }
        return '';
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/button/switchbutton.css
var switchbutton = __webpack_require__(9681);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/button/switchbutton.css

            

var switchbutton_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

switchbutton_options.insert = "head";
switchbutton_options.singleton = true;

var switchbutton_update = injectStylesIntoStyleTag_default()(switchbutton/* default */.Z, switchbutton_options);



/* harmony default export */ const button_switchbutton = (switchbutton/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/button/switchbuttonview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/button/switchbuttonview
 */



/**
 * The switch button view class.
 *
 *		const view = new SwitchButtonView();
 *
 *		view.set( {
 *			withText: true,
 *			label: 'Switch me!'
 *		} );
 *
 *		view.render();
 *
 *		document.body.append( view.element );
 *
 * @extends module:ui/button/buttonview~ButtonView
 */
class SwitchButtonView extends buttonview_ButtonView {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        this.isToggleable = true;
        /**
         * The toggle switch of the button.
         *
         * @readonly
         * @member {module:ui/view~View} #toggleSwitchView
         */
        this.toggleSwitchView = this._createToggleView();
        this.extendTemplate({
            attributes: {
                class: 'ck-switchbutton'
            }
        });
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.children.add(this.toggleSwitchView);
    }
    /**
     * Creates a toggle child view.
     *
     * @private
     * @returns {module:ui/view~View}
     */
    _createToggleView() {
        const toggleSwitchView = new src_view_View();
        toggleSwitchView.setTemplate({
            tag: 'span',
            attributes: {
                class: [
                    'ck',
                    'ck-button__toggle'
                ]
            },
            children: [
                {
                    tag: 'span',
                    attributes: {
                        class: [
                            'ck',
                            'ck-button__toggle__inner'
                        ]
                    }
                }
            ]
        });
        return toggleSwitchView;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/colorgrid/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Returns color configuration options as defined in `editor.config.(fontColor|fontBackgroundColor).colors` or
 * `editor.config.table.(tableProperties|tableCellProperties).(background|border).colors
 * but processed to account for editor localization in the correct language.
 *
 * Note: The reason behind this method is that 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.
 *
 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
 * @param {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>} options
 * @returns {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>}.
 */
function getLocalizedColorOptions(locale, options) {
    const t = locale.t;
    const localizedColorNames = {
        Black: t('Black'),
        'Dim grey': t('Dim grey'),
        Grey: t('Grey'),
        'Light grey': t('Light grey'),
        White: t('White'),
        Red: t('Red'),
        Orange: t('Orange'),
        Yellow: t('Yellow'),
        'Light green': t('Light green'),
        Green: t('Green'),
        Aquamarine: t('Aquamarine'),
        Turquoise: t('Turquoise'),
        'Light blue': t('Light blue'),
        Blue: t('Blue'),
        Purple: t('Purple')
    };
    return options.map(colorOption => {
        const label = localizedColorNames[colorOption.label];
        if (label && label != colorOption.label) {
            colorOption.label = label;
        }
        return colorOption;
    });
}
/**
 * Creates a unified color definition object from color configuration options.
 * The object contains the information necessary to both render the UI and initialize the conversion.
 *
 * @param {module:ui/colorgrid/colorgrid~ColorDefinition} options
 * @returns {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>}
 */
function normalizeColorOptions(options) {
    return options
        .map(normalizeSingleColorDefinition)
        .filter(option => !!option);
}
// Creates a normalized color definition from the user-defined configuration.
// The "normalization" means it will create full
// {@link module:ui/colorgrid/colorgrid~ColorDefinition `ColorDefinition-like`}
// object for string values, and add a `view` property, for each definition.
//
// @param {String|module:ui/colorgrid/colorgrid~ColorDefinition}
// @returns {module:ui/colorgrid/colorgrid~ColorDefinition}
function normalizeSingleColorDefinition(color) {
    if (typeof color === 'string') {
        return {
            model: color,
            label: color,
            hasBorder: false,
            view: {
                name: 'span',
                styles: {
                    color
                }
            }
        };
    }
    else {
        return {
            model: color.color,
            label: color.label || color.color,
            hasBorder: color.hasBorder === undefined ? false : color.hasBorder,
            view: {
                name: 'span',
                styles: {
                    color: `${color.color}`
                }
            }
        };
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/icons/color-tile-check.svg
/* harmony default export */ const color_tile_check = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path class=\"ck-icon__fill\" d=\"M16.935 5.328a2 2 0 0 1 0 2.829l-7.778 7.778a2 2 0 0 1-2.829 0L3.5 13.107a1.999 1.999 0 1 1 2.828-2.829l.707.707a1 1 0 0 0 1.414 0l5.658-5.657a2 2 0 0 1 2.828 0z\"/><path d=\"M14.814 6.035 8.448 12.4a1 1 0 0 1-1.414 0l-1.413-1.415A1 1 0 1 0 4.207 12.4l2.829 2.829a1 1 0 0 0 1.414 0l7.778-7.778a1 1 0 1 0-1.414-1.415z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/colorgrid/colortileview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/colorgrid/colortile
 */


/**
 * This class represents a single color tile in the {@link module:ui/colorgrid/colorgrid~ColorGridView}.
 *
 * @extends module:ui/button/buttonview~ButtonView
 */
class ColorTileView extends buttonview_ButtonView {
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * String representing a color shown as tile's background.
         *
         * @type {String}
         */
        this.set('color', undefined);
        /**
         * A flag that toggles a special CSS class responsible for displaying
         * a border around the button.
         *
         * @type {Boolean}
         */
        this.set('hasBorder', false);
        this.icon = color_tile_check;
        this.extendTemplate({
            attributes: {
                style: {
                    backgroundColor: bind.to('color')
                },
                class: [
                    'ck',
                    'ck-color-grid__tile',
                    bind.if('hasBorder', 'ck-color-table__color-tile_bordered')
                ]
            }
        });
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.iconView.fillColor = 'hsl(0, 0%, 100%)';
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/colorgrid/colorgrid.css
var colorgrid = __webpack_require__(4923);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/colorgrid/colorgrid.css

            

var colorgrid_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

colorgrid_options.insert = "head";
colorgrid_options.singleton = true;

var colorgrid_update = injectStylesIntoStyleTag_default()(colorgrid/* default */.Z, colorgrid_options);



/* harmony default export */ const colorgrid_colorgrid = (colorgrid/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/colorgrid/colorgridview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/colorgrid/colorgrid
 */






/**
 * A grid of {@link module:ui/colorgrid/colortile~ColorTileView color tiles}.
 *
 * @extends module:ui/view~View
 */
class ColorGridView extends src_view_View {
    /**
     * Creates an instance of a color grid containing {@link module:ui/colorgrid/colortile~ColorTileView tiles}.
     *
     * @param {module:utils/locale~Locale} [locale] The localization services instance.
     * @param {Object} options Component configuration
     * @param {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>} [options.colorDefinitions] Array with definitions
     * required to create the {@link module:ui/colorgrid/colortile~ColorTileView tiles}.
     * @param {Number} [options.columns=5] A number of columns to display the tiles.
     */
    constructor(locale, options) {
        super(locale);
        const colorDefinitions = options && options.colorDefinitions || [];
        /**
         * A number of columns for the tiles grid.
         *
         * @readonly
         * @member {Number}
         */
        this.columns = options && options.columns ? options.columns : 5;
        const viewStyleAttribute = {
            gridTemplateColumns: `repeat( ${this.columns}, 1fr)`
        };
        /**
         * The color of the currently selected color tile in {@link #items}.
         *
         * @observable
         * @type {String}
         */
        this.set('selectedColor', undefined);
        /**
         * Collection of the child tile views.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.items = this.createCollection();
        /**
         * Tracks information about DOM focus in the grid.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker}
         */
        this.focusTracker = new FocusTracker();
        /**
         * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
         *
         * @readonly
         * @member {module:utils/keystrokehandler~KeystrokeHandler}
         */
        this.keystrokes = new KeystrokeHandler();
        this.items.on('add', (evt, colorTile) => {
            colorTile.isOn = colorTile.color === this.selectedColor;
        });
        colorDefinitions.forEach(color => {
            const colorTile = new ColorTileView();
            colorTile.set({
                color: color.color,
                label: color.label,
                tooltip: true,
                hasBorder: color.options.hasBorder
            });
            colorTile.on('execute', () => {
                this.fire('execute', {
                    value: color.color,
                    hasBorder: color.options.hasBorder,
                    label: color.label
                });
            });
            this.items.add(colorTile);
        });
        this.setTemplate({
            tag: 'div',
            children: this.items,
            attributes: {
                class: [
                    'ck',
                    'ck-color-grid'
                ],
                style: viewStyleAttribute
            }
        });
        this.on('change:selectedColor', (evt, name, selectedColor) => {
            for (const item of this.items) {
                item.isOn = item.color === selectedColor;
            }
        });
    }
    /**
     * Focuses the first focusable in {@link #items}.
     */
    focus() {
        if (this.items.length) {
            this.items.first.focus();
        }
    }
    /**
     * Focuses the last focusable in {@link #items}.
     */
    focusLast() {
        if (this.items.length) {
            this.items.last.focus();
        }
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        // Items added before rendering should be known to the #focusTracker.
        for (const item of this.items) {
            this.focusTracker.add(item.element);
        }
        this.items.on('add', (evt, item) => {
            this.focusTracker.add(item.element);
        });
        this.items.on('remove', (evt, item) => {
            this.focusTracker.remove(item.element);
        });
        // Start listening for the keystrokes coming from #element.
        this.keystrokes.listenTo(this.element);
        addKeyboardHandlingForGrid({
            keystrokeHandler: this.keystrokes,
            focusTracker: this.focusTracker,
            gridItems: this.items,
            numberOfColumns: this.columns
        });
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this.focusTracker.destroy();
        this.keystrokes.destroy();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/icons/dropdown-arrow.svg
/* harmony default export */ const dropdown_arrow = ("<svg viewBox=\"0 0 10 10\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M.941 4.523a.75.75 0 1 1 1.06-1.06l3.006 3.005 3.005-3.005a.75.75 0 1 1 1.06 1.06l-3.549 3.55a.75.75 0 0 1-1.168-.136L.941 4.523z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/dropdown/button/dropdownbuttonview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/dropdown/button/dropdownbuttonview
 */



/**
 * The default dropdown button view class.
 *
 *		const view = new DropdownButtonView();
 *
 *		view.set( {
 *			label: 'A button',
 *			keystroke: 'Ctrl+B',
 *			tooltip: true
 *		} );
 *
 *		view.render();
 *
 *		document.body.append( view.element );
 *
 * Also see the {@link module:ui/dropdown/utils~createDropdown `createDropdown()` util}.
 *
 * @implements module:ui/dropdown/button/dropdownbutton~DropdownButton
 * @extends module:ui/button/buttonview~ButtonView
 */
class DropdownButtonView extends buttonview_ButtonView {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        /**
         * An icon that displays arrow to indicate a dropdown button.
         *
         * @readonly
         * @member {module:ui/icon/iconview~IconView}
         */
        this.arrowView = this._createArrowView();
        this.extendTemplate({
            attributes: {
                'aria-haspopup': true,
                'aria-expanded': this.bindTemplate.to('isOn', value => String(value))
            }
        });
        // The DropdownButton interface expects the open event upon which will open the dropdown.
        this.delegate('execute').to(this, 'open');
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.children.add(this.arrowView);
    }
    /**
     * Creates a {@link module:ui/icon/iconview~IconView} instance as {@link #arrowView}.
     *
     * @private
     * @returns {module:ui/icon/iconview~IconView}
     */
    _createArrowView() {
        const arrowView = new IconView();
        arrowView.content = dropdown_arrow;
        arrowView.extendTemplate({
            attributes: {
                class: 'ck-dropdown__arrow'
            }
        });
        return arrowView;
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/splitbutton.css
var splitbutton = __webpack_require__(66);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/splitbutton.css

            

var splitbutton_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

splitbutton_options.insert = "head";
splitbutton_options.singleton = true;

var splitbutton_update = injectStylesIntoStyleTag_default()(splitbutton/* default */.Z, splitbutton_options);



/* harmony default export */ const dropdown_splitbutton = (splitbutton/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/dropdown/button/splitbuttonview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/dropdown/button/splitbuttonview
 */






/**
 * The split button view class.
 *
 *		const view = new SplitButtonView();
 *
 *		view.set( {
 *			label: 'A button',
 *			keystroke: 'Ctrl+B',
 *			tooltip: true
 *		} );
 *
 *		view.render();
 *
 *		document.body.append( view.element );
 *
 * Also see the {@link module:ui/dropdown/utils~createDropdown `createDropdown()` util}.
 *
 * @implements module:ui/dropdown/button/dropdownbutton~DropdownButton
 * @extends module:ui/view~View
 */
class SplitButtonView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        // Implement the Button interface.
        this.set('class', undefined);
        this.set('labelStyle', undefined);
        this.set('icon', undefined);
        this.set('isEnabled', true);
        this.set('isOn', false);
        this.set('isToggleable', false);
        this.set('isVisible', true);
        this.set('keystroke', undefined);
        this.set('withKeystroke', false);
        this.set('label', undefined);
        this.set('tabindex', -1);
        this.set('tooltip', false);
        this.set('tooltipPosition', 's');
        this.set('type', 'button');
        this.set('withText', false);
        /**
         * Collection of the child views inside of the split button {@link #element}.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.children = this.createCollection();
        /**
         * A main button of split button.
         *
         * @readonly
         * @member {module:ui/button/buttonview~ButtonView}
         */
        this.actionView = this._createActionView();
        /**
         * A secondary button of split button that opens dropdown.
         *
         * @readonly
         * @member {module:ui/button/buttonview~ButtonView}
         */
        this.arrowView = this._createArrowView();
        /**
         * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages
         * keystrokes of the split button:
         *
         * * <kbd>▶</kbd> moves focus to arrow view when action view is focused,
         * * <kbd>◀</kbd> moves focus to action view when arrow view is focused.
         *
         * @readonly
         * @member {module:utils/keystrokehandler~KeystrokeHandler}
         */
        this.keystrokes = new KeystrokeHandler();
        /**
         * Tracks information about DOM focus in the dropdown.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker}
         */
        this.focusTracker = new FocusTracker();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-splitbutton',
                    bind.to('class'),
                    bind.if('isVisible', 'ck-hidden', value => !value),
                    this.arrowView.bindTemplate.if('isOn', 'ck-splitbutton_open')
                ]
            },
            children: this.children
        });
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.children.add(this.actionView);
        this.children.add(this.arrowView);
        this.focusTracker.add(this.actionView.element);
        this.focusTracker.add(this.arrowView.element);
        this.keystrokes.listenTo(this.element);
        // Overrides toolbar focus cycling behavior.
        this.keystrokes.set('arrowright', (evt, cancel) => {
            if (this.focusTracker.focusedElement === this.actionView.element) {
                this.arrowView.focus();
                cancel();
            }
        });
        // Overrides toolbar focus cycling behavior.
        this.keystrokes.set('arrowleft', (evt, cancel) => {
            if (this.focusTracker.focusedElement === this.arrowView.element) {
                this.actionView.focus();
                cancel();
            }
        });
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this.focusTracker.destroy();
        this.keystrokes.destroy();
    }
    /**
     * Focuses the {@link #actionView#element} of the action part of split button.
     */
    focus() {
        this.actionView.focus();
    }
    /**
     * Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #actionView} and binds it with main split button
     * attributes.
     *
     * @private
     * @returns {module:ui/button/buttonview~ButtonView}
     */
    _createActionView() {
        const actionView = new buttonview_ButtonView();
        actionView.bind('icon', 'isEnabled', 'isOn', 'isToggleable', 'keystroke', 'label', 'tabindex', 'tooltip', 'tooltipPosition', 'type', 'withText').to(this);
        actionView.extendTemplate({
            attributes: {
                class: 'ck-splitbutton__action'
            }
        });
        actionView.delegate('execute').to(this);
        return actionView;
    }
    /**
     * Creates a {@link module:ui/button/buttonview~ButtonView} instance as {@link #arrowView} and binds it with main split button
     * attributes.
     *
     * @private
     * @returns {module:ui/button/buttonview~ButtonView}
     */
    _createArrowView() {
        const arrowView = new buttonview_ButtonView();
        const bind = arrowView.bindTemplate;
        arrowView.icon = dropdown_arrow;
        arrowView.extendTemplate({
            attributes: {
                class: [
                    'ck-splitbutton__arrow'
                ],
                'data-cke-tooltip-disabled': bind.to('isOn'),
                'aria-haspopup': true,
                'aria-expanded': bind.to('isOn', value => String(value))
            }
        });
        arrowView.bind('isEnabled').to(this);
        arrowView.bind('label').to(this);
        arrowView.bind('tooltip').to(this);
        arrowView.delegate('execute').to(this, 'open');
        return arrowView;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/dropdown/dropdownpanelview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/dropdown/dropdownpanelview
 */


/**
 * The dropdown panel view class.
 *
 * See {@link module:ui/dropdown/dropdownview~DropdownView} to learn about the common usage.
 *
 * @extends module:ui/view~View
 */
class DropdownPanelView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * Controls whether the panel is visible.
         *
         * @observable
         * @member {Boolean} #isVisible
         */
        this.set('isVisible', false);
        /**
         * The position of the panel, relative to the parent.
         *
         * This property is reflected in the CSS class set to {@link #element} that controls
         * the position of the panel.
         *
         * @observable
         * @default 'se'
         * @member {'s'|'se'|'sw'|'sme'|'smw'|'n'|'ne'|'nw'|'nme'|'nmw'} #position
         */
        this.set('position', 'se');
        /**
         * Collection of the child views in this panel.
         *
         * A common child type is the {@link module:ui/list/listview~ListView} and {@link module:ui/toolbar/toolbarview~ToolbarView}.
         * See {@link module:ui/dropdown/utils~addListToDropdown} and
         * {@link module:ui/dropdown/utils~addToolbarToDropdown} to learn more about child views of dropdowns.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.children = this.createCollection();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-reset',
                    'ck-dropdown__panel',
                    bind.to('position', value => `ck-dropdown__panel_${value}`),
                    bind.if('isVisible', 'ck-dropdown__panel-visible')
                ]
            },
            children: this.children,
            on: {
                // Drag and drop in the panel should not break the selection in the editor.
                // https://github.com/ckeditor/ckeditor5-ui/issues/228
                selectstart: bind.to(evt => evt.preventDefault())
            }
        });
    }
    /**
     * Focuses the first view in the {@link #children} collection.
     *
     * See also {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable}.
     */
    focus() {
        if (this.children.length) {
            const firstChild = this.children.first;
            if (typeof firstChild.focus === 'function') {
                firstChild.focus();
            }
            else {
                /**
                 * The child view of a dropdown could not be focused because it is missing the `focus()` method.
                 *
                 * This warning appears when a dropdown {@link module:ui/dropdown/dropdownview~DropdownView#isOpen gets open} and it
                 * attempts to focus the {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView#children first child} of its panel
                 * but the child does not implement the
                 * {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable focusable interface}.
                 *
                 * Focusing the content of a dropdown on open greatly improves the accessibility. Please make sure the view instance
                 * provides the `focus()` method for the best user experience.
                 *
                 * @error ui-dropdown-panel-focus-child-missing-focus
                 * @param {module:ui/view~View} childView
                 * @param {module:ui/dropdown/dropdownpanelview~DropdownPanelView} dropdownPanel
                 */
                logWarning('ui-dropdown-panel-focus-child-missing-focus', { childView: this.children.first, dropdownPanel: this });
            }
        }
    }
    /**
     * Focuses the view element or last item in view collection on opening dropdown's panel.
     *
     * See also {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable}.
     */
    focusLast() {
        if (this.children.length) {
            const lastChild = this.children.last;
            if (typeof lastChild.focusLast === 'function') {
                lastChild.focusLast();
            }
            else {
                lastChild.focus();
            }
        }
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/dropdown.css
var dropdown = __webpack_require__(3488);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/dropdown.css

            

var dropdown_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

dropdown_options.insert = "head";
dropdown_options.singleton = true;

var dropdown_update = injectStylesIntoStyleTag_default()(dropdown/* default */.Z, dropdown_options);



/* harmony default export */ const dropdown_dropdown = (dropdown/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/dropdown/dropdownview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/dropdown/dropdownview
 */





/**
 * The dropdown view class. It manages the dropdown button and dropdown panel.
 *
 * In most cases, the easiest way to create a dropdown is by using the {@link module:ui/dropdown/utils~createDropdown}
 * util:
 *
 *		const dropdown = createDropdown( locale );
 *
 *		// Configure dropdown's button properties:
 *		dropdown.buttonView.set( {
 *			label: 'A dropdown',
 *			withText: true
 *		} );
 *
 *		dropdown.render();
 *
 *		dropdown.panelView.element.textContent = 'Content of the panel';
 *
 *		// Will render a dropdown with a panel containing a "Content of the panel" text.
 *		document.body.appendChild( dropdown.element );
 *
 * If you want to add a richer content to the dropdown panel, you can use the {@link module:ui/dropdown/utils~addListToDropdown}
 * and {@link module:ui/dropdown/utils~addToolbarToDropdown} helpers. See more examples in
 * {@link module:ui/dropdown/utils~createDropdown} documentation.
 *
 * If you want to create a completely custom dropdown, then you can compose it manually:
 *
 *		const button = new DropdownButtonView( locale );
 *		const panel = new DropdownPanelView( locale );
 *		const dropdown = new DropdownView( locale, button, panel );
 *
 *		button.set( {
 *			label: 'A dropdown',
 *			withText: true
 *		} );
 *
 *		dropdown.render();
 *
 *		panel.element.textContent = 'Content of the panel';
 *
 *		// Will render a dropdown with a panel containing a "Content of the panel" text.
 *		document.body.appendChild( dropdown.element );
 *
 * However, dropdown created this way will contain little behavior. You will need to implement handlers for actions
 * such as {@link module:ui/bindings/clickoutsidehandler~clickOutsideHandler clicking outside an open dropdown}
 * (which should close it) and support for arrow keys inside the panel. Therefore, unless you really know what
 * you do and you really need to do it, it is recommended to use the {@link module:ui/dropdown/utils~createDropdown} helper.
 *
 * @extends module:ui/view~View
 */
class DropdownView extends src_view_View {
    /**
     * Creates an instance of the dropdown.
     *
     * Also see {@link #render}.
     *
     * @param {module:utils/locale~Locale} [locale] The localization services instance.
     * @param {module:ui/dropdown/button/dropdownbutton~DropdownButton} buttonView
     * @param {module:ui/dropdown/dropdownpanelview~DropdownPanelView} panelView
     */
    constructor(locale, buttonView, panelView) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * Button of the dropdown view. Clicking the button opens the {@link #panelView}.
         *
         * @readonly
         * @member {module:ui/button/buttonview~ButtonView} #buttonView
         */
        this.buttonView = buttonView;
        /**
         * Panel of the dropdown. It opens when the {@link #buttonView} is
         * {@link module:ui/button/buttonview~ButtonView#event:execute executed} (i.e. clicked).
         *
         * Child views can be added to the panel's `children` collection:
         *
         *		dropdown.panelView.children.add( childView );
         *
         * See {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView#children} and
         * {@link module:ui/viewcollection~ViewCollection#add}.
         *
         * @readonly
         * @member {module:ui/dropdown/dropdownpanelview~DropdownPanelView} #panelView
         */
        this.panelView = panelView;
        /**
         * Controls whether the dropdown view is open, i.e. shows or hides the {@link #panelView panel}.
         *
         * **Note**: When the dropdown gets open, it will attempt to call `focus()` on the first child of its {@link #panelView}.
         * See {@link module:ui/dropdown/utils~addToolbarToDropdown}, {@link module:ui/dropdown/utils~addListToDropdown}, and
         * {@link module:ui/dropdown/utils~focusChildOnDropdownOpen} to learn more about focus management in dropdowns.
         *
         * @observable
         * @member {Boolean} #isOpen
         */
        this.set('isOpen', false);
        /**
         * Controls whether the dropdown is enabled, i.e. it can be clicked and execute an action.
         *
         * See {@link module:ui/button/buttonview~ButtonView#isEnabled}.
         *
         * @observable
         * @member {Boolean} #isEnabled
         */
        this.set('isEnabled', true);
        /**
         * (Optional) The additional CSS class set on the dropdown {@link #element}.
         *
         * @observable
         * @member {String} #class
         */
        this.set('class', undefined);
        /**
         * (Optional) The `id` attribute of the dropdown (i.e. to pair with a `<label>` element).
         *
         * @observable
         * @member {String} #id
         */
        this.set('id', undefined);
        /**
         * The position of the panel, relative to the dropdown.
         *
         * **Note**: When `'auto'`, the panel will use one of the remaining positions to stay
         * in the viewport, visible to the user. The positions correspond directly to
         * {@link module:ui/dropdown/dropdownview~DropdownView.defaultPanelPositions default panel positions}.
         *
         * **Note**: This value has an impact on the
         * {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView#position} property
         * each time the panel becomes {@link #isOpen open}.
         *
         * @observable
         * @default 'auto'
         * @member {'auto'|'s'|'se'|'sw'|'sme'|'smw'|'n'|'ne'|'nw'|'nme'|'nmw'} #panelPosition
         */
        this.set('panelPosition', 'auto');
        /**
         * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}. It manages
         * keystrokes of the dropdown:
         *
         * * <kbd>▼</kbd> opens the dropdown,
         * * <kbd>◀</kbd> and <kbd>Esc</kbd> closes the dropdown.
         *
         * @readonly
         * @member {module:utils/keystrokehandler~KeystrokeHandler}
         */
        this.keystrokes = new KeystrokeHandler();
        /**
         * Tracks information about the DOM focus in the dropdown.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker}
         */
        this.focusTracker = new FocusTracker();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-dropdown',
                    bind.to('class'),
                    bind.if('isEnabled', 'ck-disabled', value => !value)
                ],
                id: bind.to('id'),
                'aria-describedby': bind.to('ariaDescribedById')
            },
            children: [
                buttonView,
                panelView
            ]
        });
        buttonView.extendTemplate({
            attributes: {
                class: [
                    'ck-dropdown__button'
                ],
                'data-cke-tooltip-disabled': bind.to('isOpen')
            }
        });
        /**
         * A child {@link module:ui/list/listview~ListView list view} of the dropdown located
         * in its {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel}.
         *
         * **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addListToDropdown}.
         *
         * @readonly
         * @member {module:ui/list/listview~ListView} #listView
         */
        /**
         * A child toolbar of the dropdown located in the
         * {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel}.
         *
         * **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addToolbarToDropdown}.
         *
         * @readonly
         * @member {module:ui/toolbar/toolbarview~ToolbarView} #toolbarView
         */
        /**
         * Fired when the toolbar button or list item is executed.
         *
         * For {@link #listView} It fires when a child of some {@link module:ui/list/listitemview~ListItemView}
         * fired `execute`.
         *
         * For {@link #toolbarView} It fires when one of the buttons has been
         * {@link module:ui/button/buttonview~ButtonView#event:execute executed}.
         *
         * **Note**: Only supported when dropdown has list view added using {@link module:ui/dropdown/utils~addListToDropdown}
         * or {@link module:ui/dropdown/utils~addToolbarToDropdown}.
         *
         * @event execute
         */
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.focusTracker.add(this.buttonView.element);
        this.focusTracker.add(this.panelView.element);
        // Toggle the dropdown when its button has been clicked.
        this.listenTo(this.buttonView, 'open', () => {
            this.isOpen = !this.isOpen;
        });
        // Toggle the visibility of the panel when the dropdown becomes open.
        this.panelView.bind('isVisible').to(this, 'isOpen');
        // Let the dropdown control the position of the panel. The position must
        // be updated every time the dropdown is open.
        this.on('change:isOpen', (evt, name, isOpen) => {
            if (!isOpen) {
                return;
            }
            // If "auto", find the best position of the panel to fit into the viewport.
            // Otherwise, simply assign the static position.
            if (this.panelPosition === 'auto') {
                this.panelView.position = DropdownView._getOptimalPosition({
                    element: this.panelView.element,
                    target: this.buttonView.element,
                    fitInViewport: true,
                    positions: this._panelPositions
                }).name;
            }
            else {
                this.panelView.position = this.panelPosition;
            }
        });
        // Listen for keystrokes coming from within #element.
        this.keystrokes.listenTo(this.element);
        const closeDropdown = (data, cancel) => {
            if (this.isOpen) {
                this.isOpen = false;
                cancel();
            }
        };
        // Open the dropdown panel using the arrow down key, just like with return or space.
        this.keystrokes.set('arrowdown', (data, cancel) => {
            // Don't open if the dropdown is disabled or already open.
            if (this.buttonView.isEnabled && !this.isOpen) {
                this.isOpen = true;
                cancel();
            }
        });
        // Block the right arrow key (until nested dropdowns are implemented).
        this.keystrokes.set('arrowright', (data, cancel) => {
            if (this.isOpen) {
                cancel();
            }
        });
        // Close the dropdown using the arrow left/escape key.
        this.keystrokes.set('arrowleft', closeDropdown);
        this.keystrokes.set('esc', closeDropdown);
    }
    /**
     * Focuses the {@link #buttonView}.
     */
    focus() {
        this.buttonView.focus();
    }
    /**
     * Returns {@link #panelView panel} positions to be used by the
     * {@link module:utils/dom/position~getOptimalPosition `getOptimalPosition()`}
     * utility considering the direction of the language the UI of the editor is displayed in.
     *
     * @type {module:utils/dom/position~Options#positions}
     * @private
     */
    get _panelPositions() {
        const { south, north, southEast, southWest, northEast, northWest, southMiddleEast, southMiddleWest, northMiddleEast, northMiddleWest } = DropdownView.defaultPanelPositions;
        if (this.locale.uiLanguageDirection !== 'rtl') {
            return [
                southEast, southWest, southMiddleEast, southMiddleWest, south,
                northEast, northWest, northMiddleEast, northMiddleWest, north
            ];
        }
        else {
            return [
                southWest, southEast, southMiddleWest, southMiddleEast, south,
                northWest, northEast, northMiddleWest, northMiddleEast, north
            ];
        }
    }
}
/**
 * A set of positioning functions used by the dropdown view to determine
 * the optimal position (i.e. fitting into the browser viewport) of its
 * {@link module:ui/dropdown/dropdownview~DropdownView#panelView panel} when
 * {@link module:ui/dropdown/dropdownview~DropdownView#panelPosition} is set to 'auto'`.
 *
 * The available positioning functions are as follow:
 *
 * **South**
 *
 * * `south`
 *
 *			[ Button ]
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*
* * `southEast`
*
*		[ Button ]
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*
* * `southWest`
*
*		         [ Button ]
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*
* * `southMiddleEast`
*
*		  [ Button ]
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*
* * `southMiddleWest`
*
*		       [ Button ]
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*
* **North**
*
* * `north`
*
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*		    [ Button ]
*
* * `northEast`
*
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*		[ Button ]
*
* * `northWest`
*
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*		         [ Button ]
*
* * `northMiddleEast`
*
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*		  [ Button ]
*
* * `northMiddleWest`
*
*		+-----------------+
*		|      Panel      |
*		+-----------------+
*		       [ Button ]
*
* Positioning functions are compatible with {@link module:utils/dom/position~Position}.
*
* The name that position function returns will be reflected in dropdown panel's class that
* controls its placement. See {@link module:ui/dropdown/dropdownview~DropdownView#panelPosition}
* to learn more.
*
* @member {Object} module:ui/dropdown/dropdownview~DropdownView.defaultPanelPositions
*/
DropdownView.defaultPanelPositions = {
    south: (buttonRect, panelRect) => {
        return {
            top: buttonRect.bottom,
            left: buttonRect.left - (panelRect.width - buttonRect.width) / 2,
            name: 's'
        };
    },
    southEast: buttonRect => {
        return {
            top: buttonRect.bottom,
            left: buttonRect.left,
            name: 'se'
        };
    },
    southWest: (buttonRect, panelRect) => {
        return {
            top: buttonRect.bottom,
            left: buttonRect.left - panelRect.width + buttonRect.width,
            name: 'sw'
        };
    },
    southMiddleEast: (buttonRect, panelRect) => {
        return {
            top: buttonRect.bottom,
            left: buttonRect.left - (panelRect.width - buttonRect.width) / 4,
            name: 'sme'
        };
    },
    southMiddleWest: (buttonRect, panelRect) => {
        return {
            top: buttonRect.bottom,
            left: buttonRect.left - (panelRect.width - buttonRect.width) * 3 / 4,
            name: 'smw'
        };
    },
    north: (buttonRect, panelRect) => {
        return {
            top: buttonRect.top - panelRect.height,
            left: buttonRect.left - (panelRect.width - buttonRect.width) / 2,
            name: 'n'
        };
    },
    northEast: (buttonRect, panelRect) => {
        return {
            top: buttonRect.top - panelRect.height,
            left: buttonRect.left,
            name: 'ne'
        };
    },
    northWest: (buttonRect, panelRect) => {
        return {
            top: buttonRect.top - panelRect.height,
            left: buttonRect.left - panelRect.width + buttonRect.width,
            name: 'nw'
        };
    },
    northMiddleEast: (buttonRect, panelRect) => {
        return {
            top: buttonRect.top - panelRect.height,
            left: buttonRect.left - (panelRect.width - buttonRect.width) / 4,
            name: 'nme'
        };
    },
    northMiddleWest: (buttonRect, panelRect) => {
        return {
            top: buttonRect.top - panelRect.height,
            left: buttonRect.left - (panelRect.width - buttonRect.width) * 3 / 4,
            name: 'nmw'
        };
    }
};
/**
 * A function used to calculate the optimal position for the dropdown panel.
 *
 * @protected
 * @member {Function} module:ui/dropdown/dropdownview~DropdownView._getOptimalPosition
 */
DropdownView._getOptimalPosition = position_getOptimalPosition;

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/focuscycler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/focuscycler
 */

/**
 * A utility class that helps cycling over focusable {@link module:ui/view~View views} in a
 * {@link module:ui/viewcollection~ViewCollection} when the focus is tracked by the
 * {@link module:utils/focustracker~FocusTracker} instance. It helps implementing keyboard
 * navigation in HTML forms, toolbars, lists and the like.
 *
 * To work properly it requires:
 * * a collection of focusable (HTML `tabindex` attribute) views that implement the `focus()` method,
 * * an associated focus tracker to determine which view is focused.
 *
 * A simple cycler setup can look like this:
 *
 *		const focusables = new ViewCollection();
 *		const focusTracker = new FocusTracker();
 *
 *		// Add focusable views to the focus tracker.
 *		focusTracker.add( ... );
 *
 * Then, the cycler can be used manually:
 *
 *		const cycler = new FocusCycler( { focusables, focusTracker } );
 *
 *		// Will focus the first focusable view in #focusables.
 *		cycler.focusFirst();
 *
 *		// Will log the next focusable item in #focusables.
 *		console.log( cycler.next );
 *
 * Alternatively, it can work side by side with the {@link module:utils/keystrokehandler~KeystrokeHandler}:
 *
 *		const keystrokeHandler = new KeystrokeHandler();
 *
 *		// Activate the keystroke handler.
 *		keystrokeHandler.listenTo( sourceOfEvents );
 *
 *		const cycler = new FocusCycler( {
 *			focusables, focusTracker, keystrokeHandler,
 *			actions: {
 *				// When arrowup of arrowleft is detected by the #keystrokeHandler,
 *				// focusPrevious() will be called on the cycler.
 *				focusPrevious: [ 'arrowup', 'arrowleft' ],
 *			}
 *		} );
 *
 * Check out the {@glink framework/guides/deep-dive/ui/focus-tracking "Deep dive into focus tracking" guide} to learn more.
 */
class FocusCycler {
    /**
     * Creates an instance of the focus cycler utility.
     *
     * @param {Object} options Configuration options.
     * @param {module:utils/collection~Collection|Object} options.focusables
     * @param {module:utils/focustracker~FocusTracker} options.focusTracker
     * @param {module:utils/keystrokehandler~KeystrokeHandler} [options.keystrokeHandler]
     * @param {Object} [options.actions]
     */
    constructor(options) {
        this.focusables = options.focusables;
        this.focusTracker = options.focusTracker;
        this.keystrokeHandler = options.keystrokeHandler;
        this.actions = options.actions;
        /**
         * A {@link module:ui/view~View view} collection that the cycler operates on.
         *
         * @readonly
         * @member {module:utils/collection~Collection} #focusables
         */
        /**
         * A focus tracker instance that the cycler uses to determine the current focus
         * state in {@link #focusables}.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker} #focusTracker
         */
        /**
         * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}
         * which can respond to certain keystrokes and cycle the focus.
         *
         * @readonly
         * @member {module:utils/keystrokehandler~KeystrokeHandler} #keystrokeHandler
         */
        /**
         * Actions that the cycler can take when a keystroke is pressed. Requires
         * `options.keystrokeHandler` to be passed and working. When an action is
         * performed, `preventDefault` and `stopPropagation` will be called on the event
         * the keystroke fired in the DOM.
         *
         *		actions: {
         *			// Will call #focusPrevious() when arrowleft or arrowup is pressed.
         *			focusPrevious: [ 'arrowleft', 'arrowup' ],
         *
         *			// Will call #focusNext() when arrowdown is pressed.
         *			focusNext: 'arrowdown'
         *		}
         *
         * @readonly
         * @member {Object} #actions
         */
        if (options.actions && options.keystrokeHandler) {
            for (const methodName in options.actions) {
                let actions = options.actions[methodName];
                if (typeof actions == 'string') {
                    actions = [actions];
                }
                for (const keystroke of actions) {
                    options.keystrokeHandler.set(keystroke, (data, cancel) => {
                        this[methodName]();
                        cancel();
                    });
                }
            }
        }
    }
    /**
     * Returns the first focusable view in {@link #focusables}.
     * Returns `null` if there is none.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     *
     * @readonly
     * @member {module:ui/view~View|null} #first
     */
    get first() {
        return (this.focusables.find(isFocusable) || null);
    }
    /**
     * Returns the last focusable view in {@link #focusables}.
     * Returns `null` if there is none.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     *
     * @readonly
     * @member {module:ui/view~View|null} #last
     */
    get last() {
        return (this.focusables.filter(isFocusable).slice(-1)[0] || null);
    }
    /**
     * Returns the next focusable view in {@link #focusables} based on {@link #current}.
     * Returns `null` if there is none.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     *
     * @readonly
     * @member {module:ui/view~View|null} #next
     */
    get next() {
        return this._getFocusableItem(1);
    }
    /**
     * Returns the previous focusable view in {@link #focusables} based on {@link #current}.
     * Returns `null` if there is none.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     *
     * @readonly
     * @member {module:ui/view~View|null} #previous
     */
    get previous() {
        return this._getFocusableItem(-1);
    }
    /**
     * An index of the view in the {@link #focusables} which is focused according
     * to {@link #focusTracker}. Returns `null` when there is no such view.
     *
     * @readonly
     * @member {Number|null} #current
     */
    get current() {
        let index = null;
        // There's no focused view in the focusables.
        if (this.focusTracker.focusedElement === null) {
            return null;
        }
        this.focusables.find((view, viewIndex) => {
            const focused = view.element === this.focusTracker.focusedElement;
            if (focused) {
                index = viewIndex;
            }
            return focused;
        });
        return index;
    }
    /**
     * Focuses the {@link #first} item in {@link #focusables}.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     */
    focusFirst() {
        this._focus(this.first);
    }
    /**
     * Focuses the {@link #last} item in {@link #focusables}.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     */
    focusLast() {
        this._focus(this.last);
    }
    /**
     * Focuses the {@link #next} item in {@link #focusables}.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     */
    focusNext() {
        this._focus(this.next);
    }
    /**
     * Focuses the {@link #previous} item in {@link #focusables}.
     *
     * **Note**: Hidden views (e.g. with `display: none`) are ignored.
     */
    focusPrevious() {
        this._focus(this.previous);
    }
    /**
     * Focuses the given view if it exists.
     *
     * @protected
     * @param {module:ui/view~View} view
     */
    _focus(view) {
        if (view) {
            view.focus();
        }
    }
    /**
     * Returns the next or previous focusable view in {@link #focusables} with respect
     * to {@link #current}.
     *
     * @protected
     * @param {Number} step Either `1` for checking forward from {@link #current} or
     * `-1` for checking backwards.
     * @returns {module:ui/view~View|null}
     */
    _getFocusableItem(step) {
        // Cache for speed.
        const current = this.current;
        const collectionLength = this.focusables.length;
        if (!collectionLength) {
            return null;
        }
        // Start from the beginning if no view is focused.
        // https://github.com/ckeditor/ckeditor5-ui/issues/206
        if (current === null) {
            return this[step === 1 ? 'first' : 'last'];
        }
        // Cycle in both directions.
        let index = (current + collectionLength + step) % collectionLength;
        do {
            const view = this.focusables.get(index);
            if (isFocusable(view)) {
                return view;
            }
            // Cycle in both directions.
            index = (index + collectionLength + step) % collectionLength;
        } while (index !== current);
        return null;
    }
}
// Checks whether a view is focusable.
//
// @private
// @param {module:ui/view~View} view A view to be checked.
// @returns {Boolean}
function isFocusable(view) {
    return !!(view.focus && isVisible(view.element));
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/toolbar/toolbarseparatorview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/toolbar/toolbarseparatorview
 */

/**
 * The toolbar separator view class.
 *
 * @extends module:ui/view~View
 */
class ToolbarSeparatorView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        this.setTemplate({
            tag: 'span',
            attributes: {
                class: [
                    'ck',
                    'ck-toolbar__separator'
                ]
            }
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/toolbar/toolbarlinebreakview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/toolbar/toolbarlinebreakview
 */

/**
 * The toolbar line break view class.
 *
 * @extends module:ui/view~View
 */
class ToolbarLineBreakView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        this.setTemplate({
            tag: 'span',
            attributes: {
                class: [
                    'ck',
                    'ck-toolbar__line-break'
                ]
            }
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/bindings/preventdefault.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/bindings/preventdefault
 */
/**
 * A helper which executes a native `Event.preventDefault()` if the target of an event equals the
 * {@link module:ui/view~View#element element of the view}. It shortens the definition of a
 * {@link module:ui/view~View#template template}.
 *
 *		// In a class extending View.
 *		import preventDefault from '@ckeditor/ckeditor5-ui/src/bindings/preventdefault';
 *
 *		// ...
 *
 *		this.setTemplate( {
 *			tag: 'div',
 *
 *			on: {
 *				// Prevent the default mousedown action on this view.
 *				mousedown: preventDefault( this )
 *			}
 *		} );
 *
 * @param {module:ui/view~View} view View instance that defines the template.
 * @returns {module:ui/template~TemplateToBinding}
 */
function preventDefault(view) {
    return view.bindTemplate.to(evt => {
        if (evt.target === view.element) {
            evt.preventDefault();
        }
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/toolbar/normalizetoolbarconfig.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/toolbar/normalizetoolbarconfig
 */
/**
 * Normalizes the toolbar configuration (`config.toolbar`), which:
 *
 * * may be defined as an `Array`:
 *
 * 		toolbar: [ 'heading', 'bold', 'italic', 'link', ... ]
 *
 * * or an `Object`:
 *
 *		toolbar: {
 *			items: [ 'heading', 'bold', 'italic', 'link', ... ],
 *			removeItems: [ 'bold' ],
 *			...
 *		}
 *
 * * or may not be defined at all (`undefined`)
 *
 * and returns it in the object form.
 *
 * @param {Array|Object|undefined} config The value of `config.toolbar`.
 * @returns {Object} A normalized toolbar config object.
 */
function normalizetoolbarconfig_normalizeToolbarConfig(config) {
    if (Array.isArray(config)) {
        return {
            items: config,
            removeItems: []
        };
    }
    if (!config) {
        return {
            items: [],
            removeItems: []
        };
    }
    return Object.assign({
        items: [],
        removeItems: []
    }, config);
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/toolbar/toolbar.css
var toolbar = __webpack_require__(5571);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/toolbar/toolbar.css

            

var toolbar_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

toolbar_options.insert = "head";
toolbar_options.singleton = true;

var toolbar_update = injectStylesIntoStyleTag_default()(toolbar/* default */.Z, toolbar_options);



/* harmony default export */ const toolbar_toolbar = (toolbar/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/toolbar/toolbarview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/toolbar/toolbarview
 */

















const { threeVerticalDots } = icons;
const NESTED_TOOLBAR_ICONS = {
    alignLeft: icons.alignLeft,
    bold: icons.bold,
    importExport: icons.importExport,
    paragraph: icons.paragraph,
    plus: icons.plus,
    text: icons.text,
    threeVerticalDots: icons.threeVerticalDots
};
/**
 * The toolbar view class.
 *
 * @extends module:ui/view~View
 * @implements module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable
 */
class toolbarview_ToolbarView extends src_view_View {
    /**
     * Creates an instance of the {@link module:ui/toolbar/toolbarview~ToolbarView} class.
     *
     * Also see {@link #render}.
     *
     * @param {module:utils/locale~Locale} locale The localization services instance.
     * @param {module:ui/toolbar/toolbarview~ToolbarOptions} [options] Configuration options of the toolbar.
     */
    constructor(locale, options) {
        super(locale);
        const bind = this.bindTemplate;
        const t = this.t;
        /**
         * A reference to the options object passed to the constructor.
         *
         * @readonly
         * @member {module:ui/toolbar/toolbarview~ToolbarOptions}
         */
        this.options = options || {};
        /**
         * Label used by assistive technologies to describe this toolbar element.
         *
         * @default 'Editor toolbar'
         * @member {String} #ariaLabel
         */
        this.set('ariaLabel', t('Editor toolbar'));
        /**
         * The maximum width of the toolbar element.
         *
         * **Note**: When set to a specific value (e.g. `'200px'`), the value will affect the behavior of the
         * {@link module:ui/toolbar/toolbarview~ToolbarOptions#shouldGroupWhenFull}
         * option by changing the number of {@link #items} that will be displayed in the toolbar at a time.
         *
         * @observable
         * @default 'auto'
         * @member {String} #maxWidth
         */
        this.set('maxWidth', 'auto');
        /**
         * A collection of toolbar items (buttons, dropdowns, etc.).
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.items = this.createCollection();
        /**
         * Tracks information about the DOM focus in the toolbar.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker}
         */
        this.focusTracker = new FocusTracker();
        /**
         * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}
         * to handle keyboard navigation in the toolbar.
         *
         * @readonly
         * @member {module:utils/keystrokehandler~KeystrokeHandler}
         */
        this.keystrokes = new KeystrokeHandler();
        /**
         * An additional CSS class added to the {@link #element}.
         *
         * @observable
         * @member {String} #class
         */
        this.set('class', undefined);
        /**
         * When set true, makes the toolbar look compact with {@link #element}.
         *
         * @observable
         * @default false
         * @member {String} #isCompact
         */
        this.set('isCompact', false);
        /**
         * A (child) view containing {@link #items toolbar items}.
         *
         * @readonly
         * @member {module:ui/toolbar/toolbarview~ItemsView}
         */
        this.itemsView = new ItemsView(locale);
        /**
         * A top–level collection aggregating building blocks of the toolbar.
         *
         *	┌───────────────── ToolbarView ─────────────────┐
         *	| ┌──────────────── #children ────────────────┐ |
         *	| |   ┌──────────── #itemsView ───────────┐   | |
         *	| |   | [ item1 ] [ item2 ] ... [ itemN ] |   | |
         *	| |   └──────────────────────────────────-┘   | |
         *	| └───────────────────────────────────────────┘ |
         *	└───────────────────────────────────────────────┘
         *
         * By default, it contains the {@link #itemsView} but it can be extended with additional
         * UI elements when necessary.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.children = this.createCollection();
        this.children.add(this.itemsView);
        /**
         * A collection of {@link #items} that take part in the focus cycling
         * (i.e. navigation using the keyboard). Usually, it contains a subset of {@link #items} with
         * some optional UI elements that also belong to the toolbar and should be focusable
         * by the user.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.focusables = this.createCollection();
        /**
         * Controls the orientation of toolbar items. Only available when
         * {@link module:ui/toolbar/toolbarview~ToolbarOptions#shouldGroupWhenFull dynamic items grouping}
         * is **disabled**.
         *
         * @observable
         * @member {Boolean} #isVertical
         */
        /**
         * Helps cycling over {@link #focusables focusable items} in the toolbar.
         *
         * @readonly
         * @protected
         * @member {module:ui/focuscycler~FocusCycler}
         */
        const isRtl = locale.uiLanguageDirection === 'rtl';
        this._focusCycler = new FocusCycler({
            focusables: this.focusables,
            focusTracker: this.focusTracker,
            keystrokeHandler: this.keystrokes,
            actions: {
                // Navigate toolbar items backwards using the arrow[left,up] keys.
                focusPrevious: [isRtl ? 'arrowright' : 'arrowleft', 'arrowup'],
                // Navigate toolbar items forwards using the arrow[right,down] keys.
                focusNext: [isRtl ? 'arrowleft' : 'arrowright', 'arrowdown']
            }
        });
        const classes = [
            'ck',
            'ck-toolbar',
            bind.to('class'),
            bind.if('isCompact', 'ck-toolbar_compact')
        ];
        if (this.options.shouldGroupWhenFull && this.options.isFloating) {
            classes.push('ck-toolbar_floating');
        }
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: classes,
                role: 'toolbar',
                'aria-label': bind.to('ariaLabel'),
                style: {
                    maxWidth: bind.to('maxWidth')
                }
            },
            children: this.children,
            on: {
                // https://github.com/ckeditor/ckeditor5-ui/issues/206
                mousedown: preventDefault(this)
            }
        });
        /**
         * An instance of the active toolbar behavior that shapes its look and functionality.
         *
         * See {@link module:ui/toolbar/toolbarview~ToolbarBehavior} to learn more.
         *
         * @protected
         * @readonly
         * @member {module:ui/toolbar/toolbarview~ToolbarBehavior}
         */
        this._behavior = this.options.shouldGroupWhenFull ? new DynamicGrouping(this) : new StaticLayout(this);
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        // Children added before rendering should be known to the #focusTracker.
        for (const item of this.items) {
            this.focusTracker.add(item.element);
        }
        this.items.on('add', (evt, item) => {
            this.focusTracker.add(item.element);
        });
        this.items.on('remove', (evt, item) => {
            this.focusTracker.remove(item.element);
        });
        // Start listening for the keystrokes coming from #element.
        this.keystrokes.listenTo(this.element);
        this._behavior.render(this);
    }
    /**
     * @inheritDoc
     */
    destroy() {
        this._behavior.destroy();
        this.focusTracker.destroy();
        this.keystrokes.destroy();
        return super.destroy();
    }
    /**
     * Focuses the first focusable in {@link #focusables}.
     */
    focus() {
        this._focusCycler.focusFirst();
    }
    /**
     * Focuses the last focusable in {@link #focusables}.
     */
    focusLast() {
        this._focusCycler.focusLast();
    }
    /**
     * A utility that expands the plain toolbar configuration into
     * {@link module:ui/toolbar/toolbarview~ToolbarView#items} using a given component factory.
     *
     * @param {Array.<String>|Object} itemsOrConfig The toolbar items or the entire toolbar configuration object.
     * @param {module:ui/componentfactory~ComponentFactory} factory A factory producing toolbar items.
     * @param {Array.<String>} [removeItems] An array of items names to be removed from the configuration. When present, applies
     * to this toolbar and all nested ones as well.
     */
    fillFromConfig(itemsOrConfig, factory, removeItems) {
        const config = normalizetoolbarconfig_normalizeToolbarConfig(itemsOrConfig);
        const normalizedRemoveItems = removeItems || config.removeItems;
        const itemsToAdd = this._cleanItemsConfiguration(config.items, factory, normalizedRemoveItems)
            .map(item => {
            if (lodash_es_isObject(item)) {
                return this._createNestedToolbarDropdown(item, factory, normalizedRemoveItems);
            }
            else if (item === '|') {
                return new ToolbarSeparatorView();
            }
            else if (item === '-') {
                return new ToolbarLineBreakView();
            }
            return factory.create(item);
        })
            .filter((item) => !!item);
        this.items.addMany(itemsToAdd);
    }
    /**
     * Cleans up the {@link module:ui/toolbar/toolbarview~ToolbarView#items} of the toolbar by removing unwanted items and
     * duplicated (obsolete) separators or line breaks.
     *
     * @private
     * @param {Array.<String>} items The toolbar items configuration.
     * @param {module:ui/componentfactory~ComponentFactory} factory A factory producing toolbar items.
     * @param {Array.<String>} removeItems An array of items names to be removed from the configuration.
     * @returns {Array.<String>}  Items after the clean-up.
     */
    _cleanItemsConfiguration(items, factory, removeItems) {
        const filteredItems = items
            .filter((item, idx, items) => {
            if (item === '|') {
                return true;
            }
            // Items listed in `config.removeItems` should not be added to the toolbar.
            if (removeItems.indexOf(item) !== -1) {
                return false;
            }
            if (item === '-') {
                // The toolbar line breaks must not be rendered when toolbar grouping is enabled.
                // (https://github.com/ckeditor/ckeditor5/issues/8582)
                if (this.options.shouldGroupWhenFull) {
                    /**
                     * The toolbar multiline breaks (`-` items) only work when the automatic button grouping
                     * is disabled in the toolbar configuration.
                     * To do this, set the `shouldNotGroupWhenFull` option to `true` in the editor configuration:
                     *
                     *		const config = {
                     *			toolbar: {
                     *				items: [ ... ],
                     *				shouldNotGroupWhenFull: true
                     *			}
                     *		}
                     *
                     * Learn more about {@link module:core/editor/editorconfig~EditorConfig#toolbar toolbar configuration}.
                     *
                     * @error toolbarview-line-break-ignored-when-grouping-items
                     */
                    logWarning('toolbarview-line-break-ignored-when-grouping-items', items);
                    return false;
                }
                return true;
            }
            // For the items that cannot be instantiated we are sending warning message. We also filter them out.
            if (!lodash_es_isObject(item) && !factory.has(item)) {
                /**
                 * There was a problem processing the configuration of the toolbar. The item with the given
                 * name does not exist so it was omitted when rendering the toolbar.
                 *
                 * This warning usually shows up when the {@link module:core/plugin~Plugin} which is supposed
                 * to provide a toolbar item has not been loaded or there is a typo in the configuration.
                 *
                 * Make sure the plugin responsible for this toolbar item is loaded and the toolbar configuration
                 * is correct, e.g. {@link module:basic-styles/bold~Bold} is loaded for the `'bold'` toolbar item.
                 *
                 * You can use the following snippet to retrieve all available toolbar items:
                 *
                 *		Array.from( editor.ui.componentFactory.names() );
                 *
                 * @error toolbarview-item-unavailable
                 * @param {String|Object} item The name of the component or nested toolbar definition.
                 */
                logWarning('toolbarview-item-unavailable', { item });
                return false;
            }
            return true;
        });
        return this._cleanSeparatorsAndLineBreaks(filteredItems);
    }
    /**
     * Remove leading, trailing, and duplicated separators (`-` and `|`).
     *
     * @private
     * @param {Array.<String>} items
     * @returns {Array.<String>} Toolbar items after the separator and line break clean-up.
     */
    _cleanSeparatorsAndLineBreaks(items) {
        const nonSeparatorPredicate = (item) => (item !== '-' && item !== '|');
        const count = items.length;
        // Find an index of the first item that is not a separator.
        const firstCommandItemIndex = items.findIndex(nonSeparatorPredicate);
        // Items include separators only. There is no point in displaying them.
        if (firstCommandItemIndex === -1) {
            return [];
        }
        // Search from the end of the list, then convert found index back to the original direction.
        const lastCommandItemIndex = count - items
            .slice()
            .reverse()
            .findIndex(nonSeparatorPredicate);
        return items
            // Return items without the leading and trailing separators.
            .slice(firstCommandItemIndex, lastCommandItemIndex)
            // Remove duplicated separators.
            .filter((name, idx, items) => {
            // Filter only separators.
            if (nonSeparatorPredicate(name)) {
                return true;
            }
            const isDuplicated = idx > 0 && items[idx - 1] === name;
            return !isDuplicated;
        });
    }
    /**
     * Creates a user-defined dropdown containing a toolbar with items.
     *
     * @private
     * @param {Object} definition A definition of the nested toolbar dropdown.
     * @param {String} definition.label A label of the dropdown.
     * @param {String|Boolean} [definition.icon] An icon of the drop-down. One of 'bold', 'plus', 'text', 'importExport', 'alignLeft',
     * 'paragraph' or an SVG string. When `false` is passed, no icon will be used.
     * @param {Boolean} [definition.withText=false] When set `true`, the label of the dropdown will be visible. See
     * {@link module:ui/button/buttonview~ButtonView#withText} to learn more.
     * @param {Boolean|String|Function} [definition.tooltip=true] A tooltip of the dropdown button. See
     * {@link module:ui/button/buttonview~ButtonView#tooltip} to learn more.
     * @param {module:ui/componentfactory~ComponentFactory} componentFactory Component factory used to create items
     * of the nested toolbar.
     * @returns {module:ui/dropdown/dropdownview~DropdownView}
     */
    _createNestedToolbarDropdown(definition, componentFactory, removeItems) {
        let { label, icon, items, tooltip = true, withText = false } = definition;
        items = this._cleanItemsConfiguration(items, componentFactory, removeItems);
        // There is no point in rendering a dropdown without items.
        if (!items.length) {
            return null;
        }
        const locale = this.locale;
        const dropdownView = createDropdown(locale);
        if (!label) {
            /**
             * A dropdown definition in the toolbar configuration is missing a text label.
             *
             * Without a label, the dropdown becomes inaccessible to users relying on assistive technologies.
             * Make sure the `label` property is set in your drop-down configuration:
             *
             *		{
             *			label: 'A human-readable label',
             *			icon: '...',
             *			items: [ ... ]
             *		},
             *
             * Learn more about {@link module:core/editor/editorconfig~EditorConfig#toolbar toolbar configuration}.
             *
             * @error toolbarview-nested-toolbar-dropdown-missing-label
             */
            logWarning('toolbarview-nested-toolbar-dropdown-missing-label', definition);
        }
        dropdownView.class = 'ck-toolbar__nested-toolbar-dropdown';
        dropdownView.buttonView.set({
            label,
            tooltip,
            withText: !!withText
        });
        // Allow disabling icon by passing false.
        if (icon !== false) {
            // A pre-defined icon picked by name, SVG string, a fallback (default) icon.
            dropdownView.buttonView.icon = NESTED_TOOLBAR_ICONS[icon] || icon || threeVerticalDots;
        }
        // If the icon is disabled, display the label automatically.
        else {
            dropdownView.buttonView.withText = true;
        }
        addToolbarToDropdown(dropdownView, []);
        dropdownView.toolbarView.fillFromConfig(items, componentFactory, removeItems);
        return dropdownView;
    }
}
/**
 * An inner block of the {@link module:ui/toolbar/toolbarview~ToolbarView} hosting its
 * {@link module:ui/toolbar/toolbarview~ToolbarView#items}.
 *
 * @private
 * @extends module:ui/view~View
 */
class ItemsView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        /**
         * A collection of items (buttons, dropdowns, etc.).
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.children = this.createCollection();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-toolbar__items'
                ]
            },
            children: this.children
        });
    }
}
/**
 * A toolbar behavior that makes it static and unresponsive to the changes of the environment.
 * At the same time, it also makes it possible to display a toolbar with a vertical layout
 * using the {@link module:ui/toolbar/toolbarview~ToolbarView#isVertical} property.
 *
 * @private
 * @implements module:ui/toolbar/toolbarview~ToolbarBehavior
 */
class StaticLayout {
    /**
     * Creates an instance of the {@link module:ui/toolbar/toolbarview~StaticLayout} toolbar
     * behavior.
     *
     * @param {module:ui/toolbar/toolbarview~ToolbarView} view An instance of the toolbar that this behavior
     * is added to.
     */
    constructor(view) {
        const bind = view.bindTemplate;
        // Static toolbar can be vertical when needed.
        view.set('isVertical', false);
        // 1:1 pass–through binding, all ToolbarView#items are visible.
        view.itemsView.children.bindTo(view.items).using(item => item);
        // 1:1 pass–through binding, all ToolbarView#items are focusable.
        view.focusables.bindTo(view.items).using(item => item);
        view.extendTemplate({
            attributes: {
                class: [
                    // When vertical, the toolbar has an additional CSS class.
                    bind.if('isVertical', 'ck-toolbar_vertical')
                ]
            }
        });
    }
    /**
     * @inheritDoc
     */
    render() { }
    /**
     * @inheritDoc
     */
    destroy() { }
}
/**
 * A toolbar behavior that makes the items respond to changes in the geometry.
 *
 * In a nutshell, it groups {@link module:ui/toolbar/toolbarview~ToolbarView#items}
 * that do not fit visually into a single row of the toolbar (due to limited space).
 * Items that do not fit are aggregated in a dropdown displayed at the end of the toolbar.
 *
 *	┌──────────────────────────────────────── ToolbarView ──────────────────────────────────────────┐
 *	| ┌─────────────────────────────────────── #children ─────────────────────────────────────────┐ |
 *	| |   ┌─────── #itemsView ────────┐ ┌──────────────────────┐ ┌── #groupedItemsDropdown ───┐   | |
 *	| |   |       #ungroupedItems     | | ToolbarSeparatorView | |        #groupedItems       |   | |
 *	| |   └──────────────────────────-┘ └──────────────────────┘ └────────────────────────────┘   | |
 *	| |                                  \---------- only when toolbar items overflow --------/    | |
 *	| └───────────────────────────────────────────────────────────────────────────────────────────┘ |
 *	└───────────────────────────────────────────────────────────────────────────────────────────────┘
 *
 * @private
 * @implements module:ui/toolbar/toolbarview~ToolbarBehavior
 */
class DynamicGrouping {
    /**
     * Creates an instance of the {@link module:ui/toolbar/toolbarview~DynamicGrouping} toolbar
     * behavior.
     *
     * @param {module:ui/toolbar/toolbarview~ToolbarView} view An instance of the toolbar that this behavior
     * is added to.
     */
    constructor(view) {
        /**
         * A toolbar view this behavior belongs to.
         *
         * @readonly
         * @member {module:ui/toolbar~ToolbarView}
         */
        this.view = view;
        /**
         * A collection of toolbar children.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.viewChildren = view.children;
        /**
         * A collection of focusable toolbar elements.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.viewFocusables = view.focusables;
        /**
         * A view containing toolbar items.
         *
         * @readonly
         * @member {module:ui/toolbar/toolbarview~ItemsView}
         */
        this.viewItemsView = view.itemsView;
        /**
         * Toolbar focus tracker.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker}
         */
        this.viewFocusTracker = view.focusTracker;
        /**
         * Toolbar locale.
         *
         * @readonly
         * @member {module:utils/locale~Locale}
         */
        this.viewLocale = view.locale;
        /**
         * Toolbar element.
         *
         * @readonly
         * @member {HTMLElement} #viewElement
         */
        /**
         * A subset of toolbar {@link module:ui/toolbar/toolbarview~ToolbarView#items}.
         * Aggregates items that fit into a single row of the toolbar and were not {@link #groupedItems grouped}
         * into a {@link #groupedItemsDropdown dropdown}. Items of this collection are displayed in the
         * {@link module:ui/toolbar/toolbarview~ToolbarView#itemsView}.
         *
         * When none of the {@link module:ui/toolbar/toolbarview~ToolbarView#items} were grouped, it
         * matches the {@link module:ui/toolbar/toolbarview~ToolbarView#items} collection in size and order.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.ungroupedItems = view.createCollection();
        /**
         * A subset of toolbar {@link module:ui/toolbar/toolbarview~ToolbarView#items}.
         * A collection of the toolbar items that do not fit into a single row of the toolbar.
         * Grouped items are displayed in a dedicated {@link #groupedItemsDropdown dropdown}.
         *
         * When none of the {@link module:ui/toolbar/toolbarview~ToolbarView#items} were grouped,
         * this collection is empty.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.groupedItems = view.createCollection();
        /**
         * The dropdown that aggregates {@link #groupedItems grouped items} that do not fit into a single
         * row of the toolbar. It is displayed on demand as the last of
         * {@link module:ui/toolbar/toolbarview~ToolbarView#children toolbar children} and offers another
         * (nested) toolbar which displays items that would normally overflow.
         *
         * @readonly
         * @member {module:ui/dropdown/dropdownview~DropdownView}
         */
        this.groupedItemsDropdown = this._createGroupedItemsDropdown();
        /**
         * An instance of the resize observer that helps dynamically determine the geometry of the toolbar
         * and manage items that do not fit into a single row.
         *
         * **Note:** Created in {@link #_enableGroupingOnResize}.
         *
         * @readonly
         * @member {module:utils/dom/resizeobserver~ResizeObserver}
         */
        this.resizeObserver = null;
        /**
         * A cached value of the horizontal padding style used by {@link #_updateGrouping}
         * to manage the {@link module:ui/toolbar/toolbarview~ToolbarView#items} that do not fit into
         * a single toolbar line. This value can be reused between updates because it is unlikely that
         * the padding will change and re–using `Window.getComputedStyle()` is expensive.
         *
         * @readonly
         * @member {Number}
         */
        this.cachedPadding = null;
        /**
         * A flag indicating that an items grouping update has been queued (e.g. due to the toolbar being visible)
         * and should be executed immediately the next time the toolbar shows up.
         *
         * @readonly
         * @member {Boolean}
         */
        this.shouldUpdateGroupingOnNextResize = false;
        // Only those items that were not grouped are visible to the user.
        view.itemsView.children.bindTo(this.ungroupedItems).using(item => item);
        // Make sure all #items visible in the main space of the toolbar are "focuscycleable".
        this.ungroupedItems.on('add', this._updateFocusCycleableItems.bind(this));
        this.ungroupedItems.on('remove', this._updateFocusCycleableItems.bind(this));
        // Make sure the #groupedItemsDropdown is also included in cycling when it appears.
        view.children.on('add', this._updateFocusCycleableItems.bind(this));
        view.children.on('remove', this._updateFocusCycleableItems.bind(this));
        // ToolbarView#items is dynamic. When an item is added or removed, it should be automatically
        // represented in either grouped or ungrouped items at the right index.
        // In other words #items == concat( #ungroupedItems, #groupedItems )
        // (in length and order).
        view.items.on('change', (evt, changeData) => {
            const index = changeData.index;
            const added = Array.from(changeData.added);
            // Removing.
            for (const removedItem of changeData.removed) {
                if (index >= this.ungroupedItems.length) {
                    this.groupedItems.remove(removedItem);
                }
                else {
                    this.ungroupedItems.remove(removedItem);
                }
            }
            // Adding.
            for (let currentIndex = index; currentIndex < index + added.length; currentIndex++) {
                const addedItem = added[currentIndex - index];
                if (currentIndex > this.ungroupedItems.length) {
                    this.groupedItems.add(addedItem, currentIndex - this.ungroupedItems.length);
                }
                else {
                    this.ungroupedItems.add(addedItem, currentIndex);
                }
            }
            // When new ungrouped items join in and land in #ungroupedItems, there's a chance it causes
            // the toolbar to overflow.
            // Consequently if removed from grouped or ungrouped items, there is a chance
            // some new space is available and we could do some ungrouping.
            this._updateGrouping();
        });
        view.extendTemplate({
            attributes: {
                class: [
                    // To group items dynamically, the toolbar needs a dedicated CSS class.
                    'ck-toolbar_grouping'
                ]
            }
        });
    }
    /**
     * Enables dynamic items grouping based on the dimensions of the toolbar.
     *
     * @param {module:ui/toolbar/toolbarview~ToolbarView} view An instance of the toolbar that this behavior
     * is added to.
     */
    render(view) {
        this.viewElement = view.element;
        this._enableGroupingOnResize();
        this._enableGroupingOnMaxWidthChange(view);
    }
    /**
     * Cleans up the internals used by this behavior.
     */
    destroy() {
        // The dropdown may not be in ToolbarView#children at the moment of toolbar destruction
        // so let's make sure it's actually destroyed along with the toolbar.
        this.groupedItemsDropdown.destroy();
        this.resizeObserver.destroy();
    }
    /**
     * When called, it will check if any of the {@link #ungroupedItems} do not fit into a single row of the toolbar,
     * and it will move them to the {@link #groupedItems} when it happens.
     *
     * At the same time, it will also check if there is enough space in the toolbar for the first of the
     * {@link #groupedItems} to be returned back to {@link #ungroupedItems} and still fit into a single row
     * without the toolbar wrapping.
     *
     * @protected
     */
    _updateGrouping() {
        // Do no grouping–related geometry analysis when the toolbar is detached from visible DOM,
        // for instance before #render(), or after render but without a parent or a parent detached
        // from DOM. DOMRects won't work anyway and there will be tons of warning in the console and
        // nothing else. This happens, for instance, when the toolbar is detached from DOM and
        // some logic adds or removes its #items.
        if (!this.viewElement.ownerDocument.body.contains(this.viewElement)) {
            return;
        }
        // Do not update grouping when the element is invisible. Such toolbar has DOMRect filled with zeros
        // and that would cause all items to be grouped. Instead, queue the grouping so it runs next time
        // the toolbar is visible (the next ResizeObserver callback execution). This is handy because
        // the grouping could be caused by increasing the #maxWidth when the toolbar was invisible and the next
        // time it shows up, some items could actually be ungrouped (https://github.com/ckeditor/ckeditor5/issues/6575).
        if (!isVisible(this.viewElement)) {
            this.shouldUpdateGroupingOnNextResize = true;
            return;
        }
        // Remember how many items were initially grouped so at the it is possible to figure out if the number
        // of grouped items has changed. If the number has changed, geometry of the toolbar has also changed.
        const initialGroupedItemsCount = this.groupedItems.length;
        let wereItemsGrouped;
        // Group #items as long as some wrap to the next row. This will happen, for instance,
        // when the toolbar is getting narrow and there is not enough space to display all items in
        // a single row.
        while (this._areItemsOverflowing) {
            this._groupLastItem();
            wereItemsGrouped = true;
        }
        // If none were grouped now but there were some items already grouped before,
        // then, what the hell, maybe let's see if some of them can be ungrouped. This happens when,
        // for instance, the toolbar is stretching and there's more space in it than before.
        if (!wereItemsGrouped && this.groupedItems.length) {
            // Ungroup items as long as none are overflowing or there are none to ungroup left.
            while (this.groupedItems.length && !this._areItemsOverflowing) {
                this._ungroupFirstItem();
            }
            // If the ungrouping ended up with some item wrapping to the next row,
            // put it back to the group toolbar ("undo the last ungroup"). We don't know whether
            // an item will wrap or not until we ungroup it (that's a DOM/CSS thing) so this
            // clean–up is vital for the algorithm.
            if (this._areItemsOverflowing) {
                this._groupLastItem();
            }
        }
        if (this.groupedItems.length !== initialGroupedItemsCount) {
            this.view.fire('groupedItemsUpdate');
        }
    }
    /**
     * Returns `true` when {@link module:ui/toolbar/toolbarview~ToolbarView#element} children visually overflow,
     * for instance if the toolbar is narrower than its members. Returns `false` otherwise.
     *
     * @private
     * @type {Boolean}
     */
    get _areItemsOverflowing() {
        // An empty toolbar cannot overflow.
        if (!this.ungroupedItems.length) {
            return false;
        }
        const element = this.viewElement;
        const uiLanguageDirection = this.viewLocale.uiLanguageDirection;
        const lastChildRect = new rect_Rect(element.lastChild);
        const toolbarRect = new rect_Rect(element);
        if (!this.cachedPadding) {
            const computedStyle = dom_global.window.getComputedStyle(element);
            const paddingProperty = uiLanguageDirection === 'ltr' ? 'paddingRight' : 'paddingLeft';
            // parseInt() is essential because of quirky floating point numbers logic and DOM.
            // If the padding turned out too big because of that, the grouped items dropdown would
            // always look (from the Rect perspective) like it overflows (while it's not).
            this.cachedPadding = Number.parseInt(computedStyle[paddingProperty]);
        }
        if (uiLanguageDirection === 'ltr') {
            return lastChildRect.right > toolbarRect.right - this.cachedPadding;
        }
        else {
            return lastChildRect.left < toolbarRect.left + this.cachedPadding;
        }
    }
    /**
     * Enables the functionality that prevents {@link #ungroupedItems} from overflowing (wrapping to the next row)
     * upon resize when there is little space available. Instead, the toolbar items are moved to the
     * {@link #groupedItems} collection and displayed in a dropdown at the end of the row (which has its own nested toolbar).
     *
     * When called, the toolbar will automatically analyze the location of its {@link #ungroupedItems} and "group"
     * them in the dropdown if necessary. It will also observe the browser window for size changes in
     * the future and respond to them by grouping more items or reverting already grouped back, depending
     * on the visual space available.
     *
     * @private
     */
    _enableGroupingOnResize() {
        let previousWidth;
        // TODO: Consider debounce.
        this.resizeObserver = new resizeobserver_ResizeObserver(this.viewElement, entry => {
            if (!previousWidth || previousWidth !== entry.contentRect.width || this.shouldUpdateGroupingOnNextResize) {
                this.shouldUpdateGroupingOnNextResize = false;
                this._updateGrouping();
                previousWidth = entry.contentRect.width;
            }
        });
        this._updateGrouping();
    }
    /**
     * Enables the grouping functionality, just like {@link #_enableGroupingOnResize} but the difference is that
     * it listens to the changes of {@link module:ui/toolbar/toolbarview~ToolbarView#maxWidth} instead.
     *
     * @private
     */
    _enableGroupingOnMaxWidthChange(view) {
        view.on('change:maxWidth', () => {
            this._updateGrouping();
        });
    }
    /**
     * When called, it will remove the last item from {@link #ungroupedItems} and move it back
     * to the {@link #groupedItems} collection.
     *
     * The opposite of {@link #_ungroupFirstItem}.
     *
     * @private
     */
    _groupLastItem() {
        if (!this.groupedItems.length) {
            this.viewChildren.add(new ToolbarSeparatorView());
            this.viewChildren.add(this.groupedItemsDropdown);
            this.viewFocusTracker.add(this.groupedItemsDropdown.element);
        }
        this.groupedItems.add(this.ungroupedItems.remove(this.ungroupedItems.last), 0);
    }
    /**
     * Moves the very first item belonging to {@link #groupedItems} back
     * to the {@link #ungroupedItems} collection.
     *
     * The opposite of {@link #_groupLastItem}.
     *
     * @private
     */
    _ungroupFirstItem() {
        this.ungroupedItems.add(this.groupedItems.remove(this.groupedItems.first));
        if (!this.groupedItems.length) {
            this.viewChildren.remove(this.groupedItemsDropdown);
            this.viewChildren.remove(this.viewChildren.last);
            this.viewFocusTracker.remove(this.groupedItemsDropdown.element);
        }
    }
    /**
     * Creates the {@link #groupedItemsDropdown} that hosts the members of the {@link #groupedItems}
     * collection when there is not enough space in the toolbar to display all items in a single row.
     *
     * @private
     * @returns {module:ui/dropdown/dropdownview~DropdownView}
     */
    _createGroupedItemsDropdown() {
        const locale = this.viewLocale;
        const t = locale.t;
        const dropdown = createDropdown(locale);
        dropdown.class = 'ck-toolbar__grouped-dropdown';
        // Make sure the dropdown never sticks out to the left/right. It should be under the main toolbar.
        // (https://github.com/ckeditor/ckeditor5/issues/5608)
        dropdown.panelPosition = locale.uiLanguageDirection === 'ltr' ? 'sw' : 'se';
        addToolbarToDropdown(dropdown, []);
        dropdown.buttonView.set({
            label: t('Show more items'),
            tooltip: true,
            tooltipPosition: locale.uiLanguageDirection === 'rtl' ? 'se' : 'sw',
            icon: threeVerticalDots
        });
        // 1:1 pass–through binding.
        dropdown.toolbarView.items.bindTo(this.groupedItems).using(item => item);
        return dropdown;
    }
    /**
     * Updates the {@link module:ui/toolbar/toolbarview~ToolbarView#focusables focus–cycleable items}
     * collection so it represents the up–to–date state of the UI from the perspective of the user.
     *
     * For instance, the {@link #groupedItemsDropdown} can show up and hide but when it is visible,
     * it must be subject to focus cycling in the toolbar.
     *
     * See the {@link module:ui/toolbar/toolbarview~ToolbarView#focusables collection} documentation
     * to learn more about the purpose of this method.
     *
     * @private
     */
    _updateFocusCycleableItems() {
        this.viewFocusables.clear();
        this.ungroupedItems.map(item => {
            this.viewFocusables.add(item);
        });
        if (this.groupedItems.length) {
            this.viewFocusables.add(this.groupedItemsDropdown);
        }
    }
}
/**
 * Creates a new toolbar behavior instance.
 *
 * The instance is created in the {@link module:ui/toolbar/toolbarview~ToolbarView#constructor} of the toolbar.
 * This is the right place to extend the {@link module:ui/toolbar/toolbarview~ToolbarView#template} of
 * the toolbar, define extra toolbar properties, etc.
 *
 * @method #constructor
 * @param {module:ui/toolbar/toolbarview~ToolbarView} view An instance of the toolbar that this behavior is added to.
 */
/**
 * A method called after the toolbar has been {@link module:ui/toolbar/toolbarview~ToolbarView#render rendered}.
 * It can be used to, for example, customize the behavior of the toolbar when its {@link module:ui/toolbar/toolbarview~ToolbarView#element}
 * is available.
 *
 * @readonly
 * @member {Function} #render
 * @param {module:ui/toolbar/toolbarview~ToolbarView} view An instance of the toolbar being rendered.
 */
/**
 * A method called after the toolbar has been {@link module:ui/toolbar/toolbarview~ToolbarView#destroy destroyed}.
 * It allows cleaning up after the toolbar behavior, for instance, this is the right place to detach
 * event listeners, free up references, etc.
 *
 * @readonly
 * @member {Function} #destroy
 */

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/list/list.css
var list = __webpack_require__(1162);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/list/list.css

            

var list_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

list_options.insert = "head";
list_options.singleton = true;

var list_update = injectStylesIntoStyleTag_default()(list/* default */.Z, list_options);



/* harmony default export */ const list_list = (list/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/list/listview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/list/listview
 */





/**
 * The list view class.
 *
 * @extends module:ui/view~View
 * @implements module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable
 */
class ListView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        /**
         * Collection of the child list views.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.items = this.createCollection();
        /**
         * Tracks information about DOM focus in the list.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker}
         */
        this.focusTracker = new FocusTracker();
        /**
         * Instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
         *
         * @readonly
         * @member {module:utils/keystrokehandler~KeystrokeHandler}
         */
        this.keystrokes = new KeystrokeHandler();
        /**
         * Helps cycling over focusable {@link #items} in the list.
         *
         * @readonly
         * @protected
         * @member {module:ui/focuscycler~FocusCycler}
         */
        this._focusCycler = new FocusCycler({
            focusables: this.items,
            focusTracker: this.focusTracker,
            keystrokeHandler: this.keystrokes,
            actions: {
                // Navigate list items backwards using the arrowup key.
                focusPrevious: 'arrowup',
                // Navigate toolbar items forwards using the arrowdown key.
                focusNext: 'arrowdown'
            }
        });
        this.setTemplate({
            tag: 'ul',
            attributes: {
                class: [
                    'ck',
                    'ck-reset',
                    'ck-list'
                ]
            },
            children: this.items
        });
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        // Items added before rendering should be known to the #focusTracker.
        for (const item of this.items) {
            this.focusTracker.add(item.element);
        }
        this.items.on('add', (evt, item) => {
            this.focusTracker.add(item.element);
        });
        this.items.on('remove', (evt, item) => {
            this.focusTracker.remove(item.element);
        });
        // Start listening for the keystrokes coming from #element.
        this.keystrokes.listenTo(this.element);
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this.focusTracker.destroy();
        this.keystrokes.destroy();
    }
    /**
     * Focuses the first focusable in {@link #items}.
     */
    focus() {
        this._focusCycler.focusFirst();
    }
    /**
     * Focuses the last focusable in {@link #items}.
     */
    focusLast() {
        this._focusCycler.focusLast();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/list/listitemview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/list/listitemview
 */

/**
 * The list item view class.
 *
 * @extends module:ui/view~View
 */
class ListItemView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * Controls whether the item view is visible. Visible by default, list items are hidden
         * using a CSS class.
         *
         * @observable
         * @default true
         * @member {Boolean} #isVisible
         */
        this.set('isVisible', true);
        /**
         * Collection of the child views inside of the list item {@link #element}.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.children = this.createCollection();
        this.setTemplate({
            tag: 'li',
            attributes: {
                class: [
                    'ck',
                    'ck-list__item',
                    bind.if('isVisible', 'ck-hidden', value => !value)
                ]
            },
            children: this.children
        });
    }
    /**
     * Focuses the list item.
     */
    focus() {
        this.children.first.focus();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/list/listseparatorview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/list/listseparatorview
 */

/**
 * The list separator view class.
 *
 * @extends module:ui/view~View
 */
class ListSeparatorView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        this.setTemplate({
            tag: 'li',
            attributes: {
                class: [
                    'ck',
                    'ck-list__separator'
                ]
            }
        });
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/toolbardropdown.css
var toolbardropdown = __webpack_require__(5075);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/toolbardropdown.css

            

var toolbardropdown_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

toolbardropdown_options.insert = "head";
toolbardropdown_options.singleton = true;

var toolbardropdown_update = injectStylesIntoStyleTag_default()(toolbardropdown/* default */.Z, toolbardropdown_options);



/* harmony default export */ const dropdown_toolbardropdown = (toolbardropdown/* default.locals */.Z.locals || {});
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/listdropdown.css
var listdropdown = __webpack_require__(6875);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/dropdown/listdropdown.css

            

var listdropdown_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

listdropdown_options.insert = "head";
listdropdown_options.singleton = true;

var listdropdown_update = injectStylesIntoStyleTag_default()(listdropdown/* default */.Z, listdropdown_options);



/* harmony default export */ const dropdown_listdropdown = (listdropdown/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/dropdown/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/dropdown/utils
 */














/**
 * A helper for creating dropdowns. It creates an instance of a {@link module:ui/dropdown/dropdownview~DropdownView dropdown},
 * with a {@link module:ui/dropdown/button/dropdownbutton~DropdownButton button},
 * {@link module:ui/dropdown/dropdownpanelview~DropdownPanelView panel} and all standard dropdown's behaviors.
 *
 * # Creating dropdowns
 *
 * By default, the default {@link module:ui/dropdown/button/dropdownbuttonview~DropdownButtonView} class is used as
 * definition of the button:
 *
 *		const dropdown = createDropdown( model );
 *
 *		// Configure dropdown's button properties:
 *		dropdown.buttonView.set( {
 *			label: 'A dropdown',
 *			withText: true
 *		} );
 *
 *		dropdown.render();
 *
 *		// Will render a dropdown labeled "A dropdown" with an empty panel.
 *		document.body.appendChild( dropdown.element );
 *
 * You can also provide other button views (they need to implement the
 * {@link module:ui/dropdown/button/dropdownbutton~DropdownButton} interface). For instance, you can use
 * {@link module:ui/dropdown/button/splitbuttonview~SplitButtonView} to create a dropdown with a split button.
 *
 *		const dropdown = createDropdown( locale, SplitButtonView );
 *
 *		// Configure dropdown's button properties:
 *		dropdown.buttonView.set( {
 *			label: 'A dropdown',
 *			withText: true
 *		} );
 *
 *		dropdown.buttonView.on( 'execute', () => {
 *			// Add the behavior of the "action part" of the split button.
 *			// Split button consists of the "action part" and "arrow part".
 *			// The arrow opens the dropdown while the action part can have some other behavior.
 * 		} );
 *
 *		dropdown.render();
 *
 *		// Will render a dropdown labeled "A dropdown" with an empty panel.
 *		document.body.appendChild( dropdown.element );
 *
 * # Adding content to the dropdown's panel
 *
 * The content of the panel can be inserted directly into the `dropdown.panelView.element`:
 *
 *		dropdown.panelView.element.textContent = 'Content of the panel';
 *
 * However, most of the time you will want to add there either a {@link module:ui/list/listview~ListView list of options}
 * or a list of buttons (i.e. a {@link module:ui/toolbar/toolbarview~ToolbarView toolbar}).
 * To simplify the task, you can use, respectively, {@link module:ui/dropdown/utils~addListToDropdown} or
 * {@link module:ui/dropdown/utils~addToolbarToDropdown} utils.
 *
 * @param {module:utils/locale~Locale} locale The locale instance.
 * @param {Function} ButtonClass The dropdown button view class. Needs to implement the
 * {@link module:ui/dropdown/button/dropdownbutton~DropdownButton} interface.
 * @returns {module:ui/dropdown/dropdownview~DropdownView} The dropdown view instance.
 */
function createDropdown(locale, ButtonClass = DropdownButtonView) {
    const buttonView = new ButtonClass(locale);
    const panelView = new DropdownPanelView(locale);
    const dropdownView = new DropdownView(locale, buttonView, panelView);
    buttonView.bind('isEnabled').to(dropdownView);
    if (buttonView instanceof SplitButtonView) {
        buttonView.arrowView.bind('isOn').to(dropdownView, 'isOpen');
    }
    else {
        buttonView.bind('isOn').to(dropdownView, 'isOpen');
    }
    addDefaultBehavior(dropdownView);
    return dropdownView;
}
/**
 * Adds an instance of {@link module:ui/toolbar/toolbarview~ToolbarView} to a dropdown.
 *
 *		const buttons = [];
 *
 *		// Either create a new ButtonView instance or create existing.
 *		buttons.push( new ButtonView() );
 *		buttons.push( editor.ui.componentFactory.create( 'someButton' ) );
 *
 *		const dropdown = createDropdown( locale );
 *
 *		addToolbarToDropdown( dropdown, buttons );
 *
 *		dropdown.toolbarView.isVertical = true;
 *
 *		// Will render a vertical button dropdown labeled "A button dropdown"
 *		// with a button group in the panel containing two buttons.
 *		dropdown.render()
 *		document.body.appendChild( dropdown.element );
 *
 * **Note:** To improve the accessibility, you can tell the dropdown to focus the first active button of the toolbar when the dropdown
 * {@link module:ui/dropdown/dropdownview~DropdownView#isOpen gets open}. See the documentation of `options` to learn more.
 *
 * See {@link module:ui/dropdown/utils~createDropdown} and {@link module:ui/toolbar/toolbarview~ToolbarView}.
 *
 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView A dropdown instance to which `ToolbarView` will be added.
 * @param {Iterable.<module:ui/button/buttonview~ButtonView>} buttons
 * @param {Object} [options]
 * @param {Boolean} [options.enableActiveItemFocusOnDropdownOpen=false] When set `true`, the focus will automatically move to the first
 * active {@link module:ui/toolbar/toolbarview~ToolbarView#items item} of the toolbar upon
 * {@link module:ui/dropdown/dropdownview~DropdownView#isOpen opening} the dropdown. Active items are those with the `isOn` property set
 * `true` (for instance {@link module:ui/button/buttonview~ButtonView buttons}). If no active items is found, the toolbar will be focused
 * as a whole resulting in the focus moving to its first focusable item (default behavior of
 * {@link module:ui/dropdown/dropdownview~DropdownView}).
 */
function addToolbarToDropdown(dropdownView, buttons, options = {}) {
    const locale = dropdownView.locale;
    const t = locale.t;
    const toolbarView = dropdownView.toolbarView = new toolbarview_ToolbarView(locale);
    toolbarView.set('ariaLabel', t('Dropdown toolbar'));
    dropdownView.extendTemplate({
        attributes: {
            class: ['ck-toolbar-dropdown']
        }
    });
    buttons.map(view => toolbarView.items.add(view));
    if (options.enableActiveItemFocusOnDropdownOpen) {
        // Accessibility: Focus the first active button in the toolbar when the dropdown gets open.
        focusChildOnDropdownOpen(dropdownView, () => toolbarView.items.find((item) => item.isOn));
    }
    dropdownView.panelView.children.add(toolbarView);
    toolbarView.items.delegate('execute').to(dropdownView);
}
/**
 * Adds an instance of {@link module:ui/list/listview~ListView} to a dropdown.
 *
 *		const items = new Collection();
 *
 *		items.add( {
 *			type: 'button',
 *			model: new Model( {
 *				withText: true,
 *				label: 'First item',
 *				labelStyle: 'color: red'
 *			} )
 *		} );
 *
 *		items.add( {
 *			 type: 'button',
 *			 model: new Model( {
 *				withText: true,
 *				label: 'Second item',
 *				labelStyle: 'color: green',
 *				class: 'foo'
 *			} )
 *		} );
 *
 *		const dropdown = createDropdown( locale );
 *
 *		addListToDropdown( dropdown, items );
 *
 *		// Will render a dropdown with a list in the panel containing two items.
 *		dropdown.render()
 *		document.body.appendChild( dropdown.element );
 *
 * The `items` collection passed to this methods controls the presence and attributes of respective
 * {@link module:ui/list/listitemview~ListItemView list items}.
 *
 * **Note:** To improve the accessibility, when a list is added to the dropdown using this helper the dropdown will automatically attempt
 * to focus the first active item (a host to a {@link module:ui/button/buttonview~ButtonView} with
 * {@link module:ui/button/buttonview~ButtonView#isOn} set `true`) or the very first item when none are active.
 *
 * See {@link module:ui/dropdown/utils~createDropdown} and {@link module:list/list~List}.
 *
 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView A dropdown instance to which `ListVIew` will be added.
 * @param {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>} items
 * A collection of the list item definitions to populate the list.
 */
function addListToDropdown(dropdownView, items) {
    const locale = dropdownView.locale;
    const listView = dropdownView.listView = new ListView(locale);
    listView.items.bindTo(items).using(def => {
        if (def.type === 'separator') {
            return new ListSeparatorView(locale);
        }
        else if (def.type === 'button' || def.type === 'switchbutton') {
            const listItemView = new ListItemView(locale);
            let buttonView;
            if (def.type === 'button') {
                buttonView = new buttonview_ButtonView(locale);
            }
            else {
                buttonView = new SwitchButtonView(locale);
            }
            // Bind all model properties to the button view.
            buttonView.bind(...Object.keys(def.model)).to(def.model);
            buttonView.delegate('execute').to(listItemView);
            listItemView.children.add(buttonView);
            return listItemView;
        }
        return null;
    });
    dropdownView.panelView.children.add(listView);
    listView.items.delegate('execute').to(dropdownView);
    // Accessibility: Focus the first active button in the list when the dropdown gets open.
    focusChildOnDropdownOpen(dropdownView, () => listView.items.find(item => {
        if (item instanceof ListItemView) {
            return item.children.first.isOn;
        }
        return false;
    }));
}
/**
 * A helper to be used on an existing {@link module:ui/dropdown/dropdownview~DropdownView} that focuses
 * a specific child in DOM when the dropdown {@link module:ui/dropdown/dropdownview~DropdownView#isOpen gets open}.
 *
 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView A dropdown instance to which the focus behavior will be added.
 * @param {Function} childSelectorCallback A callback executed when the dropdown gets open. It should return a {@link module:ui/view~View}
 * instance (child of {@link module:ui/dropdown/dropdownview~DropdownView#panelView}) that will get focused or a falsy value.
 * If falsy value is returned, a default behavior of the dropdown will engage focusing the first focusable child in
 * the {@link module:ui/dropdown/dropdownview~DropdownView#panelView}.
 */
function focusChildOnDropdownOpen(dropdownView, childSelectorCallback) {
    dropdownView.on('change:isOpen', () => {
        if (!dropdownView.isOpen) {
            return;
        }
        const childToFocus = childSelectorCallback();
        if (!childToFocus) {
            return;
        }
        if (typeof childToFocus.focus === 'function') {
            childToFocus.focus();
        }
        else {
            /**
             * The child view of a {@link module:ui/dropdown/dropdownview~DropdownView dropdown} is missing the `focus()` method
             * and could not be focused when the dropdown got {@link module:ui/dropdown/dropdownview~DropdownView#isOpen open}.
             *
             * Making the content of a dropdown focusable in this case greatly improves the accessibility. Please make the view instance
             * implements the {@link module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable focusable interface} for the best user
             * experience.
             *
             * @error ui-dropdown-focus-child-on-open-child-missing-focus
             * @param {module:ui/view~View} view
             */
            logWarning('ui-dropdown-focus-child-on-open-child-missing-focus', { view: childToFocus });
        }
        // * Let the panel show up first (do not focus an invisible element).
        // * Execute after focusDropdownPanelOnOpen(). See focusDropdownPanelOnOpen() to learn more.
    }, { priority: src_priorities.low - 10 });
}
// Add a set of default behaviors to dropdown view.
//
// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
function addDefaultBehavior(dropdownView) {
    closeDropdownOnClickOutside(dropdownView);
    closeDropdownOnExecute(dropdownView);
    closeDropdownOnBlur(dropdownView);
    focusDropdownContentsOnArrows(dropdownView);
    focusDropdownButtonOnClose(dropdownView);
    focusDropdownPanelOnOpen(dropdownView);
}
// Adds a behavior to a dropdownView that closes opened dropdown when user clicks outside the dropdown.
//
// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
function closeDropdownOnClickOutside(dropdownView) {
    dropdownView.on('render', () => {
        clickoutsidehandler_clickOutsideHandler({
            emitter: dropdownView,
            activator: () => dropdownView.isOpen,
            callback: () => {
                dropdownView.isOpen = false;
            },
            contextElements: [dropdownView.element]
        });
    });
}
// Adds a behavior to a dropdownView that closes the dropdown view on "execute" event.
//
// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
function closeDropdownOnExecute(dropdownView) {
    // Close the dropdown when one of the list items has been executed.
    dropdownView.on('execute', evt => {
        // Toggling a switch button view should not close the dropdown.
        if (evt.source instanceof SwitchButtonView) {
            return;
        }
        dropdownView.isOpen = false;
    });
}
// Adds a behavior to a dropdown view that closes opened dropdown when it loses focus.
//
// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
function closeDropdownOnBlur(dropdownView) {
    dropdownView.focusTracker.on('change:isFocused', (evt, name, isFocused) => {
        if (dropdownView.isOpen && !isFocused) {
            dropdownView.isOpen = false;
        }
    });
}
// Adds a behavior to a dropdownView that focuses the dropdown's panel view contents on keystrokes.
//
// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
function focusDropdownContentsOnArrows(dropdownView) {
    // If the dropdown panel is already open, the arrow down key should focus the first child of the #panelView.
    dropdownView.keystrokes.set('arrowdown', (data, cancel) => {
        if (dropdownView.isOpen) {
            dropdownView.panelView.focus();
            cancel();
        }
    });
    // If the dropdown panel is already open, the arrow up key should focus the last child of the #panelView.
    dropdownView.keystrokes.set('arrowup', (data, cancel) => {
        if (dropdownView.isOpen) {
            dropdownView.panelView.focusLast();
            cancel();
        }
    });
}
// Adds a behavior that focuses the #buttonView when the dropdown was closed but focus was within the #panelView element.
// This makes sure the focus is never lost.
//
// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
function focusDropdownButtonOnClose(dropdownView) {
    dropdownView.on('change:isOpen', (evt, name, isOpen) => {
        if (isOpen) {
            return;
        }
        // If the dropdown was closed, move the focus back to the button (#12125).
        // Don't touch the focus, if it moved somewhere else (e.g. moved to the editing root on #execute) (#12178).
        // Note: Don't use the state of the DropdownView#focusTracker here. It fires #blur with the timeout.
        if (dropdownView.panelView.element.contains(dom_global.document.activeElement)) {
            dropdownView.buttonView.focus();
        }
    });
}
// Adds a behavior that focuses the #panelView when dropdown gets open (accessibility).
//
// @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
function focusDropdownPanelOnOpen(dropdownView) {
    dropdownView.on('change:isOpen', (evt, name, isOpen) => {
        if (!isOpen) {
            return;
        }
        // Focus the first item in the dropdown when the dropdown opened.
        dropdownView.panelView.focus();
        // * Let the panel show up first (do not focus an invisible element).
        // * Also, execute before focusChildOnDropdownOpen() to make sure this helper does not break the
        //   focus of a specific child by kicking in too late and resetting the focus in the panel.
    }, { priority: 'low' });
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/editorui/editorui.css
var editorui = __webpack_require__(4547);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/editorui/editorui.css

            

var editorui_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

editorui_options.insert = "head";
editorui_options.singleton = true;

var editorui_update = injectStylesIntoStyleTag_default()(editorui/* default */.Z, editorui_options);



/* harmony default export */ const editorui_editorui = (editorui/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/editorui/editoruiview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/editorui/editoruiview
 */



/**
 * The editor UI view class. Base class for the editor main views.
 *
 * @extends module:ui/view~View
 */
class EditorUIView extends src_view_View {
    /**
     * Creates an instance of the editor UI view class.
     *
     * @param {module:utils/locale~Locale} [locale] The locale instance.
     */
    constructor(locale) {
        super(locale);
        /**
         * Collection of the child views, detached from the DOM
         * structure of the editor, like panels, icons etc.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection} #body
         */
        this.body = new BodyCollection(locale);
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.body.attachToDom();
    }
    /**
     * @inheritDoc
     */
    destroy() {
        this.body.detachFromDom();
        return super.destroy();
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/label/label.css
var label = __webpack_require__(2751);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/label/label.css

            

var label_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

label_options.insert = "head";
label_options.singleton = true;

var label_update = injectStylesIntoStyleTag_default()(label/* default */.Z, label_options);



/* harmony default export */ const label_label = (label/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/label/labelview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/label/labelview
 */



/**
 * The label view class.
 *
 * @extends module:ui/view~View
 */
class LabelView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        /**
         * The text of the label.
         *
         * @observable
         * @member {String} #text
         */
        this.set('text', undefined);
        /**
         * The `for` attribute of the label (i.e. to pair with an `<input>` element).
         *
         * @observable
         * @member {String} #for
         */
        this.set('for', undefined);
        /**
         * An unique id of the label. It can be used by other UI components to reference
         * the label, for instance, using the `aria-describedby` DOM attribute.
         *
         * @member {String} #id
         */
        this.id = `ck-editor__label_${uid()}`;
        const bind = this.bindTemplate;
        this.setTemplate({
            tag: 'label',
            attributes: {
                class: [
                    'ck',
                    'ck-label'
                ],
                id: this.id,
                for: bind.to('for')
            },
            children: [
                {
                    text: bind.to('text')
                }
            ]
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/editorui/boxed/boxededitoruiview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/editorui/boxed/boxededitoruiview
 */


/**
 * The boxed editor UI view class. This class represents an editor interface
 * consisting of a toolbar and an editable area, enclosed within a box.
 *
 * @extends module:ui/editorui/editoruiview~EditorUIView
 */
class BoxedEditorUIView extends EditorUIView {
    /**
     * Creates an instance of the boxed editor UI view class.
     *
     * @param {module:utils/locale~Locale} locale The locale instance..
     */
    constructor(locale) {
        super(locale);
        /**
         * Collection of the child views located in the top (`.ck-editor__top`)
         * area of the UI.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.top = this.createCollection();
        /**
         * Collection of the child views located in the main (`.ck-editor__main`)
         * area of the UI.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.main = this.createCollection();
        /**
         * Voice label of the UI.
         *
         * @protected
         * @readonly
         * @member {module:ui/view~View} #_voiceLabelView
         */
        this._voiceLabelView = this._createVoiceLabel();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-reset',
                    'ck-editor',
                    'ck-rounded-corners'
                ],
                role: 'application',
                dir: locale.uiLanguageDirection,
                lang: locale.uiLanguage,
                'aria-labelledby': this._voiceLabelView.id
            },
            children: [
                this._voiceLabelView,
                {
                    tag: 'div',
                    attributes: {
                        class: [
                            'ck',
                            'ck-editor__top',
                            'ck-reset_all'
                        ],
                        role: 'presentation'
                    },
                    children: this.top
                },
                {
                    tag: 'div',
                    attributes: {
                        class: [
                            'ck',
                            'ck-editor__main'
                        ],
                        role: 'presentation'
                    },
                    children: this.main
                }
            ]
        });
    }
    /**
     * Creates a voice label view instance.
     *
     * @private
     * @returns {module:ui/label/labelview~LabelView}
     */
    _createVoiceLabel() {
        const t = this.t;
        const voiceLabel = new LabelView();
        voiceLabel.text = t('Rich Text Editor');
        voiceLabel.extendTemplate({
            attributes: {
                class: 'ck-voice-label'
            }
        });
        return voiceLabel;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/editableui/editableuiview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/editableui/editableuiview
 */

/**
 * The editable UI view class.
 *
 * @extends module:ui/view~View
 */
class EditableUIView extends src_view_View {
    /**
     * Creates an instance of EditableUIView class.
     *
     * @param {module:utils/locale~Locale} [locale] The locale instance.
     * @param {module:engine/view/view~View} editingView The editing view instance the editable is related to.
     * @param {HTMLElement} [editableElement] The editable element. If not specified, this view
     * should create it. Otherwise, the existing element should be used.
     */
    constructor(locale, editingView, editableElement) {
        super(locale);
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-content',
                    'ck-editor__editable',
                    'ck-rounded-corners'
                ],
                lang: locale.contentLanguage,
                dir: locale.contentLanguageDirection
            }
        });
        /**
         * The name of the editable UI view.
         *
         * @member {String} #name
         */
        this.name = null;
        /**
         * Controls whether the editable is focused, i.e. the user is typing in it.
         *
         * @observable
         * @member {Boolean} #isFocused
         */
        this.set('isFocused', false);
        /**
         * The element which is the main editable element (usually the one with `contentEditable="true"`).
         *
         * @private
         * @type {HTMLElement}
         */
        this._editableElement = editableElement;
        /**
         * Whether an external {@link #_editableElement} was passed into the constructor, which also means
         * the view will not render its {@link #template}.
         *
         * @private
         * @type {Boolean}
         */
        this._hasExternalElement = !!this._editableElement;
        /**
         * The editing view instance the editable is related to. Editable uses the editing
         * view to dynamically modify its certain DOM attributes after {@link #render rendering}.
         *
         * **Note**: The DOM attributes are performed by the editing view and not UI
         * {@link module:ui/view~View#bindTemplate template bindings} because once rendered,
         * the editable DOM element must remain under the full control of the engine to work properly.
         *
         * @protected
         * @type {module:engine/view/view~View}
         */
        this._editingView = editingView;
    }
    /**
     * Renders the view by either applying the {@link #template} to the existing
     * {@link #_editableElement} or assigning {@link #element} as {@link #_editableElement}.
     */
    render() {
        super.render();
        if (this._hasExternalElement) {
            this.template.apply(this.element = this._editableElement);
        }
        else {
            this._editableElement = this.element;
        }
        this.on('change:isFocused', () => this._updateIsFocusedClasses());
        this._updateIsFocusedClasses();
    }
    /**
     * @inheritDoc
     */
    destroy() {
        if (this._hasExternalElement) {
            this.template.revert(this._editableElement);
        }
        super.destroy();
    }
    /**
     * Updates the `ck-focused` and `ck-blurred` CSS classes on the {@link #element} according to
     * the {@link #isFocused} property value using the {@link #_editingView editing view} API.
     *
     * @private
     */
    _updateIsFocusedClasses() {
        const editingView = this._editingView;
        if (editingView.isRenderingInProgress) {
            updateAfterRender(this);
        }
        else {
            update(this);
        }
        function update(view) {
            editingView.change(writer => {
                const viewRoot = editingView.document.getRoot(view.name);
                writer.addClass(view.isFocused ? 'ck-focused' : 'ck-blurred', viewRoot);
                writer.removeClass(view.isFocused ? 'ck-blurred' : 'ck-focused', viewRoot);
            });
        }
        // In a case of a multi-root editor, a callback will be attached more than once (one callback for each root).
        // While executing one callback the `isRenderingInProgress` observable is changing what causes executing another
        // callback and render is called inside the already pending render.
        // We need to be sure that callback is executed only when the value has changed from `true` to `false`.
        // See https://github.com/ckeditor/ckeditor5/issues/1676.
        function updateAfterRender(view) {
            editingView.once('change:isRenderingInProgress', (evt, name, value) => {
                if (!value) {
                    update(view);
                }
                else {
                    updateAfterRender(view);
                }
            });
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/editableui/inline/inlineeditableuiview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/editableui/inline/inlineeditableuiview
 */

/**
 * The inline editable UI class implementing an inline {@link module:ui/editableui/editableuiview~EditableUIView}.
 *
 * @extends module:ui/editableui/editableuiview~EditableUIView
 */
class InlineEditableUIView extends EditableUIView {
    /**
     * Creates an instance of the InlineEditableUIView class.
     *
     * @param {module:utils/locale~Locale} [locale] The locale instance.
     * @param {module:engine/view/view~View} editingView The editing view instance the editable is related to.
     * @param {HTMLElement} [editableElement] The editable element. If not specified, the
     * {@link module:ui/editableui/editableuiview~EditableUIView}
     * will create it. Otherwise, the existing element will be used.
     * @param {Object} [options] Additional configuration of the view.
     * @param {Function} [options.label] A function that gets called with the instance of this view as an argument
     * and should return a string that represents the label of the editable for assistive technologies. If not provided,
     * a default label generator is used.
     */
    constructor(locale, editingView, editableElement, options = {}) {
        super(locale, editingView, editableElement);
        const t = locale.t;
        this.extendTemplate({
            attributes: {
                role: 'textbox',
                class: 'ck-editor__editable_inline'
            }
        });
        /**
         * A function that gets called with the instance of this view as an argument and should return a string that
         * represents the label of the editable for assistive technologies.
         *
         * @private
         * @readonly
         * @param {Function}
         */
        this._generateLabel = options.label || (() => t('Editor editing area: %0', this.name));
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        const editingView = this._editingView;
        editingView.change(writer => {
            const viewRoot = editingView.document.getRoot(this.name);
            writer.setAttribute('aria-label', this._generateLabel(this), viewRoot);
        });
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/formheader/formheader.css
var formheader = __webpack_require__(5523);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/formheader/formheader.css

            

var formheader_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

formheader_options.insert = "head";
formheader_options.singleton = true;

var formheader_update = injectStylesIntoStyleTag_default()(formheader/* default */.Z, formheader_options);



/* harmony default export */ const formheader_formheader = (formheader/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/formheader/formheaderview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/formheader/formheaderview
 */


/**
 * The class component representing a form header view. It should be used in more advanced forms to
 * describe the main purpose of the form.
 *
 * By default the component contains a bolded label view that has to be set. The label is usually a short (at most 3-word) string.
 * The component can also be extended by any other elements, like: icons, dropdowns, etc.
 *
 * It is used i.a.
 * by {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView}
 * and {@link module:special-characters/ui/specialcharactersnavigationview~SpecialCharactersNavigationView}.
 *
 * The latter is an example, where the component has been extended by {@link module:ui/dropdown/dropdownview~DropdownView} view.
 *
 * @extends module:ui/view~View
 */
class FormHeaderView extends src_view_View {
    /**
     * Creates an instance of the form header class.
     *
     * @param {module:utils/locale~Locale} locale The locale instance.
     * @param {Object} options
     * @param {String} options.label A label.
     * @param {String} [options.class] An additional class.
     */
    constructor(locale, options = {}) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * The label of the header.
         *
         * @observable
         * @member {String} #label
         */
        this.set('label', options.label || '');
        /**
         * An additional CSS class added to the {@link #element}.
         *
         * @observable
         * @member {String} #class
         */
        this.set('class', options.class || null);
        /**
         * A collection of header items.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.children = this.createCollection();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-form__header',
                    bind.to('class')
                ]
            },
            children: this.children
        });
        const label = new src_view_View(locale);
        label.setTemplate({
            tag: 'span',
            attributes: {
                class: [
                    'ck',
                    'ck-form__header__label'
                ]
            },
            children: [
                { text: bind.to('label') }
            ]
        });
        this.children.add(label);
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/input/input.css
var input = __webpack_require__(6985);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/input/input.css

            

var input_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

input_options.insert = "head";
input_options.singleton = true;

var input_update = injectStylesIntoStyleTag_default()(input/* default */.Z, input_options);



/* harmony default export */ const input_input = (input/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/input/inputview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/input/inputview
 */



/**
 * The base input view class.
 *
 * @extends module:ui/view~View
 */
class InputView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        /**
         * The value of the input.
         *
         * @observable
         * @member {String} #value
         */
        this.set('value', undefined);
        /**
         * The `id` attribute of the input (i.e. to pair with a `<label>` element).
         *
         * @observable
         * @member {String} #id
         */
        this.set('id', undefined);
        /**
         * The `placeholder` attribute of the input.
         *
         * @observable
         * @member {String} #placeholder
         */
        this.set('placeholder', undefined);
        /**
         * Controls whether the input view is in read-only mode.
         *
         * @observable
         * @member {Boolean} #isReadOnly
         */
        this.set('isReadOnly', false);
        /**
         * Set to `true` when the field has some error. Usually controlled via
         * {@link module:ui/labeledinput/labeledinputview~LabeledInputView#errorText}.
         *
         * @observable
         * @member {Boolean} #hasError
         */
        this.set('hasError', false);
        /**
         * The `id` of the element describing this field, e.g. when it has
         * some error; it helps screen readers read the error text.
         *
         * @observable
         * @member {Boolean} #ariaDescribedById
         */
        this.set('ariaDescribedById', undefined);
        /**
         * Stores information about the editor UI focus and propagates it so various plugins and components
         * are unified as a focus group.
         *
         * @readonly
         * @member {module:utils/focustracker~FocusTracker} #focusTracker
         */
        this.focusTracker = new FocusTracker();
        /**
         * An observable flag set to `true` when the input is currently focused by the user.
         * Set to `false` otherwise.
         *
         * @readonly
         * @observable
         * @member {Boolean} #isFocused
         * @default false
         */
        this.bind('isFocused').to(this.focusTracker);
        /**
         * An observable flag set to `true` when the input contains no text, i.e.
         * when {@link #value} is `''`, `null`, or `false`.
         *
         * @readonly
         * @observable
         * @member {Boolean} #isEmpty
         * @default true
         */
        this.set('isEmpty', true);
        /**
         * Corresponds to the `inputmode` DOM attribute. Can be `text`, `numeric`, `decimal`, etc.
         *
         * @observable
         * @member {Boolean} #inputMode
         * @default 'text'
         */
        this.set('inputMode', 'text');
        const bind = this.bindTemplate;
        this.setTemplate({
            tag: 'input',
            attributes: {
                class: [
                    'ck',
                    'ck-input',
                    bind.if('isFocused', 'ck-input_focused'),
                    bind.if('isEmpty', 'ck-input-text_empty'),
                    bind.if('hasError', 'ck-error')
                ],
                id: bind.to('id'),
                placeholder: bind.to('placeholder'),
                readonly: bind.to('isReadOnly'),
                inputmode: bind.to('inputMode'),
                'aria-invalid': bind.if('hasError', true),
                'aria-describedby': bind.to('ariaDescribedById')
            },
            on: {
                input: bind.to((...args) => {
                    this.fire('input', ...args);
                    this._updateIsEmpty();
                }),
                change: bind.to(this._updateIsEmpty.bind(this))
            }
        });
        /**
         * Fired when the user types in the input. Corresponds to the native
         * DOM `input` event.
         *
         * @event input
         */
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.focusTracker.add(this.element);
        this._setDomElementValue(this.value);
        this._updateIsEmpty();
        // Bind `this.value` to the DOM element's value.
        // We cannot use `value` DOM attribute because removing it on Edge does not clear the DOM element's value property.
        this.on('change:value', (evt, name, value) => {
            this._setDomElementValue(value);
            this._updateIsEmpty();
        });
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this.focusTracker.destroy();
    }
    /**
     * Moves the focus to the input and selects the value.
     */
    select() {
        this.element.select();
    }
    /**
     * Focuses the input.
     */
    focus() {
        this.element.focus();
    }
    /**
     * Updates the {@link #isEmpty} property value on demand.
     *
     * @private
     */
    _updateIsEmpty() {
        this.isEmpty = isInputElementEmpty(this.element);
    }
    /**
     * Sets the `value` property of the {@link #element DOM element} on demand.
     *
     * @private
     */
    _setDomElementValue(value) {
        this.element.value = (!value && value !== 0) ? '' : value;
    }
}
function isInputElementEmpty(domElement) {
    return !domElement.value;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/inputtext/inputtextview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/inputtext/inputtextview
 */

/**
 * The text input view class.
 *
 * @extends module:ui/input/inputview~InputView
 */
class InputTextView extends InputView {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        this.extendTemplate({
            attributes: {
                type: 'text',
                class: [
                    'ck-input-text'
                ]
            }
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/inputnumber/inputnumberview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/inputnumber/inputnumberview
 */

/**
 * The number input view class.
 *
 * @extends module:ui/input/inputview~InputView
 */
class InputNumberView extends InputView {
    /**
     * Creates an instance of the input number view.
     *
     * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
     * @param {Object} [options] The options of the input.
     * @param {Number} [options.min] The value of the `min` DOM attribute (the lowest accepted value).
     * @param {Number} [options.max] The value of the `max` DOM attribute (the highest accepted value).
     * @param {Number} [options.step] The value of the `step` DOM attribute.
     */
    constructor(locale, { min, max, step } = {}) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * The value of the `min` DOM attribute (the lowest accepted value) set on the {@link #element}.
         *
         * @observable
         * @default undefined
         * @member {Number} #min
         */
        this.set('min', min);
        /**
         * The value of the `max` DOM attribute (the highest accepted value) set on the {@link #element}.
         *
         * @observable
         * @default undefined
         * @member {Number} #max
         */
        this.set('max', max);
        /**
         * The value of the `step` DOM attribute set on the {@link #element}.
         *
         * @observable
         * @default undefined
         * @member {Number} #step
         */
        this.set('step', step);
        this.extendTemplate({
            attributes: {
                type: 'number',
                class: [
                    'ck-input-number'
                ],
                min: bind.to('min'),
                max: bind.to('max'),
                step: bind.to('step')
            }
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/iframe/iframeview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/iframe/iframeview
 */

/**
 * The iframe view class.
 *
 * 		const iframe = new IframeView();
 *
 *		iframe.render();
 *		document.body.appendChild( iframe.element );
 *
 * 		iframe.on( 'loaded', () => {
 *			console.log( 'The iframe has loaded', iframe.element.contentWindow );
 *		} );
 *
 * 		iframe.element.src = 'https://ckeditor.com';
 *
 * @extends module:ui/view~View
 */
class IframeView extends (/* unused pure expression or super */ null && (View)) {
    /**
     * Creates a new instance of the iframe view.
     *
     * @param {module:utils/locale~Locale} [locale] The locale instance.
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        this.setTemplate({
            tag: 'iframe',
            attributes: {
                class: [
                    'ck',
                    'ck-reset_all'
                ],
                // It seems that we need to allow scripts in order to be able to listen to events.
                // TODO: Research that. Perhaps the src must be set?
                sandbox: 'allow-same-origin allow-scripts'
            },
            on: {
                load: bind.to('loaded')
            }
        });
    }
    /**
     * Renders the iframe's {@link #element} and returns a `Promise` for asynchronous
     * child `contentDocument` loading process.
     *
     * @returns {Promise} A promise which resolves once the iframe `contentDocument` has
     * been {@link #event:loaded}.
     */
    render() {
        return new Promise(resolve => {
            this.on('loaded', resolve);
            return super.render();
        });
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/labeledfield/labeledfieldview.css
var labeledfieldview = __webpack_require__(8111);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/labeledfield/labeledfieldview.css

            

var labeledfieldview_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

labeledfieldview_options.insert = "head";
labeledfieldview_options.singleton = true;

var labeledfieldview_update = injectStylesIntoStyleTag_default()(labeledfieldview/* default */.Z, labeledfieldview_options);



/* harmony default export */ const labeledfield_labeledfieldview = (labeledfieldview/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/labeledfield/labeledfieldview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/labeledfield/labeledfieldview
 */




/**
 * The labeled field view class. It can be used to enhance any view with the following features:
 *
 * * a label,
 * * (optional) an error message,
 * * (optional) an info (status) text,
 *
 * all bound logically by proper DOM attributes for UX and accessibility.  It also provides an interface
 * (e.g. observable properties) that allows controlling those additional features.
 *
 * The constructor of this class requires a callback that returns a view to be labeled. The callback
 * is called with unique ids that allow binding of DOM properties:
 *
 *		const labeledInputView = new LabeledFieldView( locale, ( labeledFieldView, viewUid, statusUid ) => {
 *			const inputView = new InputTextView( labeledFieldView.locale );
 *
 *			inputView.set( {
 *				id: viewUid,
 *				ariaDescribedById: statusUid
 *			} );
 *
 *			inputView.bind( 'isReadOnly' ).to( labeledFieldView, 'isEnabled', value => !value );
 *			inputView.bind( 'hasError' ).to( labeledFieldView, 'errorText', value => !!value );
 *
 *			return inputView;
 *		} );
 *
 *		labeledInputView.label = 'User name';
 *		labeledInputView.infoText = 'Full name like for instance, John Doe.';
 *		labeledInputView.render();
 *
 *		document.body.append( labeledInputView.element );
 *
 * See {@link module:ui/labeledfield/utils} to discover ready–to–use labeled input helpers for common
 * UI components.
 *
 * @extends module:ui/view~View
 */
class LabeledFieldView extends src_view_View {
    /**
     * Creates an instance of the labeled field view class using a provided creator function
     * that provides the view to be labeled.
     *
     * @param {module:utils/locale~Locale} locale The locale instance.
     * @param {Function} viewCreator A function that returns a {@link module:ui/view~View}
     * that will be labeled. The following arguments are passed to the creator function:
     *
     * * an instance of the `LabeledFieldView` to allow binding observable properties,
     * * an UID string that connects the {@link #labelView label} and the labeled field view in DOM,
     * * an UID string that connects the {@link #statusView status} and the labeled field view in DOM.
     */
    constructor(locale, viewCreator) {
        super(locale);
        const viewUid = `ck-labeled-field-view-${uid()}`;
        const statusUid = `ck-labeled-field-view-status-${uid()}`;
        /**
         * The field view that gets labeled.
         *
         * @member {module:ui/view~View} #fieldView
         */
        this.fieldView = viewCreator(this, viewUid, statusUid);
        /**
         * The text of the label.
         *
         * @observable
         * @member {String} #label
         */
        this.set('label', undefined);
        /**
         * Controls whether the component is in read-only mode.
         *
         * @observable
         * @member {Boolean} #isEnabled
         */
        this.set('isEnabled', true);
        /**
         * An observable flag set to `true` when {@link #fieldView} is empty (`false` otherwise).
         *
         * @readonly
         * @observable
         * @member {Boolean} #isEmpty
         * @default true
         */
        this.set('isEmpty', true);
        /**
         * An observable flag set to `true` when {@link #fieldView} is currently focused by
         * the user (`false` otherwise).
         *
         * @readonly
         * @observable
         * @member {Boolean} #isFocused
         * @default false
         */
        this.set('isFocused', false);
        /**
         * The validation error text. When set, it will be displayed
         * next to the {@link #fieldView} as a typical validation error message.
         * Set it to `null` to hide the message.
         *
         * **Note:** Setting this property to anything but `null` will automatically
         * make the `hasError` of the {@link #fieldView} `true`.
         *
         * @observable
         * @member {String|null} #errorText
         */
        this.set('errorText', null);
        /**
         * The additional information text displayed next to the {@link #fieldView} which can
         * be used to inform the user about its purpose, provide help or hints.
         *
         * Set it to `null` to hide the message.
         *
         * **Note:** This text will be displayed in the same place as {@link #errorText} but the
         * latter always takes precedence: if the {@link #errorText} is set, it replaces
         * {@link #infoText}.
         *
         * @observable
         * @member {String|null} #infoText
         * @default null
         */
        this.set('infoText', null);
        /**
         * (Optional) The additional CSS class set on the dropdown {@link #element}.
         *
         * @observable
         * @member {String} #class
         */
        this.set('class', undefined);
        /**
         * The content of the `placeholder` attribute of the {@link #fieldView}.
         *
         * @observable
         * @member {String} #placeholder
         */
        this.set('placeholder', undefined);
        /**
         * The label view instance that describes the entire view.
         *
         * @member {module:ui/label/labelview~LabelView} #labelView
         */
        this.labelView = this._createLabelView(viewUid);
        /**
         * The status view for the {@link #fieldView}. It displays {@link #errorText} and
         * {@link #infoText}.
         *
         * @member {module:ui/view~View} #statusView
         */
        this.statusView = this._createStatusView(statusUid);
        /**
         * A collection of children of the internal wrapper element. Allows inserting additional DOM elements (views) next to
         * the {@link #fieldView} for easy styling (e.g. positioning).
         *
         * By default, the collection contains {@link #fieldView} and {@link #labelView}.
         *
         * @member {module:ui/viewcollection~ViewCollection} #fieldWrapperChildren
         */
        this.fieldWrapperChildren = this.createCollection([this.fieldView, this.labelView]);
        /**
         * The combined status text made of {@link #errorText} and {@link #infoText}.
         * Note that when present, {@link #errorText} always takes precedence in the
         * status.
         *
         * @see #errorText
         * @see #infoText
         * @see #statusView
         * @private
         * @observable
         * @member {String|null} #_statusText
         */
        this.bind('_statusText').to(this, 'errorText', this, 'infoText', (errorText, infoText) => errorText || infoText);
        const bind = this.bindTemplate;
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-labeled-field-view',
                    bind.to('class'),
                    bind.if('isEnabled', 'ck-disabled', value => !value),
                    bind.if('isEmpty', 'ck-labeled-field-view_empty'),
                    bind.if('isFocused', 'ck-labeled-field-view_focused'),
                    bind.if('placeholder', 'ck-labeled-field-view_placeholder'),
                    bind.if('errorText', 'ck-error')
                ]
            },
            children: [
                {
                    tag: 'div',
                    attributes: {
                        class: [
                            'ck',
                            'ck-labeled-field-view__input-wrapper'
                        ]
                    },
                    children: this.fieldWrapperChildren
                },
                this.statusView
            ]
        });
    }
    /**
     * Creates label view class instance and bind with view.
     *
     * @private
     * @param {String} id Unique id to set as labelView#for attribute.
     * @returns {module:ui/label/labelview~LabelView}
     */
    _createLabelView(id) {
        const labelView = new LabelView(this.locale);
        labelView.for = id;
        labelView.bind('text').to(this, 'label');
        return labelView;
    }
    /**
     * Creates the status view instance. It displays {@link #errorText} and {@link #infoText}
     * next to the {@link #fieldView}. See {@link #_statusText}.
     *
     * @private
     * @param {String} statusUid Unique id of the status, shared with the {@link #fieldView view's}
     * `aria-describedby` attribute.
     * @returns {module:ui/view~View}
     */
    _createStatusView(statusUid) {
        const statusView = new src_view_View(this.locale);
        const bind = this.bindTemplate;
        statusView.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-labeled-field-view__status',
                    bind.if('errorText', 'ck-labeled-field-view__status_error'),
                    bind.if('_statusText', 'ck-hidden', value => !value)
                ],
                id: statusUid,
                role: bind.if('errorText', 'alert')
            },
            children: [
                {
                    text: bind.to('_statusText')
                }
            ]
        });
        return statusView;
    }
    /**
     * Focuses the {@link #fieldView}.
     */
    focus() {
        this.fieldView.focus();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/labeledfield/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/labeledfield/utils
 */



/**
 * A helper for creating labeled inputs.
 *
 * It creates an instance of a {@link module:ui/inputtext/inputtextview~InputTextView input text} that is
 * logically related to a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled view} in DOM.
 *
 * The helper does the following:
 *
 * * It sets input's `id` and `ariaDescribedById` attributes.
 * * It binds input's `isReadOnly` to the labeled view.
 * * It binds input's `hasError` to the labeled view.
 * * It enables a logic that cleans up the error when user starts typing in the input.
 *
 * Usage:
 *
 *		const labeledInputView = new LabeledFieldView( locale, createLabeledInputText );
 *		console.log( labeledInputView.fieldView ); // A text input instance.
 *
 * @param {module:ui/labeledfield/labeledfieldview~LabeledFieldView} labeledFieldView The instance of the labeled field view.
 * @param {String} viewUid An UID string that allows DOM logical connection between the
 * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#labelView labeled view's label} and the input.
 * @param {String} statusUid An UID string that allows DOM logical connection between the
 * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the input.
 * @returns {module:ui/inputtext/inputtextview~InputTextView} The input text view instance.
 */
function createLabeledInputText(labeledFieldView, viewUid, statusUid) {
    const inputView = new InputTextView(labeledFieldView.locale);
    inputView.set({
        id: viewUid,
        ariaDescribedById: statusUid
    });
    inputView.bind('isReadOnly').to(labeledFieldView, 'isEnabled', value => !value);
    inputView.bind('hasError').to(labeledFieldView, 'errorText', value => !!value);
    inputView.on('input', () => {
        // UX: Make the error text disappear and disable the error indicator as the user
        // starts fixing the errors.
        labeledFieldView.errorText = null;
    });
    labeledFieldView.bind('isEmpty', 'isFocused', 'placeholder').to(inputView);
    return inputView;
}
/**
 * A helper for creating labeled number inputs.
 *
 * It creates an instance of a {@link module:ui/inputnumber/inputnumberview~InputNumberView input number} that is
 * logically related to a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled view} in DOM.
 *
 * The helper does the following:
 *
 * * It sets input's `id` and `ariaDescribedById` attributes.
 * * It binds input's `isReadOnly` to the labeled view.
 * * It binds input's `hasError` to the labeled view.
 * * It enables a logic that cleans up the error when user starts typing in the input.
 *
 * Usage:
 *
 *		const labeledInputView = new LabeledFieldView( locale, createLabeledInputNumber );
 *		console.log( labeledInputView.fieldView ); // A number input instance.
 *
 * @param {module:ui/labeledfield/labeledfieldview~LabeledFieldView} labeledFieldView The instance of the labeled field view.
 * @param {String} viewUid An UID string that allows DOM logical connection between the
 * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#labelView labeled view's label} and the input.
 * @param {String} statusUid An UID string that allows DOM logical connection between the
 * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view's status} and the input.
 * @returns {module:ui/inputnumber/inputnumberview~InputNumberView} The input number view instance.
 */
function createLabeledInputNumber(labeledFieldView, viewUid, statusUid) {
    const inputView = new InputNumberView(labeledFieldView.locale);
    inputView.set({
        id: viewUid,
        ariaDescribedById: statusUid,
        inputMode: 'numeric'
    });
    inputView.bind('isReadOnly').to(labeledFieldView, 'isEnabled', value => !value);
    inputView.bind('hasError').to(labeledFieldView, 'errorText', value => !!value);
    inputView.on('input', () => {
        // UX: Make the error text disappear and disable the error indicator as the user
        // starts fixing the errors.
        labeledFieldView.errorText = null;
    });
    labeledFieldView.bind('isEmpty', 'isFocused', 'placeholder').to(inputView);
    return inputView;
}
/**
 * A helper for creating labeled dropdowns.
 *
 * It creates an instance of a {@link module:ui/dropdown/dropdownview~DropdownView dropdown} that is
 * logically related to a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled field view}.
 *
 * The helper does the following:
 *
 * * It sets dropdown's `id` and `ariaDescribedById` attributes.
 * * It binds input's `isEnabled` to the labeled view.
 *
 * Usage:
 *
 *		const labeledInputView = new LabeledFieldView( locale, createLabeledDropdown );
 *		console.log( labeledInputView.fieldView ); // A dropdown instance.
 *
 * @param {module:ui/labeledfield/labeledfieldview~LabeledFieldView} labeledFieldView The instance of the labeled field view.
 * @param {String} viewUid An UID string that allows DOM logical connection between the
 * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#labelView labeled view label} and the dropdown.
 * @param {String} statusUid An UID string that allows DOM logical connection between the
 * {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView#statusView labeled view status} and the dropdown.
 * @returns {module:ui/dropdown/dropdownview~DropdownView} The dropdown view instance.
 */
function createLabeledDropdown(labeledFieldView, viewUid, statusUid) {
    const dropdownView = createDropdown(labeledFieldView.locale);
    dropdownView.set({
        id: viewUid,
        ariaDescribedById: statusUid
    });
    dropdownView.bind('isEnabled').to(labeledFieldView);
    return dropdownView;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/notification/notification.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/notification/notification
 */
/* globals window */

/**
 * The Notification plugin.
 *
 * This plugin sends a few types of notifications: `success`, `info` and `warning`. The notifications need to be
 * handled and displayed by a plugin responsible for showing the UI of the notifications. Using this plugin for dispatching
 * notifications makes it possible to switch the notifications UI.
 *
 * Note that every unhandled and not stopped `warning` notification will be displayed as a system alert.
 * See {@link module:ui/notification/notification~Notification#showWarning}.
 *
 * @extends module:core/contextplugin~ContextPlugin
 */
class Notification extends ContextPlugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Notification';
    }
    /**
     * @inheritDoc
     */
    init() {
        // Each unhandled and not stopped `show:warning` event is displayed as a system alert.
        this.on('show:warning', (evt, data) => {
            window.alert(data.message); // eslint-disable-line no-alert
        }, { priority: 'lowest' });
    }
    /**
     * Shows a success notification.
     *
     * By default, it fires the {@link #event:show:success `show:success` event} with the given `data`. The event namespace can be extended
     * using the `data.namespace` option. For example:
     *
     * 		showSuccess( 'Image is uploaded.', {
     * 			namespace: 'upload:image'
     * 		} );
     *
     * will fire the `show:success:upload:image` event.
     *
     * You can provide the title of the notification:
     *
     *		showSuccess( 'Image is uploaded.', {
     *			title: 'Image upload success'
     *		} );
     *
     * @param {String} message The content of the notification.
     * @param {Object} [data={}] Additional data.
     * @param {String} [data.namespace] Additional event namespace.
     * @param {String} [data.title] The title of the notification.
     */
    showSuccess(message, data = {}) {
        this._showNotification({
            message,
            type: 'success',
            namespace: data.namespace,
            title: data.title
        });
    }
    /**
     * Shows an information notification.
     *
     * By default, it fires the {@link #event:show:info `show:info` event} with the given `data`. The event namespace can be extended
     * using the `data.namespace` option. For example:
     *
     * 		showInfo( 'Editor is offline.', {
     * 			namespace: 'editor:status'
     * 		} );
     *
     * will fire the `show:info:editor:status` event.
     *
     * You can provide the title of the notification:
     *
     *		showInfo( 'Editor is offline.', {
     *			title: 'Network information'
     *		} );
     *
     * @param {String} message The content of the notification.
     * @param {Object} [data={}] Additional data.
     * @param {String} [data.namespace] Additional event namespace.
     * @param {String} [data.title] The title of the notification.
     */
    showInfo(message, data = {}) {
        this._showNotification({
            message,
            type: 'info',
            namespace: data.namespace,
            title: data.title
        });
    }
    /**
     * Shows a warning notification.
     *
     * By default, it fires the {@link #event:show:warning `show:warning` event}
     * with the given `data`. The event namespace can be extended using the `data.namespace` option. For example:
     *
     * 		showWarning( 'Image upload error.', {
     * 			namespace: 'upload:image'
     * 		} );
     *
     * will fire the `show:warning:upload:image` event.
     *
     * You can provide the title of the notification:
     *
     *		showWarning( 'Image upload error.', {
     *			title: 'Upload failed'
     *		} );
     *
     * Note that each unhandled and not stopped `warning` notification will be displayed as a system alert.
     * The plugin responsible for displaying warnings should `stop()` the event to prevent displaying it as an alert:
     *
     * 		notifications.on( 'show:warning', ( evt, data ) => {
     * 			// Do something with the data.
     *
     * 			// Stop this event to prevent displaying it as an alert.
     * 			evt.stop();
     * 		} );
     *
     * You can attach many listeners to the same event and `stop()` this event in a listener with a low priority:
     *
     * 		notifications.on( 'show:warning', ( evt, data ) => {
     * 			// Show the warning in the UI, but do not stop it.
     * 		} );
     *
     * 		notifications.on( 'show:warning', ( evt, data ) => {
     * 			// Log the warning to some error tracker.
     *
     * 			// Stop this event to prevent displaying it as an alert.
     * 			evt.stop();
     * 		}, { priority: 'low' } );
     *
     * @param {String} message The content of the notification.
     * @param {Object} [data={}] Additional data.
     * @param {String} [data.namespace] Additional event namespace.
     * @param {String} [data.title] The title of the notification.
     */
    showWarning(message, data = {}) {
        this._showNotification({
            message,
            type: 'warning',
            namespace: data.namespace,
            title: data.title
        });
    }
    /**
     * Fires the `show` event with the specified type, namespace and message.
     *
     * @private
     * @param {Object} data The message data.
     * @param {String} data.message The content of the notification.
     * @param {'success'|'info'|'warning'} data.type The type of the message.
     * @param {String} [data.namespace] Additional event namespace.
     * @param {String} [data.title=''] The title of the notification.
     */
    _showNotification(data) {
        const event = data.namespace ?
            `show:${data.type}:${data.namespace}` :
            `show:${data.type}`;
        this.fire(event, {
            message: data.message,
            type: data.type,
            title: data.title || ''
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/model.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/model
 */


/**
 * The base MVC model class.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class model_Model extends Observable {
    /**
     * Creates a new Model instance.
     *
     * @param {Object} [attributes] The model state attributes to be defined during the instance creation.
     * @param {Object} [properties] The (out of state) properties to be appended to the instance during creation.
     */
    constructor(attributes, properties) {
        super();
        // Extend this instance with the additional (out of state) properties.
        if (properties) {
            lodash_es_assignIn(this, properties);
        }
        // Initialize the attributes.
        if (attributes) {
            this.set(attributes);
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/icons/previous-arrow.svg
/* harmony default export */ const previous_arrow = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M11.463 5.187a.888.888 0 1 1 1.254 1.255L9.16 10l3.557 3.557a.888.888 0 1 1-1.254 1.255L7.26 10.61a.888.888 0 0 1 .16-1.382l4.043-4.042z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/icons/next-arrow.svg
/* harmony default export */ const next_arrow = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8.537 14.813a.888.888 0 1 1-1.254-1.255L10.84 10 7.283 6.442a.888.888 0 1 1 1.254-1.255L12.74 9.39a.888.888 0 0 1-.16 1.382l-4.043 4.042z\"/></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/balloonrotator.css
var balloonrotator = __webpack_require__(1757);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/balloonrotator.css

            

var balloonrotator_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

balloonrotator_options.insert = "head";
balloonrotator_options.singleton = true;

var balloonrotator_update = injectStylesIntoStyleTag_default()(balloonrotator/* default */.Z, balloonrotator_options);



/* harmony default export */ const panel_balloonrotator = (balloonrotator/* default.locals */.Z.locals || {});
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/fakepanel.css
var fakepanel = __webpack_require__(3553);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/fakepanel.css

            

var fakepanel_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

fakepanel_options.insert = "head";
fakepanel_options.singleton = true;

var fakepanel_update = injectStylesIntoStyleTag_default()(fakepanel/* default */.Z, fakepanel_options);



/* harmony default export */ const panel_fakepanel = (fakepanel/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/panel/balloon/contextualballoon
 */












const contextualballoon_toPx = toUnit('px');
/**
 * Provides the common contextual balloon for the editor.
 *
 * The role of this plugin is to unify the contextual balloons logic, simplify views management and help
 * avoid the unnecessary complexity of handling multiple {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
 * instances in the editor.
 *
 * This plugin allows for creating single or multiple panel stacks.
 *
 * Each stack may have multiple views, with the one on the top being visible. When the visible view is removed from the stack,
 * the previous view becomes visible.
 *
 * It might be useful to implement nested navigation in a balloon. For instance, a toolbar view may contain a link button.
 * 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
 * is displayed. When you finish editing the link and close (remove) the link view, the toolbar view is visible again.
 *
 * However, there are cases when there are multiple independent balloons to be displayed, for instance, if the selection
 * is inside two inline comments at the same time. For such cases, you can create two independent panel stacks.
 * The contextual balloon plugin will create a navigation bar to let the users switch between these panel stacks using the "Next"
 * and "Previous" buttons.
 *
 * If there are no views in the current stack, the balloon panel will try to switch to the next stack. If there are no
 * panels in any stack, the balloon panel will be hidden.
 *
 * **Note**: To force the balloon panel to show only one view, even if there are other stacks, use the `singleViewMode=true` option
 * when {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon#add adding} a view to a panel.
 *
 * From the implementation point of view, the contextual ballon plugin is reusing a single
 * {@link module:ui/panel/balloon/balloonpanelview~BalloonPanelView} instance to display multiple contextual balloon
 * panels in the editor. It also creates a special {@link module:ui/panel/balloon/contextualballoon~RotatorView rotator view},
 * used to manage multiple panel stacks. Rotator view is a child of the balloon panel view and the parent of the specific
 * view you want to display. If there is more than one panel stack to be displayed, the rotator view will add a
 * navigation bar. If there is only one stack, the rotator view is transparent (it does not add any UI elements).
 *
 * @extends module:core/plugin~Plugin
 */
class ContextualBalloon extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'ContextualBalloon';
    }
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        /**
         * The {@link module:utils/dom/position~Options#limiter position limiter}
         * for the {@link #view balloon}, used when no `limiter` has been passed into {@link #add}
         * or {@link #updatePosition}.
         *
         * By default, a function that obtains the farthest DOM
         * {@link module:engine/view/rooteditableelement~RootEditableElement}
         * of the {@link module:engine/view/document~Document#selection}.
         *
         * @member {module:utils/dom/position~Options#limiter} #positionLimiter
         */
        this.positionLimiter = () => {
            const view = this.editor.editing.view;
            const viewDocument = view.document;
            const editableElement = viewDocument.selection.editableElement;
            if (editableElement) {
                return view.domConverter.mapViewToDom(editableElement.root);
            }
            return null;
        };
        /**
         * The currently visible view or `null` when there are no views in any stack.
         *
         * @readonly
         * @observable
         * @member {module:ui/view~View|null} #visibleView
         */
        this.set('visibleView', null);
        /**
         * The common balloon panel view.
         *
         * @readonly
         * @member {module:ui/panel/balloon/balloonpanelview~BalloonPanelView} #view
         */
        this.view = new balloonpanelview_BalloonPanelView(editor.locale);
        editor.ui.view.body.add(this.view);
        editor.ui.focusTracker.add(this.view.element);
        /**
         * The map of views and their stacks.
         *
         * @private
         * @type {Map.<module:ui/view~View,Set>}
         */
        this._viewToStack = new Map();
        /**
         * The map of IDs and stacks.
         *
         * @private
         * @type {Map.<String,Set>}
         */
        this._idToStack = new Map();
        /**
         * A total number of all stacks in the balloon.
         *
         * @private
         * @readonly
         * @observable
         * @member {Number} #_numberOfStacks
         */
        this.set('_numberOfStacks', 0);
        /**
         * A flag that controls the single view mode.
         *
         * @private
         * @readonly
         * @observable
         * @member {Boolean} #_singleViewMode
         */
        this.set('_singleViewMode', false);
        /**
         * Rotator view embedded in the contextual balloon.
         * Displays the currently visible view in the balloon and provides navigation for switching stacks.
         *
         * @private
         * @type {module:ui/panel/balloon/contextualballoon~RotatorView}
         */
        this._rotatorView = this._createRotatorView();
        /**
         * Displays fake panels under the balloon panel view when multiple stacks are added to the balloon.
         *
         * @private
         * @type {module:ui/view~View}
         */
        this._fakePanelsView = this._createFakePanelsView();
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this.view.destroy();
        this._rotatorView.destroy();
        this._fakePanelsView.destroy();
    }
    /**
     * Returns `true` when the given view is in one of the stacks. Otherwise returns `false`.
     *
     * @param {module:ui/view~View} view
     * @returns {Boolean}
     */
    hasView(view) {
        return Array.from(this._viewToStack.keys()).includes(view);
    }
    /**
     * Adds a new view to the stack and makes it visible if the current stack is visible
     * or it is the first view in the balloon.
     *
     * @param {Object} data The configuration of the view.
     * @param {String} [data.stackId='main'] The ID of the stack that the view is added to.
     * @param {module:ui/view~View} [data.view] The content of the balloon.
     * @param {module:utils/dom/position~Options} [data.position] Positioning options.
     * @param {String} [data.balloonClassName] An additional CSS class added to the {@link #view balloon} when visible.
     * @param {Boolean} [data.withArrow=true] Whether the {@link #view balloon} should be rendered with an arrow.
     * @param {Boolean} [data.singleViewMode=false] Whether the view should be the only visible view even if other stacks were added.
     */
    add(data) {
        if (this.hasView(data.view)) {
            /**
             * Trying to add configuration of the same view more than once.
             *
             * @error contextualballoon-add-view-exist
             */
            throw new CKEditorError('contextualballoon-add-view-exist', [this, data]);
        }
        const stackId = data.stackId || 'main';
        // If new stack is added, creates it and show view from this stack.
        if (!this._idToStack.has(stackId)) {
            this._idToStack.set(stackId, new Map([[data.view, data]]));
            this._viewToStack.set(data.view, this._idToStack.get(stackId));
            this._numberOfStacks = this._idToStack.size;
            if (!this._visibleStack || data.singleViewMode) {
                this.showStack(stackId);
            }
            return;
        }
        const stack = this._idToStack.get(stackId);
        if (data.singleViewMode) {
            this.showStack(stackId);
        }
        // Add new view to the stack.
        stack.set(data.view, data);
        this._viewToStack.set(data.view, stack);
        // And display it if is added to the currently visible stack.
        if (stack === this._visibleStack) {
            this._showView(data);
        }
    }
    /**
     * Removes the given view from the stack. If the removed view was visible,
     * the view preceding it in the stack will become visible instead.
     * When there is no view in the stack, the next stack will be displayed.
     * When there are no more stacks, the balloon will hide.
     *
     * @param {module:ui/view~View} view A view to be removed from the balloon.
     */
    remove(view) {
        if (!this.hasView(view)) {
            /**
             * Trying to remove the configuration of the view not defined in the stack.
             *
             * @error contextualballoon-remove-view-not-exist
             */
            throw new CKEditorError('contextualballoon-remove-view-not-exist', [this, view]);
        }
        const stack = this._viewToStack.get(view);
        if (this._singleViewMode && this.visibleView === view) {
            this._singleViewMode = false;
        }
        // When visible view will be removed we need to show a preceding view or next stack
        // if a view is the only view in the stack.
        if (this.visibleView === view) {
            if (stack.size === 1) {
                if (this._idToStack.size > 1) {
                    this._showNextStack();
                }
                else {
                    this.view.hide();
                    this.visibleView = null;
                    this._rotatorView.hideView();
                }
            }
            else {
                this._showView(Array.from(stack.values())[stack.size - 2]);
            }
        }
        if (stack.size === 1) {
            this._idToStack.delete(this._getStackId(stack));
            this._numberOfStacks = this._idToStack.size;
        }
        else {
            stack.delete(view);
        }
        this._viewToStack.delete(view);
    }
    /**
     * Updates the position of the balloon using the position data of the first visible view in the stack.
     * When new position data is given, the position data of the currently visible view will be updated.
     *
     * @param {module:utils/dom/position~Options} [position] position options.
     */
    updatePosition(position) {
        if (position) {
            this._visibleStack.get(this.visibleView).position = position;
        }
        this.view.pin(this._getBalloonPosition());
        this._fakePanelsView.updatePosition();
    }
    /**
     * Shows the last view from the stack of a given ID.
     *
     * @param {String} id
     */
    showStack(id) {
        this.visibleStack = id;
        const stack = this._idToStack.get(id);
        if (!stack) {
            /**
             * Trying to show a stack that does not exist.
             *
             * @error contextualballoon-showstack-stack-not-exist
             */
            throw new CKEditorError('contextualballoon-showstack-stack-not-exist', this);
        }
        if (this._visibleStack === stack) {
            return;
        }
        this._showView(Array.from(stack.values()).pop());
    }
    /**
     * Returns the stack of the currently visible view.
     *
     * @private
     * @type {Set}
     */
    get _visibleStack() {
        return this._viewToStack.get(this.visibleView);
    }
    /**
     * Returns the ID of the given stack.
     *
     * @private
     * @param {Set} stack
     * @returns {String}
     */
    _getStackId(stack) {
        const entry = Array.from(this._idToStack.entries()).find(entry => entry[1] === stack);
        return entry[0];
    }
    /**
     * Shows the last view from the next stack.
     *
     * @private
     */
    _showNextStack() {
        const stacks = Array.from(this._idToStack.values());
        let nextIndex = stacks.indexOf(this._visibleStack) + 1;
        if (!stacks[nextIndex]) {
            nextIndex = 0;
        }
        this.showStack(this._getStackId(stacks[nextIndex]));
    }
    /**
     * Shows the last view from the previous stack.
     *
     * @private
     */
    _showPrevStack() {
        const stacks = Array.from(this._idToStack.values());
        let nextIndex = stacks.indexOf(this._visibleStack) - 1;
        if (!stacks[nextIndex]) {
            nextIndex = stacks.length - 1;
        }
        this.showStack(this._getStackId(stacks[nextIndex]));
    }
    /**
     * Creates a rotator view.
     *
     * @private
     * @returns {module:ui/panel/balloon/contextualballoon~RotatorView}
     */
    _createRotatorView() {
        const view = new RotatorView(this.editor.locale);
        const t = this.editor.locale.t;
        this.view.content.add(view);
        // Hide navigation when there is only a one stack & not in single view mode.
        view.bind('isNavigationVisible').to(this, '_numberOfStacks', this, '_singleViewMode', (value, isSingleViewMode) => {
            return !isSingleViewMode && value > 1;
        });
        // Update balloon position after toggling navigation.
        view.on('change:isNavigationVisible', () => (this.updatePosition()), { priority: 'low' });
        // Update stacks counter value.
        view.bind('counter').to(this, 'visibleView', this, '_numberOfStacks', (visibleView, numberOfStacks) => {
            if (numberOfStacks < 2) {
                return '';
            }
            const current = Array.from(this._idToStack.values()).indexOf(this._visibleStack) + 1;
            return t('%0 of %1', [current, numberOfStacks]);
        });
        view.buttonNextView.on('execute', () => {
            // When current view has a focus then move focus to the editable before removing it,
            // otherwise editor will lost focus.
            if (view.focusTracker.isFocused) {
                this.editor.editing.view.focus();
            }
            this._showNextStack();
        });
        view.buttonPrevView.on('execute', () => {
            // When current view has a focus then move focus to the editable before removing it,
            // otherwise editor will lost focus.
            if (view.focusTracker.isFocused) {
                this.editor.editing.view.focus();
            }
            this._showPrevStack();
        });
        return view;
    }
    /**
     * @private
     * @returns {module:ui/view~View}
     */
    _createFakePanelsView() {
        const view = new FakePanelsView(this.editor.locale, this.view);
        view.bind('numberOfPanels').to(this, '_numberOfStacks', this, '_singleViewMode', (number, isSingleViewMode) => {
            const showPanels = !isSingleViewMode && number >= 2;
            return showPanels ? Math.min(number - 1, 2) : 0;
        });
        view.listenTo(this.view, 'change:top', () => view.updatePosition());
        view.listenTo(this.view, 'change:left', () => view.updatePosition());
        this.editor.ui.view.body.add(view);
        return view;
    }
    /**
     * Sets the view as the content of the balloon and attaches the balloon using position
     * options of the first view.
     *
     * @private
     * @param {Object} data Configuration.
     * @param {module:ui/view~View} [data.view] The view to show in the balloon.
     * @param {String} [data.balloonClassName=''] Additional class name which will be added to the {@link #view balloon}.
     * @param {Boolean} [data.withArrow=true] Whether the {@link #view balloon} should be rendered with an arrow.
     */
    _showView({ view, balloonClassName = '', withArrow = true, singleViewMode = false }) {
        this.view.class = balloonClassName;
        this.view.withArrow = withArrow;
        this._rotatorView.showView(view);
        this.visibleView = view;
        this.view.pin(this._getBalloonPosition());
        this._fakePanelsView.updatePosition();
        if (singleViewMode) {
            this._singleViewMode = true;
        }
    }
    /**
     * Returns position options of the last view in the stack.
     * This keeps the balloon in the same position when the view is changed.
     *
     * @private
     * @returns {module:utils/dom/position~Options}
     */
    _getBalloonPosition() {
        let position = Array.from(this._visibleStack.values()).pop().position;
        if (position) {
            // Use the default limiter if none has been specified.
            if (!position.limiter) {
                // Don't modify the original options object.
                position = Object.assign({}, position, {
                    limiter: this.positionLimiter
                });
            }
            // Don't modify the original options object.
            position = Object.assign({}, position, {
                viewportOffsetConfig: this.editor.ui.viewportOffset
            });
        }
        return position;
    }
}
/**
 * Rotator view is a helper class for the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon ContextualBalloon}.
 * It is used for displaying the last view from the current stack and providing navigation buttons for switching stacks.
 * See the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon ContextualBalloon} documentation to learn more.
 *
 * @extends module:ui/view~View
 */
class RotatorView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const t = locale.t;
        const bind = this.bindTemplate;
        /**
         * Defines whether navigation is visible or not.
         *
         * @member {Boolean} #isNavigationVisible
         */
        this.set('isNavigationVisible', true);
        /**
         * Used for checking if a view is focused or not.
         *
         * @type {module:utils/focustracker~FocusTracker}
         */
        this.focusTracker = new FocusTracker();
        /**
         * Navigation button for switching the stack to the previous one.
         *
         * @type {module:ui/button/buttonview~ButtonView}
         */
        this.buttonPrevView = this._createButtonView(t('Previous'), previous_arrow);
        /**
         * Navigation button for switching the stack to the next one.
         *
         * @type {module:ui/button/buttonview~ButtonView}
         */
        this.buttonNextView = this._createButtonView(t('Next'), next_arrow);
        /**
         * A collection of the child views that creates the rotator content.
         *
         * @readonly
         * @type {module:ui/viewcollection~ViewCollection}
         */
        this.content = this.createCollection();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-balloon-rotator'
                ],
                'z-index': '-1'
            },
            children: [
                {
                    tag: 'div',
                    attributes: {
                        class: [
                            'ck-balloon-rotator__navigation',
                            bind.to('isNavigationVisible', value => value ? '' : 'ck-hidden')
                        ]
                    },
                    children: [
                        this.buttonPrevView,
                        {
                            tag: 'span',
                            attributes: {
                                class: [
                                    'ck-balloon-rotator__counter'
                                ]
                            },
                            children: [
                                {
                                    text: bind.to('counter')
                                }
                            ]
                        },
                        this.buttonNextView
                    ]
                },
                {
                    tag: 'div',
                    attributes: {
                        class: 'ck-balloon-rotator__content'
                    },
                    children: this.content
                }
            ]
        });
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        this.focusTracker.add(this.element);
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this.focusTracker.destroy();
    }
    /**
     * Shows a given view.
     *
     * @param {module:ui/view~View} view The view to show.
     */
    showView(view) {
        this.hideView();
        this.content.add(view);
    }
    /**
     * Hides the currently displayed view.
     */
    hideView() {
        this.content.clear();
    }
    /**
     * Creates a navigation button view.
     *
     * @private
     * @param {String} label The button label.
     * @param {String} icon The button icon.
     * @returns {module:ui/button/buttonview~ButtonView}
     */
    _createButtonView(label, icon) {
        const view = new buttonview_ButtonView(this.locale);
        view.set({
            label,
            icon,
            tooltip: true
        });
        return view;
    }
}
// Displays additional layers under the balloon when multiple stacks are added to the balloon.
//
// @private
// @extends module:ui/view~View
class FakePanelsView extends src_view_View {
    // @inheritDoc
    constructor(locale, balloonPanelView) {
        super(locale);
        const bind = this.bindTemplate;
        // Fake panels top offset.
        //
        // @observable
        // @member {Number} #top
        this.set('top', 0);
        // Fake panels left offset.
        //
        // @observable
        // @member {Number} #left
        this.set('left', 0);
        // Fake panels height.
        //
        // @observable
        // @member {Number} #height
        this.set('height', 0);
        // Fake panels width.
        //
        // @observable
        // @member {Number} #width
        this.set('width', 0);
        // Number of rendered fake panels.
        //
        // @observable
        // @member {Number} #numberOfPanels
        this.set('numberOfPanels', 0);
        // Collection of the child views which creates fake panel content.
        //
        // @readonly
        // @type {module:ui/viewcollection~ViewCollection}
        this.content = this.createCollection();
        // Context.
        //
        // @private
        // @type {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
        this._balloonPanelView = balloonPanelView;
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck-fake-panel',
                    bind.to('numberOfPanels', number => number ? '' : 'ck-hidden')
                ],
                style: {
                    top: bind.to('top', contextualballoon_toPx),
                    left: bind.to('left', contextualballoon_toPx),
                    width: bind.to('width', contextualballoon_toPx),
                    height: bind.to('height', contextualballoon_toPx)
                }
            },
            children: this.content
        });
        this.on('change:numberOfPanels', (evt, name, next, prev) => {
            if (next > prev) {
                this._addPanels(next - prev);
            }
            else {
                this._removePanels(prev - next);
            }
            this.updatePosition();
        });
    }
    // @private
    // @param {Number} number
    _addPanels(number) {
        while (number--) {
            const view = new src_view_View();
            view.setTemplate({ tag: 'div' });
            this.content.add(view);
            this.registerChild(view);
        }
    }
    // @private
    // @param {Number} number
    _removePanels(number) {
        while (number--) {
            const view = this.content.last;
            this.content.remove(view);
            this.deregisterChild(view);
            view.destroy();
        }
    }
    // Updates coordinates of fake panels.
    updatePosition() {
        if (this.numberOfPanels) {
            const { top, left } = this._balloonPanelView;
            const { width, height } = new rect_Rect(this._balloonPanelView.element);
            Object.assign(this, { top, left, width, height });
        }
    }
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/stickypanel.css
var stickypanel = __webpack_require__(3609);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/panel/stickypanel.css

            

var stickypanel_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

stickypanel_options.insert = "head";
stickypanel_options.singleton = true;

var stickypanel_update = injectStylesIntoStyleTag_default()(stickypanel/* default */.Z, stickypanel_options);



/* harmony default export */ const panel_stickypanel = (stickypanel/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/panel/sticky/stickypanelview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/panel/sticky/stickypanelview
 */





const stickypanelview_toPx = toUnit('px');
/**
 * The sticky panel view class.
 */
class StickyPanelView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        /**
         * Controls whether the sticky panel should be active.
         *
         * @readonly
         * @observable
         * @member {Boolean} #isActive
         */
        this.set('isActive', false);
        /**
         * Controls whether the sticky panel is in the "sticky" state.
         *
         * @readonly
         * @observable
         * @member {Boolean} #isSticky
         */
        this.set('isSticky', false);
        /**
         * The limiter element for the sticky panel instance. Its bounding rect limits
         * the "stickyness" of the panel, i.e. when the panel reaches the bottom
         * edge of the limiter, it becomes sticky to that edge and does not float
         * off the limiter. It is mandatory for the panel to work properly and once
         * set, it cannot be changed.
         *
         * @readonly
         * @observable
         * @member {HTMLElement} #limiterElement
         */
        this.set('limiterElement', null);
        /**
         * The offset from the bottom edge of {@link #limiterElement}
         * which stops the panel from stickying any further to prevent limiter's content
         * from being completely covered.
         *
         * @readonly
         * @observable
         * @default 50
         * @member {Number} #limiterBottomOffset
         */
        this.set('limiterBottomOffset', 50);
        /**
         * The offset from the top edge of the web browser's viewport which makes the
         * panel become sticky. The default value is `0`, which means the panel becomes
         * sticky when it's upper edge touches the top of the page viewport.
         *
         * This attribute is useful when the web page has UI elements positioned to the top
         * either using `position: fixed` or `position: sticky`, which would cover the
         * sticky panel or vice–versa (depending on the `z-index` hierarchy).
         *
         * Bound to {@link module:core/editor/editorui~EditorUI#viewportOffset `EditorUI#viewportOffset`}.
         *
         * If {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset.top`} is defined, then
         * it will override the default value.
         *
         * @observable
         * @default 0
         * @member {Number} #viewportTopOffset
         */
        this.set('viewportTopOffset', 0);
        /**
         * Controls the `margin-left` CSS style of the panel.
         *
         * @protected
         * @readonly
         * @observable
         * @member {String} #_marginLeft
         */
        this.set('_marginLeft', null);
        /**
         * Set `true` if the sticky panel reached the bottom edge of the
         * {@link #limiterElement}.
         *
         * @protected
         * @readonly
         * @observable
         * @member {Boolean} #_isStickyToTheLimiter
         */
        this.set('_isStickyToTheLimiter', false);
        /**
         * Set `true` if the sticky panel uses the {@link #viewportTopOffset},
         * i.e. not {@link #_isStickyToTheLimiter} and the {@link #viewportTopOffset}
         * is not `0`.
         *
         * @protected
         * @readonly
         * @observable
         * @member {Boolean} #_hasViewportTopOffset
         */
        this.set('_hasViewportTopOffset', false);
        /**
         * Collection of the child views which creates balloon panel contents.
         *
         * @readonly
         * @member {module:ui/viewcollection~ViewCollection}
         */
        this.content = this.createCollection();
        /**
         * The DOM bounding client rect of the {@link module:ui/view~View#element} of the panel.
         *
         * @protected
         * @member {Object} #_panelRect
         */
        /**
         * The DOM bounding client rect of the {@link #limiterElement}
         * of the panel.
         *
         * @protected
         * @member {Object} #_limiterRect
         */
        /**
         * A dummy element which visually fills the space as long as the
         * actual panel is sticky. It prevents flickering of the UI.
         *
         * @protected
         * @property {HTMLElement}
         */
        this._contentPanelPlaceholder = new Template({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-sticky-panel__placeholder'
                ],
                style: {
                    display: bind.to('isSticky', isSticky => isSticky ? 'block' : 'none'),
                    height: bind.to('isSticky', isSticky => {
                        return isSticky ? stickypanelview_toPx(this._panelRect.height) : null;
                    })
                }
            }
        }).render();
        /**
         * The panel which accepts children into {@link #content} collection.
         * Also an element which is positioned when {@link #isSticky}.
         *
         * @protected
         * @property {HTMLElement}
         */
        this._contentPanel = new Template({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-sticky-panel__content',
                    // Toggle class of the panel when "sticky" state changes in the view.
                    bind.if('isSticky', 'ck-sticky-panel__content_sticky'),
                    bind.if('_isStickyToTheLimiter', 'ck-sticky-panel__content_sticky_bottom-limit')
                ],
                style: {
                    width: bind.to('isSticky', isSticky => {
                        return isSticky ? stickypanelview_toPx(this._contentPanelPlaceholder.getBoundingClientRect().width) : null;
                    }),
                    top: bind.to('_hasViewportTopOffset', _hasViewportTopOffset => {
                        return _hasViewportTopOffset ? stickypanelview_toPx(this.viewportTopOffset) : null;
                    }),
                    bottom: bind.to('_isStickyToTheLimiter', _isStickyToTheLimiter => {
                        return _isStickyToTheLimiter ? stickypanelview_toPx(this.limiterBottomOffset) : null;
                    }),
                    marginLeft: bind.to('_marginLeft')
                }
            },
            children: this.content
        }).render();
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-sticky-panel'
                ]
            },
            children: [
                this._contentPanelPlaceholder,
                this._contentPanel
            ]
        });
    }
    /**
     * @inheritDoc
     */
    render() {
        super.render();
        // Check if the panel should go into the sticky state immediately.
        this._checkIfShouldBeSticky();
        // Update sticky state of the panel as the window is being scrolled.
        this.listenTo(dom_global.window, 'scroll', () => {
            this._checkIfShouldBeSticky();
        });
        // Synchronize with `model.isActive` because sticking an inactive panel is pointless.
        this.listenTo(this, 'change:isActive', () => {
            this._checkIfShouldBeSticky();
        });
    }
    /**
     * Analyzes the environment to decide whether the panel should
     * be sticky or not.
     *
     * @protected
     */
    _checkIfShouldBeSticky() {
        const panelRect = this._panelRect = this._contentPanel.getBoundingClientRect();
        let limiterRect;
        if (!this.limiterElement) {
            this.isSticky = false;
        }
        else {
            limiterRect = this._limiterRect = this.limiterElement.getBoundingClientRect();
            // The panel must be active to become sticky.
            this.isSticky = this.isActive &&
                // The limiter's top edge must be beyond the upper edge of the visible viewport (+the viewportTopOffset).
                limiterRect.top < this.viewportTopOffset &&
                // The model#limiterElement's height mustn't be smaller than the panel's height and model#limiterBottomOffset.
                // There's no point in entering the sticky mode if the model#limiterElement is very, very small, because
                // it would immediately set model#_isStickyToTheLimiter true and, given model#limiterBottomOffset, the panel
                // would be positioned before the model#limiterElement.
                this._panelRect.height + this.limiterBottomOffset < limiterRect.height;
        }
        // Stick the panel to the top edge of the viewport simulating CSS position:sticky.
        // TODO: Possibly replaced by CSS in the future http://caniuse.com/#feat=css-sticky
        if (this.isSticky) {
            this._isStickyToTheLimiter =
                limiterRect.bottom < panelRect.height + this.limiterBottomOffset + this.viewportTopOffset;
            this._hasViewportTopOffset = !this._isStickyToTheLimiter && !!this.viewportTopOffset;
            this._marginLeft = this._isStickyToTheLimiter ? null : stickypanelview_toPx(-dom_global.window.scrollX);
        }
        // Detach the panel from the top edge of the viewport.
        else {
            this._isStickyToTheLimiter = false;
            this._hasViewportTopOffset = false;
            this._marginLeft = null;
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/toolbar/balloon/balloontoolbar.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/toolbar/balloon/balloontoolbar
 */











const balloontoolbar_toPx = toUnit('px');
/**
 * The contextual toolbar.
 *
 * It uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
 *
 * @extends module:core/plugin~Plugin
 */
class BalloonToolbar extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'BalloonToolbar';
    }
    /**
     * @inheritDoc
     */
    static get requires() {
        return [ContextualBalloon];
    }
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        /**
         * A cached and normalized `config.balloonToolbar` object.
         *
         * @type {module:core/editor/editorconfig~EditorConfig#balloonToolbar}
         * @private
         */
        this._balloonConfig = normalizetoolbarconfig_normalizeToolbarConfig(editor.config.get('balloonToolbar'));
        /**
         * The toolbar view displayed in the balloon.
         *
         * @type {module:ui/toolbar/toolbarview~ToolbarView}
         */
        this.toolbarView = this._createToolbarView();
        /**
         * Tracks the focus of the {@link module:core/editor/editorui~EditorUI#getEditableElement editable element}
         * and the {@link #toolbarView}. When both are blurred then the toolbar should hide.
         *
         * @readonly
         * @type {module:utils:focustracker~FocusTracker}
         */
        this.focusTracker = new FocusTracker();
        // Wait for the EditorUI#init. EditableElement is not available before.
        editor.ui.once('ready', () => {
            this.focusTracker.add(editor.ui.getEditableElement());
            this.focusTracker.add(this.toolbarView.element);
        });
        // Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
        editor.ui.addToolbar(this.toolbarView, {
            beforeFocus: () => this.show(true),
            afterBlur: () => this.hide(),
            isContextual: true
        });
        /**
         * An instance of the resize observer that allows to respond to changes in editable's geometry
         * so the toolbar can stay within its boundaries (and group toolbar items that do not fit).
         *
         * **Note**: Used only when `shouldNotGroupWhenFull` was **not** set in the
         * {@link module:core/editor/editorconfig~EditorConfig#balloonToolbar configuration}.
         *
         * **Note:** Created in {@link #init}.
         *
         * @protected
         * @member {module:utils/dom/resizeobserver~ResizeObserver}
         */
        this._resizeObserver = null;
        /**
         * The contextual balloon plugin instance.
         *
         * @private
         * @type {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
         */
        this._balloon = editor.plugins.get(ContextualBalloon);
        /**
         * Fires {@link #event:_selectionChangeDebounced} event using `lodash#debounce`.
         *
         * This function is stored as a plugin property to make possible to cancel
         * trailing debounced invocation on destroy.
         *
         * @private
         * @type {Function}
         */
        this._fireSelectionChangeDebounced = lodash_es_debounce(() => this.fire('_selectionChangeDebounced'), 200);
        // The appearance of the BalloonToolbar method is event–driven.
        // It is possible to stop the #show event and this prevent the toolbar from showing up.
        this.decorate('show');
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const selection = editor.model.document.selection;
        // Show/hide the toolbar on editable focus/blur.
        this.listenTo(this.focusTracker, 'change:isFocused', (evt, name, isFocused) => {
            const isToolbarVisible = this._balloon.visibleView === this.toolbarView;
            if (!isFocused && isToolbarVisible) {
                this.hide();
            }
            else if (isFocused) {
                this.show();
            }
        });
        // Hide the toolbar when the selection is changed by a direct change or has changed to collapsed.
        this.listenTo(selection, 'change:range', (evt, data) => {
            if (data.directChange || selection.isCollapsed) {
                this.hide();
            }
            // Fire internal `_selectionChangeDebounced` event to use it for showing
            // the toolbar after the selection stops changing.
            this._fireSelectionChangeDebounced();
        });
        // Show the toolbar when the selection stops changing.
        this.listenTo(this, '_selectionChangeDebounced', () => {
            if (this.editor.editing.view.document.isFocused) {
                this.show();
            }
        });
        if (!this._balloonConfig.shouldNotGroupWhenFull) {
            this.listenTo(editor, 'ready', () => {
                const editableElement = editor.ui.view.editable.element;
                // Set #toolbarView's max-width on the initialization and update it on the editable resize.
                this._resizeObserver = new resizeobserver_ResizeObserver(editableElement, () => {
                    // The max-width equals 90% of the editable's width for the best user experience.
                    // The value keeps the balloon very close to the boundaries of the editable and limits the cases
                    // when the balloon juts out from the editable element it belongs to.
                    this.toolbarView.maxWidth = balloontoolbar_toPx(new rect_Rect(editableElement).width * .9);
                });
            });
        }
        // Listen to the toolbar view and whenever it changes its geometry due to some items being
        // grouped or ungrouped, update the position of the balloon because a shorter/longer toolbar
        // means the balloon could be pointing at the wrong place. Once updated, the balloon will point
        // at the right selection in the content again.
        // https://github.com/ckeditor/ckeditor5/issues/6444
        this.listenTo(this.toolbarView, 'groupedItemsUpdate', () => {
            this._updatePosition();
        });
    }
    /**
     * Creates toolbar components based on given configuration.
     * This needs to be done when all plugins are ready.
     *
     * @inheritDoc
     */
    afterInit() {
        const factory = this.editor.ui.componentFactory;
        this.toolbarView.fillFromConfig(this._balloonConfig, factory);
    }
    /**
     * Creates the toolbar view instance.
     *
     * @private
     * @returns {module:ui/toolbar/toolbarview~ToolbarView}
     */
    _createToolbarView() {
        const t = this.editor.locale.t;
        const shouldGroupWhenFull = !this._balloonConfig.shouldNotGroupWhenFull;
        const toolbarView = new toolbarview_ToolbarView(this.editor.locale, {
            shouldGroupWhenFull,
            isFloating: true
        });
        toolbarView.ariaLabel = t('Editor contextual toolbar');
        toolbarView.render();
        return toolbarView;
    }
    /**
     * Shows the toolbar and attaches it to the selection.
     *
     * Fires {@link #event:show} event which can be stopped to prevent the toolbar from showing up.
     *
     * @param {Boolean} [showForCollapsedSelection=false] When set `true`, the toolbar will show despite collapsed selection in the
     * editing view.
     */
    show(showForCollapsedSelection = false) {
        const editor = this.editor;
        const selection = editor.model.document.selection;
        const schema = editor.model.schema;
        // Do not add the toolbar to the balloon stack twice.
        if (this._balloon.hasView(this.toolbarView)) {
            return;
        }
        // Do not show the toolbar when the selection is collapsed.
        if (selection.isCollapsed && !showForCollapsedSelection) {
            return;
        }
        // Do not show the toolbar when there is more than one range in the selection and they fully contain selectable elements.
        // See https://github.com/ckeditor/ckeditor5/issues/6443.
        if (selectionContainsOnlyMultipleSelectables(selection, schema)) {
            return;
        }
        // Don not show the toolbar when all components inside are disabled
        // see https://github.com/ckeditor/ckeditor5-ui/issues/269.
        if (Array.from(this.toolbarView.items).every((item) => item.isEnabled !== undefined && !item.isEnabled)) {
            return;
        }
        // Update the toolbar position when the editor ui should be refreshed.
        this.listenTo(this.editor.ui, 'update', () => {
            this._updatePosition();
        });
        // Add the toolbar to the common editor contextual balloon.
        this._balloon.add({
            view: this.toolbarView,
            position: this._getBalloonPositionData(),
            balloonClassName: 'ck-toolbar-container'
        });
    }
    /**
     * Hides the toolbar.
     */
    hide() {
        if (this._balloon.hasView(this.toolbarView)) {
            this.stopListening(this.editor.ui, 'update');
            this._balloon.remove(this.toolbarView);
        }
    }
    /**
     * Returns positioning options for the {@link #_balloon}. They control the way balloon is attached
     * to the selection.
     *
     * @private
     * @returns {module:utils/dom/position~Options}
     */
    _getBalloonPositionData() {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        const viewSelection = viewDocument.selection;
        // Get direction of the selection.
        const isBackward = viewDocument.selection.isBackward;
        return {
            // Because the target for BalloonPanelView is a Rect (not DOMRange), it's geometry will stay fixed
            // as the window scrolls. To let the BalloonPanelView follow such Rect, is must be continuously
            // computed and hence, the target is defined as a function instead of a static value.
            // https://github.com/ckeditor/ckeditor5-ui/issues/195
            target: () => {
                const range = isBackward ? viewSelection.getFirstRange() : viewSelection.getLastRange();
                const rangeRects = rect_Rect.getDomRangeRects(view.domConverter.viewRangeToDom(range));
                // Select the proper range rect depending on the direction of the selection.
                if (isBackward) {
                    return rangeRects[0];
                }
                else {
                    // Ditch the zero-width "orphan" rect in the next line for the forward selection if there's
                    // another one preceding it. It is not rendered as a selection by the web browser anyway.
                    // https://github.com/ckeditor/ckeditor5-ui/issues/308
                    if (rangeRects.length > 1 && rangeRects[rangeRects.length - 1].width === 0) {
                        rangeRects.pop();
                    }
                    return rangeRects[rangeRects.length - 1];
                }
            },
            positions: this._getBalloonPositions(isBackward)
        };
    }
    /**
     * Updates the position of the {@link #_balloon} to make up for changes:
     *
     * * in the geometry of the selection it is attached to (e.g. the selection moved in the viewport or expanded or shrunk),
     * * or the geometry of the balloon toolbar itself (e.g. the toolbar has grouped or ungrouped some items and it is shorter or longer).
     *
     * @private
     */
    _updatePosition() {
        this._balloon.updatePosition(this._getBalloonPositionData());
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this.stopListening();
        this._fireSelectionChangeDebounced.cancel();
        this.toolbarView.destroy();
        this.focusTracker.destroy();
        if (this._resizeObserver) {
            this._resizeObserver.destroy();
        }
    }
    /**
     * This event is fired just before the toolbar shows up. Stopping this event will prevent this.
     *
     * @event show
     */
    /**
     * This is internal plugin event which is fired 200 ms after model selection last change.
     * This is to makes easy test debounced action without need to use `setTimeout`.
     *
     * @protected
     * @event _selectionChangeDebounced
     */
    /**
     * Returns toolbar positions for the given direction of the selection.
     *
     * @private
     * @param {Boolean} isBackward
     * @returns {Array.<module:utils/dom/position~Position>}
     */
    _getBalloonPositions(isBackward) {
        const isSafariIniOS = src_env.isSafari && src_env.isiOS;
        // https://github.com/ckeditor/ckeditor5/issues/7707
        const positions = isSafariIniOS ? generatePositions({
            // 20px when zoomed out. Less then 20px when zoomed in; the "radius" of the native selection handle gets
            // smaller as the user zooms in. No less than the default v-offset, though.
            heightOffset: Math.max(balloonpanelview_BalloonPanelView.arrowHeightOffset, Math.round(20 / dom_global.window.visualViewport.scale))
        }) : balloonpanelview_BalloonPanelView.defaultPositions;
        return isBackward ? [
            positions.northWestArrowSouth,
            positions.northWestArrowSouthWest,
            positions.northWestArrowSouthEast,
            positions.northWestArrowSouthMiddleEast,
            positions.northWestArrowSouthMiddleWest,
            positions.southWestArrowNorth,
            positions.southWestArrowNorthWest,
            positions.southWestArrowNorthEast,
            positions.southWestArrowNorthMiddleWest,
            positions.southWestArrowNorthMiddleEast
        ] : [
            positions.southEastArrowNorth,
            positions.southEastArrowNorthEast,
            positions.southEastArrowNorthWest,
            positions.southEastArrowNorthMiddleEast,
            positions.southEastArrowNorthMiddleWest,
            positions.northEastArrowSouth,
            positions.northEastArrowSouthEast,
            positions.northEastArrowSouthWest,
            positions.northEastArrowSouthMiddleEast,
            positions.northEastArrowSouthMiddleWest
        ];
    }
}
// Returns "true" when the selection has multiple ranges and each range contains a selectable element
// and nothing else.
//
// @private
// @param {module:engine/model/selection~Selection} selection
// @param {module:engine/model/schema~Schema} schema
// @returns {Boolean}
function selectionContainsOnlyMultipleSelectables(selection, schema) {
    // It doesn't contain multiple objects if there is only one range.
    if (selection.rangeCount === 1) {
        return false;
    }
    return [...selection.getRanges()].every(range => {
        const element = range.getContainedElement();
        return element && schema.isSelectable(element);
    });
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/toolbar/blocktoolbar.css
var blocktoolbar = __webpack_require__(6706);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/toolbar/blocktoolbar.css

            

var blocktoolbar_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

blocktoolbar_options.insert = "head";
blocktoolbar_options.singleton = true;

var blocktoolbar_update = injectStylesIntoStyleTag_default()(blocktoolbar/* default */.Z, blocktoolbar_options);



/* harmony default export */ const toolbar_blocktoolbar = (blocktoolbar/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/toolbar/block/blockbuttonview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/toolbar/block/blockbuttonview
 */



const blockbuttonview_toPx = toUnit('px');
/**
 * The block button view class.
 *
 * This view represents a button attached next to block element where the selection is anchored.
 *
 * See {@link module:ui/toolbar/block/blocktoolbar~BlockToolbar}.
 *
 * @extends {module:ui/button/buttonview~ButtonView}
 */
class blockbuttonview_BlockButtonView extends (/* unused pure expression or super */ null && (ButtonView)) {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        const bind = this.bindTemplate;
        // Hide button on init.
        this.isVisible = false;
        this.isToggleable = true;
        /**
         * Top offset.
         *
         * @member {Number} #top
         */
        this.set('top', 0);
        /**
         * Left offset.
         *
         * @member {Number} #left
         */
        this.set('left', 0);
        this.extendTemplate({
            attributes: {
                class: 'ck-block-toolbar-button',
                style: {
                    top: bind.to('top', val => blockbuttonview_toPx(val)),
                    left: bind.to('left', val => blockbuttonview_toPx(val))
                }
            }
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/toolbar/block/blocktoolbar.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui/toolbar/block/blocktoolbar
 */
/* global window */












const blocktoolbar_toPx = toUnit('px');
/**
 * The block toolbar plugin.
 *
 * This plugin provides a button positioned next to the block of content where the selection is anchored.
 * Upon clicking the button, a dropdown providing access to editor features shows up, as configured in
 * {@link module:core/editor/editorconfig~EditorConfig#blockToolbar}.
 *
 * By default, the button is displayed next to all elements marked in {@link module:engine/model/schema~Schema}
 * as `$block` for which the toolbar provides at least one option.
 *
 * By default, the button is attached so its right boundary is touching the
 * {@link module:engine/view/editableelement~EditableElement}:
 *
 * 		 __ |
 * 		|  ||  This is a block of content that the
 * 		 ¯¯ |  button is attached to. This is a
 * 		    |  block of content that the button is
 * 		    |  attached to.
 *
 * The position of the button can be adjusted using the CSS `transform` property:
 *
 * 		.ck-block-toolbar-button {
 * 			transform: translateX( -10px );
 * 		}
 *
 * 		 __   |
 * 		|  |  |  This is a block of content that the
 * 		 ¯¯   |  button is attached to. This is a
 * 		      |  block of content that the button is
 * 		      |  attached to.
 *
 * **Note**: If you plan to run the editor in a right–to–left (RTL) language, keep in mind the button
 * will be attached to the **right** boundary of the editable area. In that case, make sure the
 * CSS position adjustment works properly by adding the following styles:
 *
 * 		.ck[dir="rtl"] .ck-block-toolbar-button {
 * 			transform: translateX( 10px );
 * 		}
 *
 * @extends module:core/plugin~Plugin
 */
class BlockToolbar extends (/* unused pure expression or super */ null && (Plugin)) {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'BlockToolbar';
    }
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        /**
         * A cached and normalized `config.blockToolbar` object.
         *
         * @type {module:core/editor/editorconfig~EditorConfig#blockToolbar}
         * @private
         */
        this._blockToolbarConfig = normalizeToolbarConfig(this.editor.config.get('blockToolbar'));
        /**
         * The toolbar view.
         *
         * @type {module:ui/toolbar/toolbarview~ToolbarView}
         */
        this.toolbarView = this._createToolbarView();
        /**
         * The balloon panel view, containing the {@link #toolbarView}.
         *
         * @type {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
         */
        this.panelView = this._createPanelView();
        /**
         * The button view that opens the {@link #toolbarView}.
         *
         * @type {module:ui/toolbar/block/blockbuttonview~BlockButtonView}
         */
        this.buttonView = this._createButtonView();
        /**
         * An instance of the resize observer that allows to respond to changes in editable's geometry
         * so the toolbar can stay within its boundaries (and group toolbar items that do not fit).
         *
         * **Note**: Used only when `shouldNotGroupWhenFull` was **not** set in the
         * {@link module:core/editor/editorconfig~EditorConfig#blockToolbar configuration}.
         *
         * **Note:** Created in {@link #afterInit}.
         *
         * @protected
         * @member {module:utils/dom/resizeobserver~ResizeObserver}
         */
        this._resizeObserver = null;
        // Close the #panelView upon clicking outside of the plugin UI.
        clickOutsideHandler({
            emitter: this.panelView,
            contextElements: [this.panelView.element, this.buttonView.element],
            activator: () => this.panelView.isVisible,
            callback: () => this._hidePanel()
        });
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        // Hides panel on a direct selection change.
        this.listenTo(editor.model.document.selection, 'change:range', (evt, data) => {
            if (data.directChange) {
                this._hidePanel();
            }
        });
        this.listenTo(editor.ui, 'update', () => this._updateButton());
        // `low` priority is used because of https://github.com/ckeditor/ckeditor5-core/issues/133.
        this.listenTo(editor, 'change:isReadOnly', () => this._updateButton(), { priority: 'low' });
        this.listenTo(editor.ui.focusTracker, 'change:isFocused', () => this._updateButton());
        // Reposition button on resize.
        this.listenTo(this.buttonView, 'change:isVisible', (evt, name, isVisible) => {
            if (isVisible) {
                // Keep correct position of button and panel on window#resize.
                this.buttonView.listenTo(window, 'resize', () => this._updateButton());
            }
            else {
                // Stop repositioning button when is hidden.
                this.buttonView.stopListening(window, 'resize');
                // Hide the panel when the button disappears.
                this._hidePanel();
            }
        });
        // Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
        editor.ui.addToolbar(this.toolbarView, {
            beforeFocus: () => this._showPanel(),
            afterBlur: () => this._hidePanel()
        });
    }
    /**
     * Fills the toolbar with its items based on the configuration.
     *
     * **Note:** This needs to be done after all plugins are ready.
     *
     * @inheritDoc
     */
    afterInit() {
        const factory = this.editor.ui.componentFactory;
        const config = this._blockToolbarConfig;
        this.toolbarView.fillFromConfig(config, factory);
        // Hide panel before executing each button in the panel.
        for (const item of this.toolbarView.items) {
            item.on('execute', () => this._hidePanel(true), { priority: 'high' });
        }
        if (!config.shouldNotGroupWhenFull) {
            this.listenTo(this.editor, 'ready', () => {
                const editableElement = this.editor.ui.view.editable.element;
                // Set #toolbarView's max-width just after the initialization and update it on the editable resize.
                this._resizeObserver = new ResizeObserver(editableElement, () => {
                    this.toolbarView.maxWidth = this._getToolbarMaxWidth();
                });
            });
        }
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        // Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
        this.panelView.destroy();
        this.buttonView.destroy();
        this.toolbarView.destroy();
        if (this._resizeObserver) {
            this._resizeObserver.destroy();
        }
    }
    /**
     * Creates the {@link #toolbarView}.
     *
     * @private
     * @returns {module:ui/toolbar/toolbarview~ToolbarView}
     */
    _createToolbarView() {
        const t = this.editor.locale.t;
        const shouldGroupWhenFull = !this._blockToolbarConfig.shouldNotGroupWhenFull;
        const toolbarView = new ToolbarView(this.editor.locale, {
            shouldGroupWhenFull,
            isFloating: true
        });
        toolbarView.ariaLabel = t('Editor block content toolbar');
        // When toolbar lost focus then panel should hide.
        toolbarView.focusTracker.on('change:isFocused', (evt, name, is) => {
            if (!is) {
                this._hidePanel();
            }
        });
        return toolbarView;
    }
    /**
     * Creates the {@link #panelView}.
     *
     * @private
     * @returns {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
     */
    _createPanelView() {
        const editor = this.editor;
        const panelView = new BalloonPanelView(editor.locale);
        panelView.content.add(this.toolbarView);
        panelView.class = 'ck-toolbar-container';
        editor.ui.view.body.add(panelView);
        editor.ui.focusTracker.add(panelView.element);
        // Close #panelView on `Esc` press.
        this.toolbarView.keystrokes.set('Esc', (evt, cancel) => {
            this._hidePanel(true);
            cancel();
        });
        return panelView;
    }
    /**
     * Creates the {@link #buttonView}.
     *
     * @private
     * @returns {module:ui/toolbar/block/blockbuttonview~BlockButtonView}
     */
    _createButtonView() {
        const editor = this.editor;
        const t = editor.t;
        const buttonView = new BlockButtonView(editor.locale);
        const bind = buttonView.bindTemplate;
        buttonView.set({
            label: t('Edit block'),
            icon: pilcrow,
            withText: false
        });
        // Note that this piece over here overrides the default mousedown logic in ButtonView
        // to make it work with BlockToolbar. See the implementation of the ButtonView class to learn more.
        buttonView.extendTemplate({
            on: {
                mousedown: bind.to(evt => {
                    // On Safari we have to force the focus on a button on click as it's the only browser
                    // that doesn't do that automatically. See #12115.
                    if (env.isSafari && this.panelView.isVisible) {
                        this.toolbarView.focus();
                    }
                    // Workaround to #12184, see https://github.com/ckeditor/ckeditor5/issues/12184#issuecomment-1199147964.
                    evt.preventDefault();
                })
            }
        });
        // Bind the panelView observable properties to the buttonView.
        buttonView.bind('isOn').to(this.panelView, 'isVisible');
        buttonView.bind('tooltip').to(this.panelView, 'isVisible', isVisible => !isVisible);
        // Toggle the panelView upon buttonView#execute.
        this.listenTo(buttonView, 'execute', () => {
            if (!this.panelView.isVisible) {
                this._showPanel();
            }
            else {
                this._hidePanel(true);
            }
        });
        editor.ui.view.body.add(buttonView);
        editor.ui.focusTracker.add(buttonView.element);
        return buttonView;
    }
    /**
     * Shows or hides the button.
     * When all the conditions for displaying the button are matched, it shows the button. Hides otherwise.
     *
     * @private
     */
    _updateButton() {
        const editor = this.editor;
        const model = editor.model;
        const view = editor.editing.view;
        // Hides the button when the editor is not focused.
        if (!editor.ui.focusTracker.isFocused) {
            this._hideButton();
            return;
        }
        // Hides the button when the editor switches to the read-only mode.
        if (editor.isReadOnly) {
            this._hideButton();
            return;
        }
        // Get the first selected block, button will be attached to this element.
        const modelTarget = Array.from(model.document.selection.getSelectedBlocks())[0];
        // Hides the button when there is no enabled item in toolbar for the current block element.
        if (!modelTarget || Array.from(this.toolbarView.items).every((item) => !item.isEnabled)) {
            this._hideButton();
            return;
        }
        // Get DOM target element.
        const domTarget = view.domConverter.mapViewToDom(editor.editing.mapper.toViewElement(modelTarget));
        // Show block button.
        this.buttonView.isVisible = true;
        // Attach block button to target DOM element.
        this._attachButtonToElement(domTarget);
        // When panel is opened then refresh it position to be properly aligned with block button.
        if (this.panelView.isVisible) {
            this._showPanel();
        }
    }
    /**
     * Hides the button.
     *
     * @private
     */
    _hideButton() {
        this.buttonView.isVisible = false;
    }
    /**
     * Shows the {@link #toolbarView} attached to the {@link #buttonView}.
     * If the toolbar is already visible, then it simply repositions it.
     *
     * @private
     */
    _showPanel() {
        // Usually, the only way to show the toolbar is by pressing the block button. It makes it impossible for
        // the toolbar to show up when the button is invisible (feature does not make sense for the selection then).
        // The toolbar navigation using Alt+F10 does not access the button but shows the panel directly using this method.
        // So we need to check whether this is possible first.
        if (!this.buttonView.isVisible) {
            return;
        }
        const wasVisible = this.panelView.isVisible;
        // So here's the thing: If there was no initial panelView#show() or these two were in different order, the toolbar
        // positioning will break in RTL editors. Weird, right? What you show know is that the toolbar
        // grouping works thanks to:
        //
        // * the ResizeObserver, which kicks in as soon as the toolbar shows up in DOM (becomes visible again).
        // * the observable ToolbarView#maxWidth, which triggers re-grouping when changed.
        //
        // Here are the possible scenarios:
        //
        // 1. (WRONG ❌) If the #maxWidth is set when the toolbar is invisible, it won't affect item grouping (no DOMRects, no grouping).
        //    Then, when panelView.pin() is called, the position of the toolbar will be calculated for the old
        //    items grouping state, and when finally ResizeObserver kicks in (hey, the toolbar is visible now, right?)
        //    it will group/ungroup some items and the length of the toolbar will change. But since in RTL the toolbar
        //    is attached on the right side and the positioning uses CSS "left", it will result in the toolbar shifting
        //    to the left and being displayed in the wrong place.
        // 2. (WRONG ❌) If the panelView.pin() is called first and #maxWidth set next, then basically the story repeats. The balloon
        //    calculates the position for the old toolbar grouping state, then the toolbar re-groups items and because
        //    it is positioned using CSS "left" it will move.
        // 3. (RIGHT ✅) We show the panel first (the toolbar does re-grouping but it does not matter), then the #maxWidth
        //    is set allowing the toolbar to re-group again and finally panelView.pin() does the positioning when the
        //    items grouping state is stable and final.
        //
        // https://github.com/ckeditor/ckeditor5/issues/6449, https://github.com/ckeditor/ckeditor5/issues/6575
        this.panelView.show();
        this.toolbarView.maxWidth = this._getToolbarMaxWidth();
        this.panelView.pin({
            target: this.buttonView.element,
            limiter: this.editor.ui.getEditableElement()
        });
        if (!wasVisible) {
            this.toolbarView.items.get(0).focus();
        }
    }
    /**
     * Hides the {@link #toolbarView}.
     *
     * @private
     * @param {Boolean} [focusEditable=false] When `true`, the editable will be focused after hiding the panel.
     */
    _hidePanel(focusEditable) {
        this.panelView.isVisible = false;
        if (focusEditable) {
            this.editor.editing.view.focus();
        }
    }
    /**
     * Attaches the {@link #buttonView} to the target block of content.
     *
     * @protected
     * @param {HTMLElement} targetElement Target element.
     */
    _attachButtonToElement(targetElement) {
        const contentStyles = window.getComputedStyle(targetElement);
        const editableRect = new Rect(this.editor.ui.getEditableElement());
        const contentPaddingTop = parseInt(contentStyles.paddingTop, 10);
        // When line height is not an integer then thread it as "normal".
        // MDN says that 'normal' == ~1.2 on desktop browsers.
        const contentLineHeight = parseInt(contentStyles.lineHeight, 10) || parseInt(contentStyles.fontSize, 10) * 1.2;
        const position = getOptimalPosition({
            element: this.buttonView.element,
            target: targetElement,
            positions: [
                (contentRect, buttonRect) => {
                    let left;
                    if (this.editor.locale.uiLanguageDirection === 'ltr') {
                        left = editableRect.left - buttonRect.width;
                    }
                    else {
                        left = editableRect.right;
                    }
                    return {
                        top: contentRect.top + contentPaddingTop + (contentLineHeight - buttonRect.height) / 2,
                        left
                    };
                }
            ]
        });
        this.buttonView.top = position.top;
        this.buttonView.left = position.left;
    }
    /**
     * Gets the {@link #toolbarView} max-width, based on
     * editable width plus distance between farthest edge of the {@link #buttonView} and the editable.
     *
     * @private
     * @returns {String} maxWidth A maximum width that toolbar can have, in pixels.
     */
    _getToolbarMaxWidth() {
        const editableElement = this.editor.ui.view.editable.element;
        const editableRect = new Rect(editableElement);
        const buttonRect = new Rect(this.buttonView.element);
        const isRTL = this.editor.locale.uiLanguageDirection === 'rtl';
        const offset = isRTL ? (buttonRect.left - editableRect.right) + buttonRect.width : editableRect.left - buttonRect.left;
        return blocktoolbar_toPx(editableRect.width + offset);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module ui
 */











































;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/ui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/ui
 */


;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/engine.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/engine
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-classic/src/classiceditorui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-classic/classiceditorui
 */






/**
 * The classic editor UI class.
 *
 * @extends module:core/editor/editorui~EditorUI
 */
class ClassicEditorUI extends EditorUI {
	/**
	 * Creates an instance of the classic editor UI class.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {module:ui/editorui/editoruiview~EditorUIView} view The view of the UI.
	 */
	constructor( editor, view ) {
		super( editor );

		/**
		 * The main (top–most) view of the editor UI.
		 *
		 * @readonly
		 * @member {module:ui/editorui/editoruiview~EditorUIView} #view
		 */
		this.view = view;

		/**
		 * A normalized `config.toolbar` object.
		 *
		 * @private
		 * @member {Object}
		 */
		this._toolbarConfig = normalizetoolbarconfig_normalizeToolbarConfig( editor.config.get( 'toolbar' ) );

		/**
		 * The element replacer instance used to hide the editor's source element.
		 *
		 * @protected
		 * @member {module:utils/elementreplacer~ElementReplacer}
		 */
		this._elementReplacer = new ElementReplacer();
	}

	/**
	 * @inheritDoc
	 */
	get element() {
		return this.view.element;
	}

	/**
	 * Initializes the UI.
	 *
	 * @param {HTMLElement|null} replacementElement The DOM element that will be the source for the created editor.
	 */
	init( replacementElement ) {
		const editor = this.editor;
		const view = this.view;
		const editingView = editor.editing.view;
		const editable = view.editable;
		const editingRoot = editingView.document.getRoot();

		// The editable UI and editing root should share the same name. Then name is used
		// to recognize the particular editable, for instance in ARIA attributes.
		editable.name = editingRoot.rootName;

		view.render();

		// The editable UI element in DOM is available for sure only after the editor UI view has been rendered.
		// But it can be available earlier if a DOM element has been passed to BalloonEditor.create().
		const editableElement = editable.element;

		// Register the editable UI view in the editor. A single editor instance can aggregate multiple
		// editable areas (roots) but the classic editor has only one.
		this.setEditableElement( editable.name, editableElement );

		// Let the editable UI element respond to the changes in the global editor focus
		// tracker. It has been added to the same tracker a few lines above but, in reality, there are
		// many focusable areas in the editor, like balloons, toolbars or dropdowns and as long
		// as they have focus, the editable should act like it is focused too (although technically
		// it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user.
		// Doing otherwise will result in editable focus styles disappearing, once e.g. the
		// toolbar gets focused.
		view.editable.bind( 'isFocused' ).to( this.focusTracker );

		// Bind the editable UI element to the editing view, making it an end– and entry–point
		// of the editor's engine. This is where the engine meets the UI.
		editingView.attachDomRoot( editableElement );

		// If an element containing the initial data of the editor was provided, replace it with
		// an editor instance's UI in DOM until the editor is destroyed. For instance, a <textarea>
		// can be such element.
		if ( replacementElement ) {
			this._elementReplacer.replace( replacementElement, this.element );
		}

		this._initPlaceholder();
		this._initToolbar();
		this.fire( 'ready' );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		const view = this.view;
		const editingView = this.editor.editing.view;

		this._elementReplacer.restore();
		editingView.detachDomRoot( view.editable.name );
		view.destroy();
	}

	/**
	 * Initializes the editor toolbar.
	 *
	 * @private
	 */
	_initToolbar() {
		const view = this.view;

		// Set–up the sticky panel with toolbar.
		view.stickyPanel.bind( 'isActive' ).to( this.focusTracker, 'isFocused' );
		view.stickyPanel.limiterElement = view.element;
		view.stickyPanel.bind( 'viewportTopOffset' ).to( this, 'viewportOffset', ( { top } ) => top );

		view.toolbar.fillFromConfig( this._toolbarConfig, this.componentFactory );

		// Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
		this.addToolbar( view.toolbar );
	}

	/**
	 * Enable the placeholder text on the editing root, if any was configured.
	 *
	 * @private
	 */
	_initPlaceholder() {
		const editor = this.editor;
		const editingView = editor.editing.view;
		const editingRoot = editingView.document.getRoot();
		const sourceElement = editor.sourceElement;

		const placeholderText = editor.config.get( 'placeholder' ) ||
			sourceElement && sourceElement.tagName.toLowerCase() === 'textarea' && sourceElement.getAttribute( 'placeholder' );

		if ( placeholderText ) {
			enablePlaceholder( {
				view: editingView,
				element: editingRoot,
				text: placeholderText,
				isDirectHost: false,
				keepOnFocus: true
			} );
		}
	}
}


// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-editor-classic/theme/classiceditor.css
var classiceditor = __webpack_require__(3638);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-classic/theme/classiceditor.css

            

var classiceditor_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

classiceditor_options.insert = "head";
classiceditor_options.singleton = true;

var classiceditor_update = injectStylesIntoStyleTag_default()(classiceditor/* default */.Z, classiceditor_options);



/* harmony default export */ const theme_classiceditor = (classiceditor/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-classic/src/classiceditoruiview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-classic/classiceditoruiview
 */





/**
 * Classic editor UI view. Uses an inline editable and a sticky toolbar, all
 * enclosed in a boxed UI view.
 *
 * @extends module:ui/editorui/boxed/boxededitoruiview~BoxedEditorUIView
 */
class ClassicEditorUIView extends BoxedEditorUIView {
	/**
	 * Creates an instance of the classic editor UI view.
	 *
	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
	 * @param {module:engine/view/view~View} editingView The editing view instance this view is related to.
	 * @param {Object} [options={}] Configuration options for the view instance.
	 * @param {Boolean} [options.shouldToolbarGroupWhenFull] When set `true` enables automatic items grouping
	 * in the main {@link module:editor-classic/classiceditoruiview~ClassicEditorUIView#toolbar toolbar}.
	 * See {@link module:ui/toolbar/toolbarview~ToolbarOptions#shouldGroupWhenFull} to learn more.
	 */
	constructor( locale, editingView, options = {} ) {
		super( locale );

		/**
		 * Sticky panel view instance. This is a parent view of a {@link #toolbar}
		 * that makes toolbar sticky.
		 *
		 * @readonly
		 * @member {module:ui/panel/sticky/stickypanelview~StickyPanelView}
		 */
		this.stickyPanel = new StickyPanelView( locale );

		/**
		 * Toolbar view instance.
		 *
		 * @readonly
		 * @member {module:ui/toolbar/toolbarview~ToolbarView}
		 */
		this.toolbar = new toolbarview_ToolbarView( locale, {
			shouldGroupWhenFull: options.shouldToolbarGroupWhenFull
		} );

		/**
		 * Editable UI view.
		 *
		 * @readonly
		 * @member {module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}
		 */
		this.editable = new InlineEditableUIView( locale, editingView );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		// Set toolbar as a child of a stickyPanel and makes toolbar sticky.
		this.stickyPanel.content.add( this.toolbar );

		this.top.add( this.stickyPanel );
		this.main.add( this.editable );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-classic/src/classiceditor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-classic/classiceditor
 */









/**
 * The {@glink installation/getting-started/predefined-builds#classic-editor classic editor} implementation.
 * It uses an inline editable and a sticky toolbar, all enclosed in a boxed UI.
 * See the {@glink examples/builds/classic-editor demo}.
 *
 * In order to create a classic editor instance, use the static
 * {@link module:editor-classic/classiceditor~ClassicEditor.create `ClassicEditor.create()`} method.
 *
 * # Classic editor and classic build
 *
 * The classic editor can be used directly from source (if you installed the
 * [`@ckeditor/ckeditor5-editor-classic`](https://www.npmjs.com/package/@ckeditor/ckeditor5-editor-classic) package)
 * but it is also available in the {@glink installation/getting-started/predefined-builds#classic-editor classic build}.
 *
 * {@glink installation/getting-started/predefined-builds Builds}
 * are ready-to-use editors with plugins bundled in. When using the editor from
 * source you need to take care of loading all plugins by yourself
 * (through the {@link module:core/editor/editorconfig~EditorConfig#plugins `config.plugins`} option).
 * Using the editor from source gives much better flexibility and allows easier customization.
 *
 * Read more about initializing the editor from source or as a build in
 * {@link module:editor-classic/classiceditor~ClassicEditor.create `ClassicEditor.create()`}.
 *
 * @mixes module:core/editor/utils/dataapimixin~DataApiMixin
 * @mixes module:core/editor/utils/elementapimixin~ElementApiMixin
 * @implements module:core/editor/editorwithui~EditorWithUI
 * @extends module:core/editor/editor~Editor
 */
class classiceditor_ClassicEditor extends Editor {
	/**
	 * Creates an instance of the classic editor.
	 *
	 * **Note:** do not use the constructor to create editor instances. Use the static
	 * {@link module:editor-classic/classiceditor~ClassicEditor.create `ClassicEditor.create()`} method instead.
	 *
	 * @protected
	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
	 * or the editor's initial data. For more information see
	 * {@link module:editor-classic/classiceditor~ClassicEditor.create `ClassicEditor.create()`}.
	 * @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
	 */
	constructor( sourceElementOrData, config = {} ) {
		// If both `config.initialData` is set and initial data is passed as the constructor parameter, then throw.
		if ( !lodash_es_isElement( sourceElementOrData ) && config.initialData !== undefined ) {
			// Documented in core/editor/editorconfig.jsdoc.
			// eslint-disable-next-line ckeditor5-rules/ckeditor-error-message
			throw new CKEditorError( 'editor-create-initial-data', null );
		}

		super( config );

		if ( this.config.get( 'initialData' ) === undefined ) {
			this.config.set( 'initialData', getInitialData( sourceElementOrData ) );
		}

		if ( lodash_es_isElement( sourceElementOrData ) ) {
			this.sourceElement = sourceElementOrData;
		}

		this.model.document.createRoot();

		const shouldToolbarGroupWhenFull = !this.config.get( 'toolbar.shouldNotGroupWhenFull' );
		const view = new ClassicEditorUIView( this.locale, this.editing.view, {
			shouldToolbarGroupWhenFull
		} );

		this.ui = new ClassicEditorUI( this, view );

		attachToForm( this );
	}

	/**
	 * Destroys the editor instance, releasing all resources used by it.
	 *
	 * Updates the original editor element with the data if the
	 * {@link module:core/editor/editorconfig~EditorConfig#updateSourceElementOnDestroy `updateSourceElementOnDestroy`}
	 * configuration option is set to `true`.
	 *
	 * @returns {Promise}
	 */
	destroy() {
		if ( this.sourceElement ) {
			this.updateSourceElement();
		}

		this.ui.destroy();

		return super.destroy();
	}

	/**
	 * Creates a new classic editor instance.
	 *
	 * There are three ways how the editor can be initialized.
	 *
	 * # Replacing a DOM element (and loading data from it)
	 *
	 * You can initialize the editor using an existing DOM element:
	 *
	 *		ClassicEditor
	 *			.create( document.querySelector( '#editor' ) )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * The element's content will be used as the editor data and the element will be replaced by the editor UI.
	 *
	 * # Creating a detached editor
	 *
	 * Alternatively, you can initialize the editor by passing the initial data directly as a string.
	 * In this case, the editor will render an element that must be inserted into the DOM:
	 *
	 *		ClassicEditor
	 *			.create( '<p>Hello world!</p>' )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *
	 *				// Initial data was provided so the editor UI element needs to be added manually to the DOM.
	 *				document.body.appendChild( editor.ui.element );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * This lets you dynamically append the editor to your web page whenever it is convenient for you. You may use this method if your
	 * web page content is generated on the client side and the DOM structure is not ready at the moment when you initialize the editor.
	 *
	 * # Replacing a DOM element (and data provided in `config.initialData`)
	 *
	 * You can also mix these two ways by providing a DOM element to be used and passing the initial data through the configuration:
	 *
	 *		ClassicEditor
	 *			.create( document.querySelector( '#editor' ), {
	 *				initialData: '<h2>Initial data</h2><p>Foo bar.</p>'
	 *			} )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * This method can be used to initialize the editor on an existing element with the specified content in case if your integration
	 * makes it difficult to set the content of the source element.
	 *
	 * Note that an error will be thrown if you pass the initial data both as the first parameter and also in the configuration.
	 *
	 * # Configuring the editor
	 *
	 * See the {@link module:core/editor/editorconfig~EditorConfig editor configuration documentation} to learn more about
	 * customizing plugins, toolbar and more.
	 *
	 * # Using the editor from source
	 *
	 * The code samples listed in the previous sections of this documentation assume that you are using an
	 * {@glink installation/getting-started/predefined-builds editor build} (for example – `@ckeditor/ckeditor5-build-classic`).
	 *
	 * If you want to use the classic editor from source (`@ckeditor/ckeditor5-editor-classic/src/classiceditor`),
	 * you need to define the list of
	 * {@link module:core/editor/editorconfig~EditorConfig#plugins plugins to be initialized} and
	 * {@link module:core/editor/editorconfig~EditorConfig#toolbar toolbar items}. Read more about using the editor from
	 * source in the {@glink installation/advanced/alternative-setups/integrating-from-source dedicated guide}.
	 *
	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
	 * or the editor's initial data.
	 *
	 * If a DOM element is passed, its content will be automatically loaded to the editor upon initialization
	 * and the {@link module:editor-classic/classiceditorui~ClassicEditorUI#element editor element} will replace the passed element
	 * in the DOM (the original one will be hidden and the editor will be injected next to it).
	 *
	 * If the {@link module:core/editor/editorconfig~EditorConfig#updateSourceElementOnDestroy updateSourceElementOnDestroy}
	 * option is set to `true`, the editor data will be set back to the original element once the editor is destroyed and when a form,
	 * in which this element is contained, is submitted (if the original element is a `<textarea>`). This ensures seamless integration
	 * with native web forms.
	 *
	 * If the initial data is passed, a detached editor will be created. In this case you need to insert it into the DOM manually.
	 * It is available under the {@link module:editor-classic/classiceditorui~ClassicEditorUI#element `editor.ui.element`} property.
	 *
	 * @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
	 * @returns {Promise} A promise resolved once the editor is ready. The promise resolves with the created editor instance.
	 */
	static create( sourceElementOrData, config = {} ) {
		return new Promise( resolve => {
			const editor = new this( sourceElementOrData, config );

			resolve(
				editor.initPlugins()
					.then( () => editor.ui.init( lodash_es_isElement( sourceElementOrData ) ? sourceElementOrData : null ) )
					.then( () => editor.data.init( editor.config.get( 'initialData' ) ) )
					.then( () => editor.fire( 'ready' ) )
					.then( () => editor )
			);
		} );
	}
}

mix( classiceditor_ClassicEditor, DataApiMixin );
mix( classiceditor_ClassicEditor, ElementApiMixin );

function getInitialData( sourceElementOrData ) {
	return lodash_es_isElement( sourceElementOrData ) ? getDataFromElement( sourceElementOrData ) : sourceElementOrData;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-inline/src/inlineeditorui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-inline/inlineeditorui
 */





/**
 * The inline editor UI class.
 *
 * @extends module:core/editor/editorui~EditorUI
 */
class InlineEditorUI extends EditorUI {
	/**
	 * Creates an instance of the inline editor UI class.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {module:ui/editorui/editoruiview~EditorUIView} view The view of the UI.
	 */
	constructor( editor, view ) {
		super( editor );

		/**
		 * The main (top–most) view of the editor UI.
		 *
		 * @readonly
		 * @member {module:ui/editorui/editoruiview~EditorUIView} #view
		 */
		this.view = view;

		/**
		 * A normalized `config.toolbar` object.
		 *
		 * @type {Object}
		 * @private
		 */
		this._toolbarConfig = normalizetoolbarconfig_normalizeToolbarConfig( editor.config.get( 'toolbar' ) );
	}

	/**
	 * @inheritDoc
	 */
	get element() {
		return this.view.editable.element;
	}

	/**
	 * Initializes the UI.
	 */
	init() {
		const editor = this.editor;
		const view = this.view;
		const editingView = editor.editing.view;
		const editable = view.editable;
		const editingRoot = editingView.document.getRoot();

		// The editable UI and editing root should share the same name. Then name is used
		// to recognize the particular editable, for instance in ARIA attributes.
		editable.name = editingRoot.rootName;

		view.render();

		// The editable UI element in DOM is available for sure only after the editor UI view has been rendered.
		// But it can be available earlier if a DOM element has been passed to InlineEditor.create().
		const editableElement = editable.element;

		// Register the editable UI view in the editor. A single editor instance can aggregate multiple
		// editable areas (roots) but the inline editor has only one.
		this.setEditableElement( editable.name, editableElement );

		// Let the editable UI element respond to the changes in the global editor focus
		// tracker. It has been added to the same tracker a few lines above but, in reality, there are
		// many focusable areas in the editor, like balloons, toolbars or dropdowns and as long
		// as they have focus, the editable should act like it is focused too (although technically
		// it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user.
		// Doing otherwise will result in editable focus styles disappearing, once e.g. the
		// toolbar gets focused.
		editable.bind( 'isFocused' ).to( this.focusTracker );

		// Bind the editable UI element to the editing view, making it an end– and entry–point
		// of the editor's engine. This is where the engine meets the UI.
		editingView.attachDomRoot( editableElement );

		this._initPlaceholder();
		this._initToolbar();
		this.fire( 'ready' );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		const view = this.view;
		const editingView = this.editor.editing.view;

		editingView.detachDomRoot( view.editable.name );
		view.destroy();
	}

	/**
	 * Initializes the inline editor toolbar and its panel.
	 *
	 * @private
	 */
	_initToolbar() {
		const editor = this.editor;
		const view = this.view;
		const editableElement = view.editable.element;
		const toolbar = view.toolbar;

		// Set–up the view#panel.
		view.panel.bind( 'isVisible' ).to( this.focusTracker, 'isFocused' );

		view.bind( 'viewportTopOffset' ).to( this, 'viewportOffset', ( { top } ) => top );

		// https://github.com/ckeditor/ckeditor5-editor-inline/issues/4
		view.listenTo( editor.ui, 'update', () => {
			// Don't pin if the panel is not already visible. It prevents the panel
			// showing up when there's no focus in the UI.
			if ( view.panel.isVisible ) {
				view.panel.pin( {
					target: editableElement,
					positions: view.panelPositions
				} );
			}
		} );

		toolbar.fillFromConfig( this._toolbarConfig, this.componentFactory );

		// Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
		this.addToolbar( toolbar );
	}

	/**
	 * Enable the placeholder text on the editing root, if any was configured.
	 *
	 * @private
	 */
	_initPlaceholder() {
		const editor = this.editor;
		const editingView = editor.editing.view;
		const editingRoot = editingView.document.getRoot();
		const sourceElement = editor.sourceElement;

		const placeholderText = editor.config.get( 'placeholder' ) ||
			sourceElement && sourceElement.tagName.toLowerCase() === 'textarea' && sourceElement.getAttribute( 'placeholder' );

		if ( placeholderText ) {
			enablePlaceholder( {
				view: editingView,
				element: editingRoot,
				text: placeholderText,
				isDirectHost: false,
				keepOnFocus: true
			} );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-inline/src/inlineeditoruiview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-inline/inlineeditoruiview
 */



const inlineeditoruiview_toPx = toUnit( 'px' );

/**
 * Inline editor UI view. Uses an nline editable and a floating toolbar.
 *
 * @extends module:ui/editorui/editoruiview~EditorUIView
 */
class InlineEditorUIView extends EditorUIView {
	/**
	 * Creates an instance of the inline editor UI view.
	 *
	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
	 * @param {module:engine/view/view~View} editingView The editing view instance this view is related to.
	 * @param {HTMLElement} [editableElement] The editable element. If not specified, it will be automatically created by
	 * {@link module:ui/editableui/editableuiview~EditableUIView}. Otherwise, the given element will be used.
	 * @param {Object} [options={}] Configuration options for the view instance.
	 * @param {Boolean} [options.shouldToolbarGroupWhenFull] When set `true` enables automatic items grouping
	 * in the main {@link module:editor-inline/inlineeditoruiview~InlineEditorUIView#toolbar toolbar}.
	 * See {@link module:ui/toolbar/toolbarview~ToolbarOptions#shouldGroupWhenFull} to learn more.
	 */
	constructor( locale, editingView, editableElement, options = {} ) {
		super( locale );

		const t = locale.t;

		/**
		 * A floating toolbar view instance.
		 *
		 * @readonly
		 * @member {module:ui/toolbar/toolbarview~ToolbarView}
		 */
		this.toolbar = new toolbarview_ToolbarView( locale, {
			shouldGroupWhenFull: options.shouldToolbarGroupWhenFull,
			isFloating: true
		} );

		/**
		 * The offset from the top edge of the web browser's viewport which makes the
		 * UI become sticky. The default value is `0`, which means that the UI becomes
		 * sticky when its upper edge touches the top of the page viewport.
		 *
		 * This attribute is useful when the web page has UI elements positioned to the top
		 * either using `position: fixed` or `position: sticky`, which would cover the
		 * UI or vice–versa (depending on the `z-index` hierarchy).
		 *
		 * Bound to {@link module:core/editor/editorui~EditorUI#viewportOffset `EditorUI#viewportOffset`}.
		 *
		 * If {@link module:core/editor/editorconfig~EditorConfig#ui `EditorConfig#ui.viewportOffset.top`} is defined, then
		 * it will override the default value.
		 *
		 * @observable
		 * @default 0
		 * @member {Number} #viewportTopOffset
		 */
		this.set( 'viewportTopOffset', 0 );

		/**
		 * A balloon panel view instance.
		 *
		 * @readonly
		 * @member {module:ui/panel/balloon/balloonpanelview~BalloonPanelView}
		 */
		this.panel = new balloonpanelview_BalloonPanelView( locale );

		/**
		 * A set of positioning functions used by the {@link #panel} to float around
		 * {@link #element editableElement}.
		 *
		 * The positioning functions are as follows:
		 *
		 * * West:
		 *
		 *		[ Panel ]
		 *		+------------------+
		 *		| #editableElement |
		 *		+------------------+
		 *
		 *		+------------------+
		 *		| #editableElement |
		 *		|[ Panel ]         |
		 *		|                  |
		 *		+------------------+
		 *
		 *		+------------------+
		 *		| #editableElement |
		 *		+------------------+
		 *		[ Panel ]
		 *
		 * * East:
		 *
		 *		           [ Panel ]
		 *		+------------------+
		 *		| #editableElement |
		 *		+------------------+
		 *
		 *		+------------------+
		 *		| #editableElement |
		 *		|         [ Panel ]|
		 *		|                  |
		 *		+------------------+
		 *
		 *		+------------------+
		 *		| #editableElement |
		 *		+------------------+
		 *		           [ Panel ]
		 *
		 * See: {@link module:utils/dom/position~Options#positions}.
		 *
		 * @readonly
		 * @type {Array.<module:utils/dom/position~PositioningFunction>}
		 */
		this.panelPositions = this._getPanelPositions();

		this.panel.extendTemplate( {
			attributes: {
				class: 'ck-toolbar-container'
			}
		} );

		/**
		 * Editable UI view.
		 *
		 * @readonly
		 * @member {module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}
		 */
		this.editable = new InlineEditableUIView( locale, editingView, editableElement, {
			label: editableView => {
				return t( 'Rich Text Editor. Editing area: %0', editableView.name );
			}
		} );

		/**
		 * An instance of the resize observer that helps dynamically determine the geometry of the toolbar
		 * and manage items that do not fit into a single row.
		 *
		 * **Note:** Created in {@link #render}.
		 *
		 * @private
		 * @member {module:utils/dom/resizeobserver~ResizeObserver}
		 */
		this._resizeObserver = null;
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		this.body.add( this.panel );
		this.registerChild( this.editable );
		this.panel.content.add( this.toolbar );

		const options = this.toolbar.options;

		// Set toolbar's max-width on the initialization and update it on the editable resize,
		// if 'shouldToolbarGroupWhenFull' in config is set to 'true'.
		if ( options.shouldGroupWhenFull ) {
			const editableElement = this.editable.element;

			this._resizeObserver = new resizeobserver_ResizeObserver( editableElement, () => {
				this.toolbar.maxWidth = inlineeditoruiview_toPx( new rect_Rect( editableElement ).width );
			} );
		}
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		if ( this._resizeObserver ) {
			this._resizeObserver.destroy();
		}
	}

	/**
	 * Determines the panel top position of the {@link #panel} in {@link #panelPositions}.
	 *
	 * @private
	 * @param {module:utils/dom/rect~Rect} editableRect Rect of the {@link #element}.
	 * @param {module:utils/dom/rect~Rect} panelRect Rect of the {@link #panel}.
	 */
	_getPanelPositionTop( editableRect, panelRect ) {
		let top;

		if ( editableRect.top > panelRect.height + this.viewportTopOffset ) {
			top = editableRect.top - panelRect.height;
		} else if ( editableRect.bottom > panelRect.height + this.viewportTopOffset + 50 ) {
			top = this.viewportTopOffset;
		} else {
			top = editableRect.bottom;
		}

		return top;
	}

	/**
	 * Returns the positions for {@link #panelPositions}.
	 *
	 * See: {@link module:utils/dom/position~Options#positions}.
	 *
	 * @private
	 * @returns {Array.<module:utils/dom/position~PositioningFunction>}
	 */
	_getPanelPositions() {
		const positions = [
			( editableRect, panelRect ) => {
				return {
					top: this._getPanelPositionTop( editableRect, panelRect ),
					left: editableRect.left,
					name: 'toolbar_west',
					config: {
						withArrow: false
					}
				};
			},
			( editableRect, panelRect ) => {
				return {
					top: this._getPanelPositionTop( editableRect, panelRect ),
					left: editableRect.left + editableRect.width - panelRect.width,
					name: 'toolbar_east',
					config: {
						withArrow: false
					}
				};
			}
		];

		if ( this.locale.uiLanguageDirection === 'ltr' ) {
			return positions;
		} else {
			return positions.reverse();
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-inline/src/inlineeditor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-inline/inlineeditor
 */









/**
 * The {@glink installation/getting-started/predefined-builds#inline-editor inline editor} implementation.
 * It uses an inline editable and a floating toolbar.
 * See the {@glink examples/builds/inline-editor demo}.
 *
 * In order to create a inline editor instance, use the static
 * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`} method.
 *
 * # Inline editor and inline build
 *
 * The inline editor can be used directly from source (if you installed the
 * [`@ckeditor/ckeditor5-editor-inline`](https://www.npmjs.com/package/@ckeditor/ckeditor5-editor-inline) package)
 * but it is also available in the {@glink installation/getting-started/predefined-builds#inline-editor inline build}.
 *
 * {@glink installation/getting-started/predefined-builds Builds}
 * are ready-to-use editors with plugins bundled in. When using the editor from
 * source you need to take care of loading all plugins by yourself
 * (through the {@link module:core/editor/editorconfig~EditorConfig#plugins `config.plugins`} option).
 * Using the editor from source gives much better flexibility and allows easier customization.
 *
 * Read more about initializing the editor from source or as a build in
 * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`}.
 *
 * @mixes module:core/editor/utils/dataapimixin~DataApiMixin
 * @mixes module:core/editor/utils/elementapimixin~ElementApiMixin
 * @implements module:core/editor/editorwithui~EditorWithUI
 * @extends module:core/editor/editor~Editor
 */
class inlineeditor_InlineEditor extends Editor {
	/**
	 * Creates an instance of the inline editor.
	 *
	 * **Note:** Do not use the constructor to create editor instances. Use the static
	 * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`} method instead.
	 *
	 * @protected
	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
	 * (on which the editor will be initialized) or initial data for the editor. For more information see
	 * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`}.
	 * @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
	 */
	constructor( sourceElementOrData, config = {} ) {
		// If both `config.initialData` and initial data parameter in `create()` are set, then throw.
		if ( !lodash_es_isElement( sourceElementOrData ) && config.initialData !== undefined ) {
			// Documented in core/editor/editorconfig.jsdoc.
			// eslint-disable-next-line ckeditor5-rules/ckeditor-error-message
			throw new CKEditorError( 'editor-create-initial-data', null );
		}

		super( config );

		if ( this.config.get( 'initialData' ) === undefined ) {
			this.config.set( 'initialData', inlineeditor_getInitialData( sourceElementOrData ) );
		}

		this.model.document.createRoot();

		if ( lodash_es_isElement( sourceElementOrData ) ) {
			this.sourceElement = sourceElementOrData;
			secureSourceElement( this );
		}

		const shouldToolbarGroupWhenFull = !this.config.get( 'toolbar.shouldNotGroupWhenFull' );

		const view = new InlineEditorUIView( this.locale, this.editing.view, this.sourceElement, {
			shouldToolbarGroupWhenFull
		} );
		this.ui = new InlineEditorUI( this, view );

		attachToForm( this );
	}

	/**
	 * Destroys the editor instance, releasing all resources used by it.
	 *
	 * Updates the original editor element with the data if the
	 * {@link module:core/editor/editorconfig~EditorConfig#updateSourceElementOnDestroy `updateSourceElementOnDestroy`}
	 * configuration option is set to `true`.
	 *
	 * @returns {Promise}
	 */
	destroy() {
		// Cache the data, then destroy.
		// It's safe to assume that the model->view conversion will not work after super.destroy().
		const data = this.getData();

		this.ui.destroy();

		return super.destroy()
			.then( () => {
				if ( this.sourceElement ) {
					this.updateSourceElement( data );
				}
			} );
	}

	/**
	 * Creates a new inline editor instance.
	 *
	 * There are three general ways how the editor can be initialized.
	 *
	 * # Using an existing DOM element (and loading data from it)
	 *
	 * You can initialize the editor using an existing DOM element:
	 *
	 *		InlineEditor
	 *			.create( document.querySelector( '#editor' ) )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * The element's content will be used as the editor data and the element will become the editable element.
	 *
	 * # Creating a detached editor
	 *
	 * Alternatively, you can initialize the editor by passing the initial data directly as a `String`.
	 * In this case, the editor will render an element that must be inserted into the DOM for the editor to work properly:
	 *
	 *		InlineEditor
	 *			.create( '<p>Hello world!</p>' )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *
	 *				// Initial data was provided so the editor UI element needs to be added manually to the DOM.
	 *				document.body.appendChild( editor.ui.element );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * This lets you dynamically append the editor to your web page whenever it is convenient for you. You may use this method if your
	 * web page content is generated on the client side and the DOM structure is not ready at the moment when you initialize the editor.
	 *
	 * # Using an existing DOM element (and data provided in `config.initialData`)
	 *
	 * You can also mix these two ways by providing a DOM element to be used and passing the initial data through the configuration:
	 *
	 *		InlineEditor
	 *			.create( document.querySelector( '#editor' ), {
	 *				initialData: '<h2>Initial data</h2><p>Foo bar.</p>'
	 *			} )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * This method can be used to initialize the editor on an existing element with the specified content in case if your integration
	 * makes it difficult to set the content of the source element.
	 *
	 * Note that an error will be thrown if you pass the initial data both as the first parameter and also in the configuration.
	 *
	 * # Configuring the editor
	 *
	 * See the {@link module:core/editor/editorconfig~EditorConfig editor configuration documentation} to learn more about
	 * customizing plugins, toolbar and more.
	 *
	 * # Using the editor from source
	 *
	 * The code samples listed in the previous sections of this documentation assume that you are using an
	 * {@glink installation/getting-started/predefined-builds editor build} (for example – `@ckeditor/ckeditor5-build-inline`).
	 *
	 * If you want to use the inline editor from source (`@ckeditor/ckeditor5-editor-inline/src/inlineeditor`),
	 * you need to define the list of
	 * {@link module:core/editor/editorconfig~EditorConfig#plugins plugins to be initialized} and
	 * {@link module:core/editor/editorconfig~EditorConfig#toolbar toolbar items}. Read more about using the editor from
	 * source in the {@glink installation/advanced/alternative-setups/integrating-from-source dedicated guide}.
	 *
	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
	 * or the editor's initial data.
	 *
	 * If a DOM element is passed, its content will be automatically loaded to the editor upon initialization.
	 * The editor data will be set back to the original element once the editor is destroyed only if the
	 * {@link module:core/editor/editorconfig~EditorConfig#updateSourceElementOnDestroy updateSourceElementOnDestroy}
	 * option is set to `true`.
	 *
	 * If the initial data is passed, a detached editor will be created. In this case you need to insert it into the DOM manually.
	 * It is available under the {@link module:editor-inline/inlineeditorui~InlineEditorUI#element `editor.ui.element`} property.
	 *
	 * @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
	 * @returns {Promise} A promise resolved once the editor is ready. The promise resolves with the created editor instance.
	 */
	static create( sourceElementOrData, config = {} ) {
		return new Promise( resolve => {
			if ( lodash_es_isElement( sourceElementOrData ) && sourceElementOrData.tagName === 'TEXTAREA' ) {
				// Documented in core/editor/editor.js
				// eslint-disable-next-line ckeditor5-rules/ckeditor-error-message
				throw new CKEditorError( 'editor-wrong-element', null );
			}

			const editor = new this( sourceElementOrData, config );

			resolve(
				editor.initPlugins()
					.then( () => editor.ui.init() )
					.then( () => editor.data.init( editor.config.get( 'initialData' ) ) )
					.then( () => editor.fire( 'ready' ) )
					.then( () => editor )
			);
		} );
	}
}

mix( inlineeditor_InlineEditor, DataApiMixin );
mix( inlineeditor_InlineEditor, ElementApiMixin );

function inlineeditor_getInitialData( sourceElementOrData ) {
	return lodash_es_isElement( sourceElementOrData ) ? getDataFromElement( sourceElementOrData ) : sourceElementOrData;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-balloon/src/ballooneditorui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-balloon/ballooneditorui
 */




/**
 * The balloon editor UI class.
 *
 * @extends module:core/editor/editorui~EditorUI
 */
class BalloonEditorUI extends EditorUI {
	/**
	 * Creates an instance of the balloon editor UI class.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {module:ui/editorui/editoruiview~EditorUIView} view The view of the UI.
	 */
	constructor( editor, view ) {
		super( editor );

		/**
		 * The main (top–most) view of the editor UI.
		 *
		 * @readonly
		 * @member {module:ui/editorui/editoruiview~EditorUIView} #view
		 */
		this.view = view;
	}

	/**
	 * @inheritDoc
	 */
	get element() {
		return this.view.editable.element;
	}

	/**
	 * Initializes the UI.
	 */
	init() {
		const editor = this.editor;
		const view = this.view;
		const editingView = editor.editing.view;
		const editable = view.editable;
		const editingRoot = editingView.document.getRoot();

		// The editable UI and editing root should share the same name. Then name is used
		// to recognize the particular editable, for instance in ARIA attributes.
		editable.name = editingRoot.rootName;

		view.render();

		// The editable UI element in DOM is available for sure only after the editor UI view has been rendered.
		// But it can be available earlier if a DOM element has been passed to BalloonEditor.create().
		const editableElement = editable.element;

		// Register the editable UI view in the editor. A single editor instance can aggregate multiple
		// editable areas (roots) but the balloon editor has only one.
		this.setEditableElement( editable.name, editableElement );

		// Let the editable UI element respond to the changes in the global editor focus
		// tracker. It has been added to the same tracker a few lines above but, in reality, there are
		// many focusable areas in the editor, like balloons, toolbars or dropdowns and as long
		// as they have focus, the editable should act like it is focused too (although technically
		// it isn't), e.g. by setting the proper CSS class, visually announcing focus to the user.
		// Doing otherwise will result in editable focus styles disappearing, once e.g. the
		// toolbar gets focused.
		editable.bind( 'isFocused' ).to( this.focusTracker );

		// Bind the editable UI element to the editing view, making it an end– and entry–point
		// of the editor's engine. This is where the engine meets the UI.
		editingView.attachDomRoot( editableElement );

		this._initPlaceholder();
		this.fire( 'ready' );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		const view = this.view;
		const editingView = this.editor.editing.view;

		editingView.detachDomRoot( view.editable.name );
		view.destroy();
	}

	/**
	 * Enable the placeholder text on the editing root, if any was configured.
	 *
	 * @private
	 */
	_initPlaceholder() {
		const editor = this.editor;
		const editingView = editor.editing.view;
		const editingRoot = editingView.document.getRoot();
		const sourceElement = editor.sourceElement;

		const placeholderText = editor.config.get( 'placeholder' ) ||
			sourceElement && sourceElement.tagName.toLowerCase() === 'textarea' && sourceElement.getAttribute( 'placeholder' );

		if ( placeholderText ) {
			enablePlaceholder( {
				view: editingView,
				element: editingRoot,
				text: placeholderText,
				isDirectHost: false,
				keepOnFocus: true
			} );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-balloon/src/ballooneditoruiview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-balloon/ballooneditoruiview
 */



/**
 * Contextual editor UI view. Uses the {@link module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}.
 *
 * @extends module:ui/editorui/editoruiview~EditorUIView
 */
class BalloonEditorUIView extends EditorUIView {
	/**
	 * Creates an instance of the balloon editor UI view.
	 *
	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
	 * @param {module:engine/view/view~View} editingView The editing view instance this view is related to.
	 * @param {HTMLElement} [editableElement] The editable element. If not specified, it will be automatically created by
	 * {@link module:ui/editableui/editableuiview~EditableUIView}. Otherwise, the given element will be used.
	 */
	constructor( locale, editingView, editableElement ) {
		super( locale );

		const t = locale.t;

		/**
		 * The editable UI view.
		 *
		 * @readonly
		 * @member {module:ui/editableui/inline/inlineeditableuiview~InlineEditableUIView}
		 */
		this.editable = new InlineEditableUIView( locale, editingView, editableElement, {
			label: editableView => {
				return t( 'Rich Text Editor. Editing area: %0', editableView.name );
			}
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		this.registerChild( this.editable );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-editor-balloon/src/ballooneditor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module editor-balloon/ballooneditor
 */










/**
 * The {@glink installation/getting-started/predefined-builds#balloon-editor balloon editor}
 * implementation (Medium-like editor).
 * It uses an inline editable and a toolbar based on the {@link module:ui/toolbar/balloon/balloontoolbar~BalloonToolbar}.
 * See the {@glink examples/builds/balloon-editor demo}.
 *
 * In order to create a balloon editor instance, use the static
 * {@link module:editor-balloon/ballooneditor~BalloonEditor.create `BalloonEditor.create()`} method.
 *
 * # Balloon editor and balloon build
 *
 * The balloon editor can be used directly from source (if you installed the
 * [`@ckeditor/ckeditor5-editor-balloon`](https://www.npmjs.com/package/@ckeditor/ckeditor5-editor-balloon) package)
 * but it is also available in the {@glink installation/getting-started/predefined-builds#balloon-editor balloon build}.
 *
 * {@glink installation/getting-started/predefined-builds Builds}
 * are ready-to-use editors with plugins bundled in. When using the editor from
 * source you need to take care of loading all plugins by yourself
 * (through the {@link module:core/editor/editorconfig~EditorConfig#plugins `config.plugins`} option).
 * Using the editor from source gives much better flexibility and allows easier customization.
 *
 * Read more about initializing the editor from source or as a build in
 * {@link module:editor-balloon/ballooneditor~BalloonEditor.create `BalloonEditor.create()`}.
 *
 * @mixes module:core/editor/utils/dataapimixin~DataApiMixin
 * @mixes module:core/editor/utils/elementapimixin~ElementApiMixin
 * @implements module:core/editor/editorwithui~EditorWithUI
 * @extends module:core/editor/editor~Editor
 */
class ballooneditor_BalloonEditor extends Editor {
	/**
	 * Creates an instance of the balloon editor.
	 *
	 * **Note:** do not use the constructor to create editor instances. Use the static
	 * {@link module:editor-balloon/ballooneditor~BalloonEditor.create `BalloonEditor.create()`} method instead.
	 *
	 * @protected
	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
	 * (on which the editor will be initialized) or initial data for the editor. For more information see
	 * {@link module:editor-balloon/ballooneditor~BalloonEditor.create `BalloonEditor.create()`}.
	 * @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
	 */
	constructor( sourceElementOrData, config = {} ) {
		// If both `config.initialData` is set and initial data is passed as the constructor parameter, then throw.
		if ( !lodash_es_isElement( sourceElementOrData ) && config.initialData !== undefined ) {
			// Documented in core/editor/editorconfig.jsdoc.
			// eslint-disable-next-line ckeditor5-rules/ckeditor-error-message
			throw new CKEditorError( 'editor-create-initial-data', null );
		}

		super( config );

		if ( this.config.get( 'initialData' ) === undefined ) {
			this.config.set( 'initialData', ballooneditor_getInitialData( sourceElementOrData ) );
		}

		if ( lodash_es_isElement( sourceElementOrData ) ) {
			this.sourceElement = sourceElementOrData;
			secureSourceElement( this );
		}

		const plugins = this.config.get( 'plugins' );
		plugins.push( BalloonToolbar );

		this.config.set( 'plugins', plugins );

		this.config.define( 'balloonToolbar', this.config.get( 'toolbar' ) );

		this.model.document.createRoot();

		const view = new BalloonEditorUIView( this.locale, this.editing.view, this.sourceElement );
		this.ui = new BalloonEditorUI( this, view );

		attachToForm( this );
	}

	/**
	 * Destroys the editor instance, releasing all resources used by it.
	 *
	 * Updates the original editor element with the data if the
	 * {@link module:core/editor/editorconfig~EditorConfig#updateSourceElementOnDestroy `updateSourceElementOnDestroy`}
	 * configuration option is set to `true`.
	 *
	 * @returns {Promise}
	 */
	destroy() {
		// Cache the data, then destroy.
		// It's safe to assume that the model->view conversion will not work after super.destroy().
		const data = this.getData();

		this.ui.destroy();

		return super.destroy()
			.then( () => {
				if ( this.sourceElement ) {
					this.updateSourceElement( data );
				}
			} );
	}

	/**
	 * Creates a new balloon editor instance.
	 *
	 * There are three general ways how the editor can be initialized.
	 *
	 * # Using an existing DOM element (and loading data from it)
	 *
	 * You can initialize the editor using an existing DOM element:
	 *
	 *		BalloonEditor
	 *			.create( document.querySelector( '#editor' ) )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * The element's content will be used as the editor data and the element will become the editable element.
	 *
	 * # Creating a detached editor
	 *
	 * Alternatively, you can initialize the editor by passing the initial data directly as a string.
	 * In this case, the editor will render an element that must be inserted into the DOM for the editor to work properly:
	 *
	 *		BalloonEditor
	 *			.create( '<p>Hello world!</p>' )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *
	 *				// Initial data was provided so the editor UI element needs to be added manually to the DOM.
	 *				document.body.appendChild( editor.ui.element );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * This lets you dynamically append the editor to your web page whenever it is convenient for you. You may use this method if your
	 * web page content is generated on the client side and the DOM structure is not ready at the moment when you initialize the editor.
	 *
	 * # Using an existing DOM element (and data provided in `config.initialData`)
	 *
	 * You can also mix these two ways by providing a DOM element to be used and passing the initial data through the configuration:
	 *
	 *		BalloonEditor
	 *			.create( document.querySelector( '#editor' ), {
	 *				initialData: '<h2>Initial data</h2><p>Foo bar.</p>'
	 *			} )
	 *			.then( editor => {
	 *				console.log( 'Editor was initialized', editor );
	 *			} )
	 *			.catch( err => {
	 *				console.error( err.stack );
	 *			} );
	 *
	 * This method can be used to initialize the editor on an existing element with the specified content in case if your integration
	 * makes it difficult to set the content of the source element.
	 *
	 * Note that an error will be thrown if you pass the initial data both as the first parameter and also in the configuration.
	 *
	 * # Configuring the editor
	 *
	 * See the {@link module:core/editor/editorconfig~EditorConfig editor configuration documentation} to learn more about
	 * customizing plugins, toolbar and more.
	 *
	 * # Using the editor from source
	 *
	 * The code samples listed in the previous sections of this documentation assume that you are using an
	 * {@glink installation/getting-started/predefined-builds editor build} (for example – `@ckeditor/ckeditor5-build-balloon`).
	 *
	 * If you want to use the balloon editor from source (`@ckeditor/ckeditor5-editor-balloon/src/ballooneditor`),
	 * you need to define the list of
	 * {@link module:core/editor/editorconfig~EditorConfig#plugins plugins to be initialized} and
	 * {@link module:core/editor/editorconfig~EditorConfig#toolbar toolbar items}. Read more about using the editor from
	 * source in the {@glink installation/advanced/alternative-setups/integrating-from-source dedicated guide}.
	 *
	 * @param {HTMLElement|String} sourceElementOrData The DOM element that will be the source for the created editor
	 * or the editor's initial data.
	 *
	 * If a DOM element is passed, its content will be automatically loaded to the editor upon initialization.
	 * The editor data will be set back to the original element once the editor is destroyed only if the
	 * {@link module:core/editor/editorconfig~EditorConfig#updateSourceElementOnDestroy updateSourceElementOnDestroy}
	 * option is set to `true`.
	 *
	 * If the initial data is passed, a detached editor will be created. In this case you need to insert it into the DOM manually.
	 * It is available under the {@link module:editor-balloon/ballooneditorui~BalloonEditorUI#element `editor.ui.element`} property.
	 *
	 * @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
	 * @returns {Promise} A promise resolved once the editor is ready. The promise resolves with the created editor instance.
	 */
	static create( sourceElementOrData, config = {} ) {
		return new Promise( resolve => {
			if ( lodash_es_isElement( sourceElementOrData ) && sourceElementOrData.tagName === 'TEXTAREA' ) {
				// Documented in core/editor/editor.js
				// eslint-disable-next-line ckeditor5-rules/ckeditor-error-message
				throw new CKEditorError( 'editor-wrong-element', null );
			}

			const editor = new this( sourceElementOrData, config );

			resolve(
				editor.initPlugins()
					.then( () => editor.ui.init() )
					.then( () => editor.data.init( editor.config.get( 'initialData' ) ) )
					.then( () => editor.fire( 'ready' ) )
					.then( () => editor )
			);
		} );
	}
}

mix( ballooneditor_BalloonEditor, DataApiMixin );
mix( ballooneditor_BalloonEditor, ElementApiMixin );

function ballooneditor_getInitialData( sourceElementOrData ) {
	return lodash_es_isElement( sourceElementOrData ) ? getDataFromElement( sourceElementOrData ) : sourceElementOrData;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-alignment/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */



/**
 * @module alignment/utils
 */

/**
 * The list of supported alignment options:
 *
 * * `'left'`,
 * * `'right'`,
 * * `'center'`,
 * * `'justify'`
 */
const supportedOptions = [ 'left', 'right', 'center', 'justify' ];

/**
 * Checks whether the passed option is supported by {@link module:alignment/alignmentediting~AlignmentEditing}.
 *
 * @param {String} option The option value to check.
 * @returns {Boolean}
 */
function isSupported( option ) {
	return supportedOptions.includes( option );
}

/**
 * Checks whether alignment is the default one considering the direction
 * of the editor content.
 *
 * @param {String} alignment The name of the alignment to check.
 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
 * @returns {Boolean}
 */
function isDefault( alignment, locale ) {
	// Right now only LTR is supported so the 'left' value is always the default one.

	if ( locale.contentLanguageDirection == 'rtl' ) {
		return alignment === 'right';
	} else {
		return alignment === 'left';
	}
}

/**
 * Brings the configuration to the common form, an array of objects.
 *
 * @param {Array.<String|module:alignment/alignmentediting~AlignmentFormat>} configuredOptions Alignment plugin configuration.
 * @returns {Array.<module:alignment/alignmentediting~AlignmentFormat>} Normalized object holding the configuration.
 */
function normalizeAlignmentOptions( configuredOptions ) {
	const normalizedOptions = configuredOptions
		.map( option => {
			let result;

			if ( typeof option == 'string' ) {
				result = { name: option };
			} else {
				result = option;
			}

			return result;
		} )
		// Remove all unknown options.
		.filter( option => {
			const isNameValid = !!supportedOptions.includes( option.name );
			if ( !isNameValid ) {
				/**
				 * The `name` in one of the `alignment.options` is not recognized.
				 * The available options are: `'left'`, `'right'`, `'center'` and `'justify'`.
				 *
				 * @error alignment-config-name-not-recognized
				 * @param {Object} option Options with unknown value of the `name` property.
				 */
				logWarning( 'alignment-config-name-not-recognized', { option } );
			}

			return isNameValid;
		} );

	const classNameCount = normalizedOptions.filter( option => !!option.className ).length;

	// We either use classes for all styling options or for none.
	if ( classNameCount && classNameCount < normalizedOptions.length ) {
		/**
		 * The `className` property has to be defined for all options once at least one option declares `className`.
		 *
		 * @error alignment-config-classnames-are-missing
		 * @param {Array.<String|module:alignment/alignmentediting~AlignmentFormat>} configuredOptions Contents of `alignment.options`.
		 */
		throw new CKEditorError( 'alignment-config-classnames-are-missing', { configuredOptions } );
	}

	// Validate resulting config.
	normalizedOptions.forEach( ( option, index, allOptions ) => {
		const succeedingOptions = allOptions.slice( index + 1 );
		const nameAlreadyExists = succeedingOptions.some( item => item.name == option.name );

		if ( nameAlreadyExists ) {
			/**
			 * The same `name` in one of the `alignment.options` was already declared.
			 * Each `name` representing one alignment option can be set exactly once.
			 *
			 * @error alignment-config-name-already-defined
			 * @param {Object} option First option that declares given `name`.
			 * @param {Array.<String|module:alignment/alignmentediting~AlignmentFormat>} configuredOptions Contents of `alignment.options`.
			 */
			throw new CKEditorError( 'alignment-config-name-already-defined', { option, configuredOptions } );
		}

		// The `className` property is present. Check for duplicates then.
		if ( option.className ) {
			const classNameAlreadyExists = succeedingOptions.some( item => item.className == option.className );

			if ( classNameAlreadyExists ) {
				/**
				 * The same `className` in one of the `alignment.options` was already declared.
				 *
				 * @error alignment-config-classname-already-defined
				 * @param {Object} option First option that declares given `className`.
				 * @param {Array.<String|module:alignment/alignmentediting~AlignmentFormat>} configuredOptions
				 * Contents of `alignment.options`.
				 */
				throw new CKEditorError( 'alignment-config-classname-already-defined', { option, configuredOptions } );
			}
		}
	} );

	return normalizedOptions;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-alignment/src/alignmentcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module alignment/alignmentcommand
 */






const ALIGNMENT = 'alignment';

/**
 * The alignment command plugin.
 *
 * @extends module:core/command~Command
 */
class AlignmentCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const locale = editor.locale;
		const firstBlock = first_first( this.editor.model.document.selection.getSelectedBlocks() );

		// As first check whether to enable or disable the command as the value will always be false if the command cannot be enabled.
		this.isEnabled = !!firstBlock && this._canBeAligned( firstBlock );

		/**
		 * A value of the current block's alignment.
		 *
		 * @observable
		 * @readonly
		 * @member {String} #value
		 */
		if ( this.isEnabled && firstBlock.hasAttribute( 'alignment' ) ) {
			this.value = firstBlock.getAttribute( 'alignment' );
		} else {
			this.value = locale.contentLanguageDirection === 'rtl' ? 'right' : 'left';
		}
	}

	/**
	 * Executes the command. Applies the alignment `value` to the selected blocks.
	 * If no `value` is passed, the `value` is the default one or it is equal to the currently selected block's alignment attribute,
	 * the command will remove the attribute from the selected blocks.
	 *
	 * @param {Object} [options] Options for the executed command.
	 * @param {String} [options.value] The value to apply.
	 * @fires execute
	 */
	execute( options = {} ) {
		const editor = this.editor;
		const locale = editor.locale;
		const model = editor.model;
		const doc = model.document;

		const value = options.value;

		model.change( writer => {
			// Get only those blocks from selected that can have alignment set
			const blocks = Array.from( doc.selection.getSelectedBlocks() ).filter( block => this._canBeAligned( block ) );
			const currentAlignment = blocks[ 0 ].getAttribute( 'alignment' );

			// Remove alignment attribute if current alignment is:
			// - default (should not be stored in model as it will bloat model data)
			// - equal to currently set
			// - or no value is passed - denotes default alignment.
			const removeAlignment = isDefault( value, locale ) || currentAlignment === value || !value;

			if ( removeAlignment ) {
				removeAlignmentFromSelection( blocks, writer );
			} else {
				setAlignmentOnSelection( blocks, writer, value );
			}
		} );
	}

	/**
	 * Checks whether a block can have alignment set.
	 *
	 * @private
	 * @param {module:engine/model/element~Element} block The block to be checked.
	 * @returns {Boolean}
	 */
	_canBeAligned( block ) {
		return this.editor.model.schema.checkAttribute( block, ALIGNMENT );
	}
}

// Removes the alignment attribute from blocks.
// @private
function removeAlignmentFromSelection( blocks, writer ) {
	for ( const block of blocks ) {
		writer.removeAttribute( ALIGNMENT, block );
	}
}

// Sets the alignment attribute on blocks.
// @private
function setAlignmentOnSelection( blocks, writer, alignment ) {
	for ( const block of blocks ) {
		writer.setAttribute( ALIGNMENT, alignment, block );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-alignment/src/alignmentediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module alignment/alignmentediting
 */






/**
 * The alignment editing feature. It introduces the {@link module:alignment/alignmentcommand~AlignmentCommand command} and adds
 * the `alignment` attribute for block elements in the {@link module:engine/model/model~Model model}.
 * @extends module:core/plugin~Plugin
 */
class AlignmentEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'AlignmentEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'alignment', {
			options: [ ...supportedOptions.map( option => ( { name: option } ) ) ]
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const locale = editor.locale;
		const schema = editor.model.schema;

		const options = normalizeAlignmentOptions( editor.config.get( 'alignment.options' ) );

		// Filter out unsupported options and those that are redundant, e.g. `left` in LTR / `right` in RTL mode.
		const optionsToConvert = options.filter(
			option => isSupported( option.name ) && !isDefault( option.name, locale )
		);

		// Once there is at least one `className` defined, we switch to alignment with classes.
		const shouldUseClasses = optionsToConvert.some( option => !!option.className );

		// Allow alignment attribute on all blocks.
		schema.extend( '$block', { allowAttributes: 'alignment' } );
		editor.model.schema.setAttributeProperties( 'alignment', { isFormatting: true } );

		if ( shouldUseClasses ) {
			editor.conversion.attributeToAttribute( buildClassDefinition( optionsToConvert ) );
		} else {
			// Downcast inline styles.
			editor.conversion.for( 'downcast' ).attributeToAttribute( buildDowncastInlineDefinition( optionsToConvert ) );
		}

		const upcastInlineDefinitions = buildUpcastInlineDefinitions( optionsToConvert );

		// Always upcast from inline styles.
		for ( const definition of upcastInlineDefinitions ) {
			editor.conversion.for( 'upcast' ).attributeToAttribute( definition );
		}

		const upcastCompatibilityDefinitions = buildUpcastCompatibilityDefinitions( optionsToConvert );

		// Always upcast from deprecated `align` attribute.
		for ( const definition of upcastCompatibilityDefinitions ) {
			editor.conversion.for( 'upcast' ).attributeToAttribute( definition );
		}

		editor.commands.add( 'alignment', new AlignmentCommand( editor ) );
	}
}

// Prepare downcast conversion definition for inline alignment styling.
// @private
function buildDowncastInlineDefinition( options ) {
	const definition = {
		model: {
			key: 'alignment',
			values: options.map( option => option.name )
		},
		view: {}
	};

	for ( const { name } of options ) {
		definition.view[ name ] = {
			key: 'style',
			value: {
				'text-align': name
			}
		};
	}

	return definition;
}

// Prepare upcast definitions for inline alignment styles.
// @private
function buildUpcastInlineDefinitions( options ) {
	const definitions = [];

	for ( const { name } of options ) {
		definitions.push( {
			view: {
				key: 'style',
				value: {
					'text-align': name
				}
			},
			model: {
				key: 'alignment',
				value: name
			}
		} );
	}

	return definitions;
}

// Prepare upcast definitions for deprecated `align` attribute.
// @private
function buildUpcastCompatibilityDefinitions( options ) {
	const definitions = [];

	for ( const { name } of options ) {
		definitions.push( {
			view: {
				key: 'align',
				value: name
			},
			model: {
				key: 'alignment',
				value: name
			}
		} );
	}

	return definitions;
}

// Prepare conversion definitions for upcast and downcast alignment with classes.
// @private
function buildClassDefinition( options ) {
	const definition = {
		model: {
			key: 'alignment',
			values: options.map( option => option.name )
		},
		view: {}
	};

	for ( const option of options ) {
		definition.view[ option.name ] = {
			key: 'class',
			value: option.className
		};
	}

	return definition;
}

/**
 * The alignment configuration format descriptor.
 *
 *		const alignmentFormat = {
 *			name: 'right',
 *			className: 'my-align-right-class'
 *		}
 *
 * @typedef {Object} module:alignment/alignmentediting~AlignmentFormat
 *
 * @property {'left'|'right'|'center'|'justify'} name One of the alignment names options.
 *
 * @property {String} className The CSS class used to represent the style in the view.
 * Used to override default, inline styling for alignment.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-alignment/src/alignmentui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module alignment/alignmentui
 */






const iconsMap = new Map( [
	[ 'left', icons.alignLeft ],
	[ 'right', icons.alignRight ],
	[ 'center', icons.alignCenter ],
	[ 'justify', icons.alignJustify ]
] );

/**
 * The default alignment UI plugin.
 *
 * It introduces the `'alignment:left'`, `'alignment:right'`, `'alignment:center'` and `'alignment:justify'` buttons
 * and the `'alignment'` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class AlignmentUI extends plugin_Plugin {
	/**
	 * Returns the localized option titles provided by the plugin.
	 *
	 * The following localized titles corresponding with
	 * {@link module:alignment/alignment~AlignmentConfig#options} are available:
	 *
	 * * `'left'`,
	 * * `'right'`,
	 * * `'center'`,
	 * * `'justify'`.
	 *
	 * @readonly
	 * @type {Object.<String,String>}
	 */
	get localizedOptionTitles() {
		const t = this.editor.t;

		return {
			'left': t( 'Align left' ),
			'right': t( 'Align right' ),
			'center': t( 'Align center' ),
			'justify': t( 'Justify' )
		};
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'AlignmentUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const componentFactory = editor.ui.componentFactory;
		const t = editor.t;
		const options = normalizeAlignmentOptions( editor.config.get( 'alignment.options' ) );

		options
			.map( option => option.name )
			.filter( isSupported )
			.forEach( option => this._addButton( option ) );

		componentFactory.add( 'alignment', locale => {
			const dropdownView = createDropdown( locale );

			// Add existing alignment buttons to dropdown's toolbar.
			const buttons = options.map( option => componentFactory.create( `alignment:${ option.name }` ) );
			addToolbarToDropdown( dropdownView, buttons, { enableActiveItemFocusOnDropdownOpen: true } );

			// Configure dropdown properties an behavior.
			dropdownView.buttonView.set( {
				label: t( 'Text alignment' ),
				tooltip: true
			} );

			dropdownView.toolbarView.isVertical = true;
			dropdownView.toolbarView.ariaLabel = t( 'Text alignment toolbar' );

			dropdownView.extendTemplate( {
				attributes: {
					class: 'ck-alignment-dropdown'
				}
			} );

			// The default icon depends on the direction of the content.
			const defaultIcon = locale.contentLanguageDirection === 'rtl' ? iconsMap.get( 'right' ) : iconsMap.get( 'left' );

			// Change icon to reflect current selection's alignment.
			dropdownView.buttonView.bind( 'icon' ).toMany( buttons, 'isOn', ( ...areActive ) => {
				// Get the index of an active button.
				const index = areActive.findIndex( value => value );

				// If none of the commands is active, display either defaultIcon or the first button's icon.
				if ( index < 0 ) {
					return defaultIcon;
				}

				// Return active button's icon.
				return buttons[ index ].icon;
			} );

			// Enable button if any of the buttons is enabled.
			dropdownView.bind( 'isEnabled' ).toMany( buttons, 'isEnabled', ( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled ) );

			// Focus the editable after executing the command.
			// Overrides a default behaviour where the focus is moved to the dropdown button (#12125).
			this.listenTo( dropdownView, 'execute', () => {
				editor.editing.view.focus();
			} );

			return dropdownView;
		} );
	}

	/**
	 * Helper method for initializing the button and linking it with an appropriate command.
	 *
	 * @private
	 * @param {String} option The name of the alignment option for which the button is added.
	 */
	_addButton( option ) {
		const editor = this.editor;

		editor.ui.componentFactory.add( `alignment:${ option }`, locale => {
			const command = editor.commands.get( 'alignment' );
			const buttonView = new buttonview_ButtonView( locale );

			buttonView.set( {
				label: this.localizedOptionTitles[ option ],
				icon: iconsMap.get( option ),
				tooltip: true,
				isToggleable: true
			} );

			// Bind button model to command.
			buttonView.bind( 'isEnabled' ).to( command );
			buttonView.bind( 'isOn' ).to( command, 'value', value => value === option );

			// Execute command.
			this.listenTo( buttonView, 'execute', () => {
				editor.execute( 'alignment', { value: option } );
				editor.editing.view.focus();
			} );

			return buttonView;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-alignment/src/alignment.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module alignment/alignment
 */






/**
 * The text alignment plugin.
 *
 * For a detailed overview, check the {@glink features/text-alignment Text alignment feature documentation}
 * and the {@glink api/alignment package page}.
 *
 * This is a "glue" plugin which loads the {@link module:alignment/alignmentediting~AlignmentEditing} and
 * {@link module:alignment/alignmentui~AlignmentUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Alignment extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ AlignmentEditing, AlignmentUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Alignment';
	}
}

/**
 * The configuration of the {@link module:alignment/alignment~Alignment alignment feature}.
 *
 * Read more in {@link module:alignment/alignment~AlignmentConfig}.
 *
 * @member {module:alignment/alignment~AlignmentConfig} module:core/editor/editorconfig~EditorConfig#alignment
 */

/**
 * The configuration of the {@link module:alignment/alignment~Alignment alignment feature}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				alignment: {
 *					options: [ 'left', 'right' ]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor configuration options}.
 *
 * @interface AlignmentConfig
 */

/**
 * Available alignment options.
 *
 * The available options are: `'left'`, `'right'`, `'center'` and `'justify'`. Other values are ignored.
 *
 * **Note:** It is recommended to always use `'left'` or `'right'` as these are default values which the user should
 * normally be able to choose depending on the
 * {@glink features/ui-language#setting-the-language-of-the-content language of the editor content}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				alignment: {
 *					options: [ 'left', 'right' ]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * By default the alignment is set inline using the `text-align` CSS property. To further customize the alignment,
 * you can provide names of classes for each alignment option using the `className` property.
 *
 * **Note:** Once you define the `className` property for one option, you need to specify it for all other options.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				alignment: {
 *					options: [
 *						{ name: 'left', className: 'my-align-left' },
 *						{ name: 'right', className: 'my-align-right' }
 *					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See the demo of {@glink features/text-alignment#configuring-alignment-options custom alignment options}.
 *
 * @member {Array.<String|module:alignment/alignmentediting~AlignmentFormat>} module:alignment/alignment~AlignmentConfig#options
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/utils/changebuffer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Change buffer allows to group atomic changes (like characters that have been typed) into
 * {@link module:engine/model/batch~Batch batches}.
 *
 * Batches represent single undo steps, hence changes added to one single batch are undone together.
 *
 * The buffer has a configurable limit of atomic changes that it can accommodate. After the limit was
 * exceeded (see {@link ~ChangeBuffer#input}), a new batch is created in {@link ~ChangeBuffer#batch}.
 *
 * To use the change buffer you need to let it know about the number of changes that were added to the batch:
 *
 *		const buffer = new ChangeBuffer( model, LIMIT );
 *
 *		// Later on in your feature:
 *		buffer.batch.insert( pos, insertedCharacters );
 *		buffer.input( insertedCharacters.length );
 *
 */
class ChangeBuffer {
    /**
     * Creates a new instance of the change buffer.
     *
     * @param {module:engine/model/model~Model} model
     * @param {Number} [limit=20] The maximum number of atomic changes which can be contained in one batch.
     */
    constructor(model, limit = 20) {
        this._batch = null;
        /**
         * The model instance.
         *
         * @readonly
         * @member {module:engine/model/model~Model} #model
         */
        this.model = model;
        /**
         * The number of atomic changes in the buffer. Once it exceeds the {@link #limit},
         * the {@link #batch batch} is set to a new one.
         *
         * @readonly
         * @member {Number} #size
         */
        this._size = 0;
        /**
         * The maximum number of atomic changes which can be contained in one batch.
         *
         * @readonly
         * @member {Number} #limit
         */
        this.limit = limit;
        /**
         * Whether the buffer is locked. A locked buffer cannot be reset unless it gets unlocked.
         *
         * @readonly
         * @member {Boolean} #isLocked
         */
        this._isLocked = false;
        // The function to be called in order to notify the buffer about batches which appeared in the document.
        // The callback will check whether it is a new batch and in that case the buffer will be flushed.
        //
        // The reason why the buffer needs to be flushed whenever a new batch appears is that the changes added afterwards
        // should be added to a new batch. For instance, when the user types, then inserts an image, and then types again,
        // the characters typed after inserting the image should be added to a different batch than the characters typed before.
        this._changeCallback = (evt, batch) => {
            if (batch.isLocal && batch.isUndoable && batch !== this._batch) {
                this._reset(true);
            }
        };
        this._selectionChangeCallback = () => {
            this._reset();
        };
        this.model.document.on('change', this._changeCallback);
        this.model.document.selection.on('change:range', this._selectionChangeCallback);
        this.model.document.selection.on('change:attribute', this._selectionChangeCallback);
        /**
         * The current batch instance.
         *
         * @private
         * @member #_batch
         */
        /**
         * The callback to document the change event which later needs to be removed.
         *
         * @private
         * @member #_changeCallback
         */
        /**
         * The callback to document selection `change:attribute` and `change:range` events which resets the buffer.
         *
         * @private
         * @member #_selectionChangeCallback
         */
    }
    /**
     * The current batch to which a feature should add its operations. Once the {@link #size}
     * is reached or exceeds the {@link #limit}, the batch is set to a new instance and the size is reset.
     *
     * @type {module:engine/model/batch~Batch}
     */
    get batch() {
        if (!this._batch) {
            this._batch = this.model.createBatch({ isTyping: true });
        }
        return this._batch;
    }
    /**
     * The number of atomic changes in the buffer. Once it exceeds the {@link #limit},
     * the {@link #batch batch} is set to a new one.
     */
    get size() {
        return this._size;
    }
    /**
     * The input number of changes into the buffer. Once the {@link #size} is
     * reached or exceeds the {@link #limit}, the batch is set to a new instance and the size is reset.
     *
     * @param {Number} changeCount The number of atomic changes to input.
     */
    input(changeCount) {
        this._size += changeCount;
        if (this._size >= this.limit) {
            this._reset(true);
        }
    }
    /**
     * Whether the buffer is locked. A locked buffer cannot be reset unless it gets unlocked.
     */
    get isLocked() {
        return this._isLocked;
    }
    /**
     * Locks the buffer.
     */
    lock() {
        this._isLocked = true;
    }
    /**
     * Unlocks the buffer.
     */
    unlock() {
        this._isLocked = false;
    }
    /**
     * Destroys the buffer.
     */
    destroy() {
        this.model.document.off('change', this._changeCallback);
        this.model.document.selection.off('change:range', this._selectionChangeCallback);
        this.model.document.selection.off('change:attribute', this._selectionChangeCallback);
    }
    /**
     * Resets the change buffer.
     *
     * @private
     * @param {Boolean} [ignoreLock] Whether internal lock {@link #isLocked} should be ignored.
     */
    _reset(ignoreLock = false) {
        if (!this.isLocked || ignoreLock) {
            this._batch = null;
            this._size = 0;
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/inserttextcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/inserttextcommand
 */


/**
 * The insert text command. Used by the {@link module:typing/input~Input input feature} to handle typing.
 *
 * @extends module:core/command~Command
 */
class InsertTextCommand extends command_Command {
    /**
     * Creates an instance of the command.
     *
     * @param {module:core/editor/editor~Editor} editor
     * @param {Number} undoStepSize The maximum number of atomic changes
     * which can be contained in one batch in the command buffer.
     */
    constructor(editor, undoStepSize) {
        super(editor);
        /**
         * Typing's change buffer used to group subsequent changes into batches.
         *
         * @readonly
         * @private
         * @member {module:typing/utils/changebuffer~ChangeBuffer} #_buffer
         */
        this._buffer = new ChangeBuffer(editor.model, undoStepSize);
    }
    /**
     * The current change buffer.
     *
     * @type {module:typing/utils/changebuffer~ChangeBuffer}
     */
    get buffer() {
        return this._buffer;
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this._buffer.destroy();
    }
    /**
     * Executes the input command. It replaces the content within the given range with the given text.
     * Replacing is a two step process, first the content within the range is removed and then the new text is inserted
     * at the beginning of the range (which after the removal is a collapsed range).
     *
     * @fires execute
     * @param {Object} [options] The command options.
     * @param {String} [options.text=''] The text to be inserted.
     * @param {module:engine/model/selection~Selection} [options.selection] The selection in which the text is inserted.
     * Inserting a text into a selection deletes the current content within selection ranges. If the selection is not specified,
     * the current selection in the model will be used instead.
     * // TODO note that those 2 options are exclusive (either selection or range)
     * @param {module:engine/model/range~Range} [options.range] The range in which the text is inserted. Defaults
     * to the first range in the current selection.
     * @param {module:engine/model/range~Range} [options.resultRange] The range where the selection
     * should be placed after the insertion. If not specified, the selection will be placed right after
     * the inserted text.
     */
    execute(options = {}) {
        const model = this.editor.model;
        const doc = model.document;
        const text = options.text || '';
        const textInsertions = text.length;
        let selection = doc.selection;
        if (options.selection) {
            selection = options.selection;
        }
        else if (options.range) {
            selection = model.createSelection(options.range);
        }
        const resultRange = options.resultRange;
        model.enqueueChange(this._buffer.batch, writer => {
            this._buffer.lock();
            model.deleteContent(selection);
            if (text) {
                model.insertContent(writer.createText(text, doc.selection.getAttributes()), selection);
            }
            if (resultRange) {
                writer.setSelection(resultRange);
            }
            else if (!selection.is('documentSelection')) {
                writer.setSelection(selection);
            }
            this._buffer.unlock();
            this._buffer.input(textInsertions);
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/inserttextobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/inserttextobserver
 */




const TYPING_INPUT_TYPES = [
    // For collapsed range:
    //	- This one is a regular typing (all browsers, all systems).
    //	- This one is used by Chrome when typing accented letter – 2nd step when the user selects the accent (Mac).
    // For non-collapsed range:
    //	- This one is used by Chrome when typing accented letter – when the selection box first appears (Mac).
    //	- This one is used by Safari when accepting spell check suggestions from the context menu (Mac).
    'insertText',
    // This one is used by Safari when typing accented letter (Mac).
    // This one is used by Safari when accepting spell check suggestions from the autocorrection pop-up (Mac).
    'insertReplacementText'
];
/**
 * Text insertion observer introduces the {@link module:engine/view/document~Document#event:insertText} event.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class InsertTextObserver extends Observer {
    /**
     * @inheritDoc
     */
    constructor(view) {
        super(view);
        // On Android composition events should immediately be applied to the model. Rendering is not disabled.
        // On non-Android the model is updated only on composition end.
        // On Android we can't rely on composition start/end to update model.
        if (src_env.isAndroid) {
            TYPING_INPUT_TYPES.push('insertCompositionText');
        }
        const viewDocument = view.document;
        viewDocument.on('beforeinput', (evt, data) => {
            if (!this.isEnabled) {
                return;
            }
            const { data: text, targetRanges, inputType, domEvent } = data;
            if (!TYPING_INPUT_TYPES.includes(inputType)) {
                return;
            }
            const eventInfo = new EventInfo(viewDocument, 'insertText');
            viewDocument.fire(eventInfo, new DomEventData(view, domEvent, {
                text,
                selection: view.createSelection(targetRanges)
            }));
            // Stop the beforeinput event if `delete` event was stopped.
            // https://github.com/ckeditor/ckeditor5/issues/753
            if (eventInfo.stop.called) {
                evt.stop();
            }
        });
        // Note: The priority must be lower than the CompositionObserver handler to call it after the renderer is unblocked.
        viewDocument.on('compositionend', (evt, { data, domEvent }) => {
            // On Android composition events are immediately applied to the model.
            // On non-Android the model is updated only on composition end.
            // On Android we can't rely on composition start/end to update model.
            if (!this.isEnabled || src_env.isAndroid) {
                return;
            }
            // In case of aborted composition.
            if (!data) {
                return;
            }
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.log( `%c[InsertTextObserver]%c Fire insertText event, text: ${ JSON.stringify( data ) }`,
            // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green;', ''
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            // How do we know where to insert the composed text?
            // The selection observer is blocked and the view is not updated with the composition changes.
            // There were three options:
            //   - Store the selection on `compositionstart` and use it now. This wouldn't work in RTC
            //     where the view would change and the stored selection might get incorrect.
            //     We'd need to fallback to the current view selection anyway.
            //   - Use the current view selection. This is a bit weird and non-intuitive because
            //     this isn't necessarily the selection on which the user started composing.
            //     We cannot even know whether it's still collapsed (there might be some weird
            //     editor feature that changed it in unpredictable ways for us). But it's by far
            //     the simplest solution and should be stable (the selection is definitely correct)
            //     and probably mostly predictable (features usually don't modify the selection
            //     unless called explicitly by the user).
            //   - Try to follow it from the `beforeinput` events. This would be really complex as each
            //     `beforeinput` would come with just the range it's changing and we'd need to calculate that.
            // We decided to go with the 2nd option for its simplicity and stability.
            viewDocument.fire('insertText', new DomEventData(view, domEvent, {
                text: data,
                selection: viewDocument.selection
            }));
        }, { priority: 'lowest' });
    }
    /**
     * @inheritDoc
     */
    observe() { }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/input.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/input
 */




// Import config.typing declaration.

/**
 * Handles text input coming from the keyboard or other input methods.
 *
 * @extends module:core/plugin~Plugin
 */
class Input extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Input';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const model = editor.model;
        const view = editor.editing.view;
        const modelSelection = model.document.selection;
        view.addObserver(InsertTextObserver);
        // TODO The above default configuration value should be defined using editor.config.define() once it's fixed.
        const insertTextCommand = new InsertTextCommand(editor, editor.config.get('typing.undoStep') || 20);
        // Register `insertText` command and add `input` command as an alias for backward compatibility.
        editor.commands.add('insertText', insertTextCommand);
        editor.commands.add('input', insertTextCommand);
        this.listenTo(view.document, 'insertText', (evt, data) => {
            // Rendering is disabled while composing so prevent events that will be rendered by the engine
            // and should not be applied by the browser.
            if (!view.document.isComposing) {
                data.preventDefault();
            }
            const { text, selection: viewSelection, resultRange: viewResultRange } = data;
            // If view selection was specified, translate it to model selection.
            const modelRanges = Array.from(viewSelection.getRanges()).map(viewRange => {
                return editor.editing.mapper.toModelRange(viewRange);
            });
            let insertText = text;
            // Typing in English on Android is firing composition events for the whole typed word.
            // We need to check the target range text to only apply the difference.
            if (src_env.isAndroid) {
                const selectedText = Array.from(modelRanges[0].getItems()).reduce((rangeText, node) => {
                    return rangeText + (node.is('$textProxy') ? node.data : '');
                }, '');
                if (selectedText) {
                    if (selectedText.length <= insertText.length) {
                        if (insertText.startsWith(selectedText)) {
                            insertText = insertText.substring(selectedText.length);
                            modelRanges[0].start = modelRanges[0].start.getShiftedBy(selectedText.length);
                        }
                    }
                    else {
                        if (selectedText.startsWith(insertText)) {
                            // TODO this should be mapped as delete?
                            modelRanges[0].start = modelRanges[0].start.getShiftedBy(insertText.length);
                            insertText = '';
                        }
                    }
                }
            }
            const insertTextCommandData = {
                text: insertText,
                selection: model.createSelection(modelRanges)
            };
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.log( '%c[Input]%c Execute insertText:',
            // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green;', '',
            // @if CK_DEBUG_TYPING // 		insertText,
            // @if CK_DEBUG_TYPING // 		`[${ modelRanges[ 0 ].start.path }]-[${ modelRanges[ 0 ].end.path }]`
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            if (viewResultRange) {
                insertTextCommandData.resultRange = editor.editing.mapper.toModelRange(viewResultRange);
            }
            editor.execute('insertText', insertTextCommandData);
        });
        if (src_env.isAndroid) {
            // On Android with English keyboard, the composition starts just by putting caret
            // at the word end or by selecting a table column. This is not a real composition started.
            // Trigger delete content on first composition key pressed.
            this.listenTo(view.document, 'keydown', (evt, data) => {
                if (modelSelection.isCollapsed || data.keyCode != 229 || !view.document.isComposing) {
                    return;
                }
                // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
                // @if CK_DEBUG_TYPING // 	console.log( '%c[Input]%c KeyDown 229 -> model.deleteContent()',
                // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green;', '',
                // @if CK_DEBUG_TYPING // 		`[${ modelSelection.getFirstPosition().path }]-[${ modelSelection.getLastPosition().path }]`
                // @if CK_DEBUG_TYPING // 	);
                // @if CK_DEBUG_TYPING // }
                deleteSelectionContent(model, insertTextCommand);
            });
        }
        else {
            // Note: The priority must precede the CompositionObserver handler to call it before
            // the renderer is blocked, because we want to render this change.
            this.listenTo(view.document, 'compositionstart', () => {
                if (modelSelection.isCollapsed) {
                    return;
                }
                // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
                // @if CK_DEBUG_TYPING // 	console.log( '%c[Input]%c Composition start -> model.deleteContent()',
                // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green;', '',
                // @if CK_DEBUG_TYPING // 		`[${ modelSelection.getFirstPosition().path }]-[${ modelSelection.getLastPosition().path }]`
                // @if CK_DEBUG_TYPING // 	);
                // @if CK_DEBUG_TYPING // }
                deleteSelectionContent(model, insertTextCommand);
            });
        }
    }
}
function deleteSelectionContent(model, insertTextCommand) {
    // By relying on the state of the input command we allow disabling the entire input easily
    // by just disabling the input command. We could’ve used here the delete command but that
    // would mean requiring the delete feature which would block loading one without the other.
    // We could also check the editor.isReadOnly property, but that wouldn't allow to block
    // the input without blocking other features.
    if (!insertTextCommand.isEnabled) {
        return;
    }
    const buffer = insertTextCommand.buffer;
    buffer.lock();
    model.enqueueChange(buffer.batch, () => {
        model.deleteContent(model.document.selection);
    });
    buffer.unlock();
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/deletecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/deletecommand
 */



// Import config.typing declaration.

/**
 * The delete command. Used by the {@link module:typing/delete~Delete delete feature} to handle the <kbd>Delete</kbd> and
 * <kbd>Backspace</kbd> keys.
 *
 * @extends module:core/command~Command
 */
class DeleteCommand extends command_Command {
    /**
     * Creates an instance of the command.
     *
     * @param {module:core/editor/editor~Editor} editor
     * @param {'forward'|'backward'} direction The directionality of the delete describing in what direction it
     * should consume the content when the selection is collapsed.
     */
    constructor(editor, direction) {
        super(editor);
        /**
         * The directionality of the delete describing in what direction it should
         * consume the content when the selection is collapsed.
         *
         * @readonly
         * @member {'forward'|'backward'} #direction
         */
        this.direction = direction;
        /**
         * Delete's change buffer used to group subsequent changes into batches.
         *
         * @readonly
         * @private
         * @type {module:typing/utils/changebuffer~ChangeBuffer}
         */
        this._buffer = new ChangeBuffer(editor.model, editor.config.get('typing.undoStep'));
    }
    /**
     * The current change buffer.
     *
     * @type {module:typing/utils/changebuffer~ChangeBuffer}
     */
    get buffer() {
        return this._buffer;
    }
    /**
     * Executes the delete command. Depending on whether the selection is collapsed or not, deletes its content
     * or a piece of content in the {@link #direction defined direction}.
     *
     * @fires execute
     * @param {Object} [options] The command options.
     * @param {'character'|'codePoint'|'word'} [options.unit='character']
     * See {@link module:engine/model/utils/modifyselection~modifySelection}'s options.
     * @param {Number} [options.sequence=1] A number describing which subsequent delete event it is without the key being released.
     * See the {@link module:engine/view/document~Document#event:delete} event data.
     * @param {module:engine/model/selection~Selection} [options.selection] Selection to remove. If not set, current model selection
     * will be used.
     */
    execute(options = {}) {
        const model = this.editor.model;
        const doc = model.document;
        model.enqueueChange(this._buffer.batch, writer => {
            this._buffer.lock();
            const selection = writer.createSelection(options.selection || doc.selection);
            const sequence = options.sequence || 1;
            // Do not replace the whole selected content if selection was collapsed.
            // This prevents such situation:
            //
            // <h1></h1><p>[]</p>	-->  <h1>[</h1><p>]</p> 		-->  <p></p>
            // starting content		-->   after `modifySelection`	-->  after `deleteContent`.
            const doNotResetEntireContent = selection.isCollapsed;
            // Try to extend the selection in the specified direction.
            if (selection.isCollapsed) {
                model.modifySelection(selection, {
                    direction: this.direction,
                    unit: options.unit,
                    treatEmojiAsSingleUnit: true
                });
            }
            // Check if deleting in an empty editor. See #61.
            if (this._shouldEntireContentBeReplacedWithParagraph(sequence)) {
                this._replaceEntireContentWithParagraph(writer);
                return;
            }
            // Check if deleting in the first empty block.
            // See https://github.com/ckeditor/ckeditor5/issues/8137.
            if (this._shouldReplaceFirstBlockWithParagraph(selection, sequence)) {
                this.editor.execute('paragraph', { selection });
                return;
            }
            // If selection is still collapsed, then there's nothing to delete.
            if (selection.isCollapsed) {
                return;
            }
            let changeCount = 0;
            selection.getFirstRange().getMinimalFlatRanges().forEach(range => {
                changeCount += count(range.getWalker({ singleCharacters: true, ignoreElementEnd: true, shallow: true }));
            });
            // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
            // @if CK_DEBUG_TYPING // 	console.log( '%c[DeleteCommand]%c Delete content',
            // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green;', '',
            // @if CK_DEBUG_TYPING // 		`[${ selection.getFirstPosition().path }]-[${ selection.getLastPosition().path }]`, options
            // @if CK_DEBUG_TYPING // 	);
            // @if CK_DEBUG_TYPING // }
            model.deleteContent(selection, {
                doNotResetEntireContent,
                direction: this.direction
            });
            this._buffer.input(changeCount);
            writer.setSelection(selection);
            this._buffer.unlock();
        });
    }
    /**
     * If the user keeps <kbd>Backspace</kbd> or <kbd>Delete</kbd> key pressed, the content of the current
     * editable will be cleared. However, this will not yet lead to resetting the remaining block to a paragraph
     * (which happens e.g. when the user does <kbd>Ctrl</kbd> + <kbd>A</kbd>, <kbd>Backspace</kbd>).
     *
     * But, if the user pressed the key in an empty editable for the first time,
     * we want to replace the entire content with a paragraph if:
     *
     * * the current limit element is empty,
     * * the paragraph is allowed in the limit element,
     * * the limit doesn't already have a paragraph inside.
     *
     * See https://github.com/ckeditor/ckeditor5-typing/issues/61.
     *
     * @private
     * @param {Number} sequence A number describing which subsequent delete event it is without the key being released.
     * @returns {Boolean}
     */
    _shouldEntireContentBeReplacedWithParagraph(sequence) {
        // Does nothing if user pressed and held the "Backspace" or "Delete" key.
        if (sequence > 1) {
            return false;
        }
        const model = this.editor.model;
        const doc = model.document;
        const selection = doc.selection;
        const limitElement = model.schema.getLimitElement(selection);
        // If a collapsed selection contains the whole content it means that the content is empty
        // (from the user perspective).
        const limitElementIsEmpty = selection.isCollapsed && selection.containsEntireContent(limitElement);
        if (!limitElementIsEmpty) {
            return false;
        }
        if (!model.schema.checkChild(limitElement, 'paragraph')) {
            return false;
        }
        const limitElementFirstChild = limitElement.getChild(0);
        // Does nothing if the limit element already contains only a paragraph.
        // We ignore the case when paragraph might have some inline elements (<p><inlineWidget>[]</inlineWidget></p>)
        // because we don't support such cases yet and it's unclear whether inlineWidget shouldn't be a limit itself.
        if (limitElementFirstChild && limitElementFirstChild.is('element', 'paragraph')) {
            return false;
        }
        return true;
    }
    /**
     * The entire content is replaced with the paragraph. Selection is moved inside the paragraph.
     *
     * @private
     * @param {module:engine/model/writer~Writer} writer The model writer.
     */
    _replaceEntireContentWithParagraph(writer) {
        const model = this.editor.model;
        const doc = model.document;
        const selection = doc.selection;
        const limitElement = model.schema.getLimitElement(selection);
        const paragraph = writer.createElement('paragraph');
        writer.remove(writer.createRangeIn(limitElement));
        writer.insert(paragraph, limitElement);
        writer.setSelection(paragraph, 0);
    }
    /**
     * Checks if the selection is inside an empty element that is the first child of the limit element
     * and should be replaced with a paragraph.
     *
     * @private
     * @param {module:engine/model/selection~Selection} selection The selection.
     * @param {Number} sequence A number describing which subsequent delete event it is without the key being released.
     * @returns {Boolean}
     */
    _shouldReplaceFirstBlockWithParagraph(selection, sequence) {
        const model = this.editor.model;
        // Does nothing if user pressed and held the "Backspace" key or it was a "Delete" button.
        if (sequence > 1 || this.direction != 'backward') {
            return false;
        }
        if (!selection.isCollapsed) {
            return false;
        }
        const position = selection.getFirstPosition();
        const limitElement = model.schema.getLimitElement(position);
        const limitElementFirstChild = limitElement.getChild(0);
        // Only elements that are direct children of the limit element can be replaced.
        // Unwrapping from a block quote should be handled in a dedicated feature.
        if (position.parent != limitElementFirstChild) {
            return false;
        }
        // A block should be replaced only if it was empty.
        if (!selection.containsEntireContent(limitElementFirstChild)) {
            return false;
        }
        // Replace with a paragraph only if it's allowed there.
        if (!model.schema.checkChild(limitElement, 'paragraph')) {
            return false;
        }
        // Does nothing if the limit element already contains only a paragraph.
        if (limitElementFirstChild.name == 'paragraph') {
            return false;
        }
        return true;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/deleteobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/deleteobserver
 */




const DELETE_CHARACTER = 'character';
const DELETE_WORD = 'word';
const DELETE_CODE_POINT = 'codePoint';
const DELETE_SELECTION = 'selection';
const DELETE_BACKWARD = 'backward';
const DELETE_FORWARD = 'forward';
const DELETE_EVENT_TYPES = {
    // --------------------------------------- Backward delete types -----------------------------------------------------
    // This happens in Safari on Mac when some content is selected and Ctrl + K is pressed.
    deleteContent: {
        unit: DELETE_SELECTION,
        // According to the Input Events Level 2 spec, this delete type has no direction
        // but to keep things simple, let's default to backward.
        direction: DELETE_BACKWARD
    },
    // Chrome and Safari on Mac: Backspace or Ctrl + H
    deleteContentBackward: {
        // This kind of deletions must be done on the code point-level instead of target range provided by the DOM beforeinput event.
        // Take for instance "👨‍👩‍👧‍👧", it equals:
        //
        //	* [ "👨", "ZERO WIDTH JOINER", "👩", "ZERO WIDTH JOINER", "👧", "ZERO WIDTH JOINER", "👧" ]
        //	* or simply "\u{1F468}\u200D\u{1F469}\u200D\u{1F467}\u200D\u{1F467}"
        //
        // The range provided by the browser would cause the entire multi-byte grapheme to disappear while the user
        // intention when deleting backwards ("👨‍👩‍👧‍👧[]", then backspace) is gradual "decomposition" (first to "👨‍👩‍👧‍[]",
        // then to "👨‍👩‍[]", etc.).
        //
        //	* "👨‍👩‍👧‍👧[]" + backward delete (by code point)  -> results in "👨‍👩‍👧[]", removed the last "👧" 👍
        //	* "👨‍👩‍👧‍👧[]" + backward delete (by character)  -> results in "[]", removed the whole grapheme 👎
        //
        // Deleting by code-point is simply a better UX. See "deleteContentForward" to learn more.
        unit: DELETE_CODE_POINT,
        direction: DELETE_BACKWARD
    },
    // On Mac: Option + Backspace.
    // On iOS: Hold the backspace for a while and the whole words will start to disappear.
    deleteWordBackward: {
        unit: DELETE_WORD,
        direction: DELETE_BACKWARD
    },
    // Safari on Mac: Cmd + Backspace
    deleteHardLineBackward: {
        unit: DELETE_SELECTION,
        direction: DELETE_BACKWARD
    },
    // Chrome on Mac: Cmd + Backspace.
    deleteSoftLineBackward: {
        unit: DELETE_SELECTION,
        direction: DELETE_BACKWARD
    },
    // --------------------------------------- Forward delete types -----------------------------------------------------
    // Chrome on Mac: Fn + Backspace or Ctrl + D
    // Safari on Mac: Ctrl + K or Ctrl + D
    deleteContentForward: {
        // Unlike backward delete, this delete must be performed by character instead of by code point, which
        // provides the best UX for working with accented letters.
        // Take, for example "b̂" ("\u0062\u0302", or [ "LATIN SMALL LETTER B", "COMBINING CIRCUMFLEX ACCENT" ]):
        //
        //	* "b̂[]" + backward delete (by code point)  -> results in "b[]", removed the combining mark 👍
        //	* "[]b̂" + forward delete (by code point)   -> results in "[]^", a bare combining mark does that not make sense when alone 👎
        //	* "[]b̂" + forward delete (by character)    -> results in "[]", removed both "b" and the combining mark 👍
        //
        // See: "deleteContentBackward" to learn more.
        unit: DELETE_CHARACTER,
        direction: DELETE_FORWARD
    },
    // On Mac: Fn + Option + Backspace.
    deleteWordForward: {
        unit: DELETE_WORD,
        direction: DELETE_FORWARD
    },
    // Chrome on Mac: Ctrl + K (you have to disable the Link plugin first, though, because it uses the same keystroke)
    // This is weird that it does not work in Safari on Mac despite being listed in the official shortcuts listing
    // on Apple's webpage.
    deleteHardLineForward: {
        unit: DELETE_SELECTION,
        direction: DELETE_FORWARD
    },
    // At this moment there is no known way to trigger this event type but let's keep it for the symmetry with
    // deleteSoftLineBackward.
    deleteSoftLineForward: {
        unit: DELETE_SELECTION,
        direction: DELETE_FORWARD
    }
};
/**
 * Delete observer introduces the {@link module:engine/view/document~Document#event:delete} event.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class DeleteObserver extends Observer {
    /**
     * @inheritDoc
     */
    constructor(view) {
        super(view);
        const document = view.document;
        // It matters how many subsequent deletions were made, e.g. when the backspace key was pressed and held
        // by the user for some time. For instance, if such scenario ocurred and the heading the selection was
        // anchored to was the only content of the editor, it will not be converted into a paragraph (the user
        // wanted to clean it up, not remove it, it's about UX). Check out the DeleteCommand implementation to learn more.
        //
        // Fun fact: Safari on Mac won't fire beforeinput for backspace in an empty heading (only content).
        let sequence = 0;
        document.on('keydown', () => {
            sequence++;
        });
        document.on('keyup', () => {
            sequence = 0;
        });
        document.on('beforeinput', (evt, data) => {
            if (!this.isEnabled) {
                return;
            }
            const { targetRanges, domEvent, inputType } = data;
            const deleteEventSpec = DELETE_EVENT_TYPES[inputType];
            if (!deleteEventSpec) {
                return;
            }
            const deleteData = {
                direction: deleteEventSpec.direction,
                unit: deleteEventSpec.unit,
                sequence
            };
            if (deleteData.unit == DELETE_SELECTION) {
                deleteData.selectionToRemove = view.createSelection(targetRanges[0]);
            }
            // The default deletion unit for deleteContentBackward is a single code point
            // but on Android it sometimes passes a wider target range, so we need to change
            // the unit of deletion to include the whole range to be removed and not a single code point.
            if (src_env.isAndroid && inputType === 'deleteContentBackward') {
                // On Android, deleteContentBackward has sequence 1 by default.
                deleteData.sequence = 1;
                // IME wants more than a single character to be removed.
                if (targetRanges.length == 1 && (targetRanges[0].start.parent != targetRanges[0].end.parent ||
                    targetRanges[0].start.offset + 1 != targetRanges[0].end.offset)) {
                    deleteData.unit = DELETE_SELECTION;
                    deleteData.selectionToRemove = view.createSelection(targetRanges);
                }
            }
            const eventInfo = new BubblingEventInfo(document, 'delete', targetRanges[0]);
            document.fire(eventInfo, new DomEventData(view, domEvent, deleteData));
            // Stop the beforeinput event if `delete` event was stopped.
            // https://github.com/ckeditor/ckeditor5/issues/753
            if (eventInfo.stop.called) {
                evt.stop();
            }
        });
        // TODO: to be removed when https://bugs.chromium.org/p/chromium/issues/detail?id=1365311 is solved.
        if (src_env.isBlink) {
            enableChromeWorkaround(this);
        }
    }
    /**
     * @inheritDoc
     */
    observe() { }
}
// Enables workaround for the issue https://github.com/ckeditor/ckeditor5/issues/11904.
function enableChromeWorkaround(observer) {
    const view = observer.view;
    const document = view.document;
    let pressedKeyCode = null;
    let beforeInputReceived = false;
    document.on('keydown', (evt, { keyCode }) => {
        pressedKeyCode = keyCode;
        beforeInputReceived = false;
    });
    document.on('keyup', (evt, { keyCode, domEvent }) => {
        const selection = document.selection;
        const shouldFireDeleteEvent = observer.isEnabled &&
            keyCode == pressedKeyCode &&
            isDeleteKeyCode(keyCode) &&
            !selection.isCollapsed &&
            !beforeInputReceived;
        pressedKeyCode = null;
        if (shouldFireDeleteEvent) {
            const targetRange = selection.getFirstRange();
            const eventInfo = new BubblingEventInfo(document, 'delete', targetRange);
            const deleteData = {
                unit: DELETE_SELECTION,
                direction: getDeleteDirection(keyCode),
                selectionToRemove: selection
            };
            document.fire(eventInfo, new DomEventData(view, domEvent, deleteData));
        }
    });
    document.on('beforeinput', (evt, { inputType }) => {
        const deleteEventSpec = DELETE_EVENT_TYPES[inputType];
        const isMatchingBeforeInput = isDeleteKeyCode(pressedKeyCode) &&
            deleteEventSpec &&
            deleteEventSpec.direction == getDeleteDirection(pressedKeyCode);
        if (isMatchingBeforeInput) {
            beforeInputReceived = true;
        }
    });
    document.on('beforeinput', (evt, { inputType, data }) => {
        const shouldIgnoreBeforeInput = pressedKeyCode == keyCodes["delete"] &&
            inputType == 'insertText' &&
            data == '\x7f'; // Delete character :P
        if (shouldIgnoreBeforeInput) {
            evt.stop();
        }
    }, { priority: 'high' });
    function isDeleteKeyCode(keyCode) {
        return keyCode == keyCodes.backspace || keyCode == keyCodes["delete"];
    }
    function getDeleteDirection(keyCode) {
        return keyCode == keyCodes.backspace ? DELETE_BACKWARD : DELETE_FORWARD;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/delete.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/delete
 */



/**
 * The delete and backspace feature. Handles keys such as <kbd>Delete</kbd> and <kbd>Backspace</kbd>, other
 * keystrokes and user actions that result in deleting content in the editor.
 *
 * @extends module:core/plugin~Plugin
 */
class delete_Delete extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Delete';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        const modelDocument = editor.model.document;
        view.addObserver(DeleteObserver);
        this._undoOnBackspace = false;
        const deleteForwardCommand = new DeleteCommand(editor, 'forward');
        // Register `deleteForward` command and add `forwardDelete` command as an alias for backward compatibility.
        editor.commands.add('deleteForward', deleteForwardCommand);
        editor.commands.add('forwardDelete', deleteForwardCommand);
        editor.commands.add('delete', new DeleteCommand(editor, 'backward'));
        this.listenTo(viewDocument, 'delete', (evt, data) => {
            // When not in composition, we handle the action, so prevent the default one.
            // When in composition, it's the browser who modify the DOM (renderer is disabled).
            if (!viewDocument.isComposing) {
                data.preventDefault();
            }
            const { direction, sequence, selectionToRemove, unit } = data;
            const commandName = direction === 'forward' ? 'deleteForward' : 'delete';
            const commandData = { sequence };
            if (unit == 'selection') {
                const modelRanges = Array.from(selectionToRemove.getRanges()).map(viewRange => {
                    return editor.editing.mapper.toModelRange(viewRange);
                });
                commandData.selection = editor.model.createSelection(modelRanges);
            }
            else {
                commandData.unit = unit;
            }
            editor.execute(commandName, commandData);
            view.scrollToTheSelection();
        }, { priority: 'low' });
        if (this.editor.plugins.has('UndoEditing')) {
            this.listenTo(viewDocument, 'delete', (evt, data) => {
                if (this._undoOnBackspace && data.direction == 'backward' && data.sequence == 1 && data.unit == 'codePoint') {
                    this._undoOnBackspace = false;
                    editor.execute('undo');
                    data.preventDefault();
                    evt.stop();
                }
            }, { context: '$capture' });
            this.listenTo(modelDocument, 'change', () => {
                this._undoOnBackspace = false;
            });
        }
    }
    /**
     * If the next user action after calling this method is pressing backspace, it would undo the last change.
     *
     * Requires {@link module:undo/undoediting~UndoEditing} plugin. If not loaded, does nothing.
     */
    requestUndoOnBackspace() {
        if (this.editor.plugins.has('UndoEditing')) {
            this._undoOnBackspace = true;
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/typing.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/typing
 */



/**
 * The typing feature. It handles typing.
 *
 * This is a "glue" plugin which loads the {@link module:typing/input~Input} and {@link module:typing/delete~Delete}
 * plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Typing extends plugin_Plugin {
    static get requires() {
        return [Input, delete_Delete];
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Typing';
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/utils/getlasttextline.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Returns the last text line from the given range.
 *
 * "The last text line" is understood as text (from one or more text nodes) which is limited either by a parent block
 * or by inline elements (e.g. `<softBreak>`).
 *
 *		const rangeToCheck = model.createRange(
 *			model.createPositionAt( paragraph, 0 ),
 *			model.createPositionAt( paragraph, 'end' )
 *		);
 *
 *		const { text, range } = getLastTextLine( rangeToCheck, model );
 *
 * For model below, the returned `text` will be "Foo bar baz" and `range` will be set on whole `<paragraph>` content:
 *
 *		<paragraph>Foo bar baz<paragraph>
 *
 * However, in below case, `text` will be set to "baz" and `range` will be set only on "baz".
 *
 *		<paragraph>Foo<softBreak></softBreak>bar<softBreak></softBreak>baz<paragraph>
 *
 * @protected
 * @param {module:engine/model/range~Range} range
 * @param {module:engine/model/model~Model} model
 * @returns {module:typing/utils/getlasttextline~LastTextLineData}
 */
function getLastTextLine(range, model) {
    let start = range.start;
    const text = Array.from(range.getItems()).reduce((rangeText, node) => {
        // Trim text to a last occurrence of an inline element and update range start.
        if (!(node.is('$text') || node.is('$textProxy'))) {
            start = model.createPositionAfter(node);
            return '';
        }
        return rangeText + node.data;
    }, '');
    return { text, range: model.createRange(start, range.end) };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/textwatcher.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/textwatcher
 */


/**
 * The text watcher feature.
 *
 * Fires the {@link module:typing/textwatcher~TextWatcher#event:matched:data `matched:data`},
 * {@link module:typing/textwatcher~TextWatcher#event:matched:selection `matched:selection`} and
 * {@link module:typing/textwatcher~TextWatcher#event:unmatched `unmatched`} events on typing or selection changes.
 *
 * @private
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class TextWatcher extends Observable {
    /**
     * Creates a text watcher instance.
     *
     * @param {module:engine/model/model~Model} model
     * @param {Function} testCallback See {@link module:typing/textwatcher~TextWatcher#testCallback}.
     */
    constructor(model, testCallback) {
        super();
        /**
         * The editor's model.
         *
         * @readonly
         * @member {module:engine/model/model~Model}
         */
        this.model = model;
        /**
         * The function used to match the text.
         *
         * The test callback can return 3 values:
         *
         * * `false` if there is no match,
         * * `true` if there is a match,
         * * an object if there is a match and we want to pass some additional information to the {@link #event:matched:data} event.
         *
         * @member {Function} #testCallback
         * @returns {Object} testResult
         */
        this.testCallback = testCallback;
        /**
         * Whether there is a match currently.
         *
         * @readonly
         * @member {Boolean}
         */
        this._hasMatch = false;
        /**
         * Flag indicating whether the `TextWatcher` instance is enabled or disabled.
         * A disabled TextWatcher will not evaluate text.
         *
         * To disable TextWatcher:
         *
         *		const watcher = new TextWatcher( editor.model, testCallback );
         *
         *		// After this a testCallback will not be called.
         *		watcher.isEnabled = false;
         *
         * @observable
         * @member {Boolean} #isEnabled
         */
        this.set('isEnabled', true);
        // Toggle text watching on isEnabled state change.
        this.on('change:isEnabled', () => {
            if (this.isEnabled) {
                this._startListening();
            }
            else {
                this.stopListening(model.document.selection);
                this.stopListening(model.document);
            }
        });
        this._startListening();
    }
    /**
     * TODO
     */
    get hasMatch() {
        return this._hasMatch;
    }
    /**
     * Starts listening to the editor for typing and selection events.
     *
     * @private
     */
    _startListening() {
        const model = this.model;
        const document = model.document;
        this.listenTo(document.selection, 'change:range', (evt, { directChange }) => {
            // Indirect changes (i.e. when the user types or external changes are applied) are handled in the document's change event.
            if (!directChange) {
                return;
            }
            // Act only on collapsed selection.
            if (!document.selection.isCollapsed) {
                if (this.hasMatch) {
                    this.fire('unmatched');
                    this._hasMatch = false;
                }
                return;
            }
            this._evaluateTextBeforeSelection('selection');
        });
        this.listenTo(document, 'change:data', (evt, batch) => {
            if (batch.isUndo || !batch.isLocal) {
                return;
            }
            this._evaluateTextBeforeSelection('data', { batch });
        });
    }
    /**
     * Checks the editor content for matched text.
     *
     * @fires matched:data
     * @fires matched:selection
     * @fires unmatched
     *
     * @private
     * @param {'data'|'selection'} suffix A suffix used for generating the event name.
     * @param {Object} data Data object for event.
     */
    _evaluateTextBeforeSelection(suffix, data = {}) {
        const model = this.model;
        const document = model.document;
        const selection = document.selection;
        const rangeBeforeSelection = model.createRange(model.createPositionAt(selection.focus.parent, 0), selection.focus);
        const { text, range } = getLastTextLine(rangeBeforeSelection, model);
        const testResult = this.testCallback(text);
        if (!testResult && this.hasMatch) {
            this.fire('unmatched');
        }
        this._hasMatch = !!testResult;
        if (testResult) {
            const eventData = Object.assign(data, { text, range });
            // If the test callback returns an object with additional data, assign the data as well.
            if (typeof testResult == 'object') {
                Object.assign(eventData, testResult);
            }
            this.fire(`matched:${suffix}`, eventData);
        }
    }
}
/**
 * Fired whenever the text does not match anymore. Fired only when the text watcher found a match.
 *
 * @event unmatched
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/twostepcaretmovement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/twostepcaretmovement
 */


/**
 * This plugin enables the two-step caret (phantom) movement behavior for
 * {@link module:typing/twostepcaretmovement~TwoStepCaretMovement#registerAttribute registered attributes}
 * on arrow right (<kbd>→</kbd>) and left (<kbd>←</kbd>) key press.
 *
 * Thanks to this (phantom) caret movement the user is able to type before/after as well as at the
 * beginning/end of an attribute.
 *
 * **Note:** This plugin support right–to–left (Arabic, Hebrew, etc.) content by mirroring its behavior
 * but for the sake of simplicity examples showcase only left–to–right use–cases.
 *
 * # Forward movement
 *
 * ## "Entering" an attribute:
 *
 * When this plugin is enabled and registered for the `a` attribute and the selection is right before it
 * (at the attribute boundary), pressing the right arrow key will not move the selection but update its
 * attributes accordingly:
 *
 * * When enabled:
 *
 *   		foo{}<$text a="true">bar</$text>
 *
 *    <kbd>→</kbd>
 *
 *   		foo<$text a="true">{}bar</$text>
 *
 * * When disabled:
 *
 *   		foo{}<$text a="true">bar</$text>
 *
 *   <kbd>→</kbd>
 *
 *   		foo<$text a="true">b{}ar</$text>
 *
 *
 * ## "Leaving" an attribute:
 *
 * * When enabled:
 *
 *   		<$text a="true">bar{}</$text>baz
 *
 *    <kbd>→</kbd>
 *
 *   		<$text a="true">bar</$text>{}baz
 *
 * * When disabled:
 *
 *   		<$text a="true">bar{}</$text>baz
 *
 *   <kbd>→</kbd>
 *
 *   		<$text a="true">bar</$text>b{}az
 *
 * # Backward movement
 *
 * * When enabled:
 *
 *   		<$text a="true">bar</$text>{}baz
 *
 *    <kbd>←</kbd>
 *
 *   		<$text a="true">bar{}</$text>baz
 *
 * * When disabled:
 *
 *   		<$text a="true">bar</$text>{}baz
 *
 *   <kbd>←</kbd>
 *
 *   		<$text a="true">ba{}r</$text>b{}az
 *
 * # Multiple attributes
 *
 * * When enabled and many attributes starts or ends at the same position:
 *
 *   		<$text a="true" b="true">bar</$text>{}baz
 *
 *    <kbd>←</kbd>
 *
 *   		<$text a="true" b="true">bar{}</$text>baz
 *
 * * When enabled and one procedes another:
 *
 *   		<$text a="true">bar</$text><$text b="true">{}bar</$text>
 *
 *    <kbd>←</kbd>
 *
 *   		<$text a="true">bar{}</$text><$text b="true">bar</$text>
 *
 */
class TwoStepCaretMovement extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'TwoStepCaretMovement';
    }
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        /**
         * A set of attributes to handle.
         *
         * @protected
         * @property {module:typing/twostepcaretmovement~TwoStepCaretMovement}
         */
        this.attributes = new Set();
        /**
         * The current UID of the overridden gravity, as returned by
         * {@link module:engine/model/writer~Writer#overrideSelectionGravity}.
         *
         * @private
         * @member {String}
         */
        this._overrideUid = null;
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const model = editor.model;
        const view = editor.editing.view;
        const locale = editor.locale;
        const modelSelection = model.document.selection;
        // Listen to keyboard events and handle the caret movement according to the 2-step caret logic.
        this.listenTo(view.document, 'arrowKey', (evt, data) => {
            // This implementation works only for collapsed selection.
            if (!modelSelection.isCollapsed) {
                return;
            }
            // When user tries to expand the selection or jump over the whole word or to the beginning/end then
            // two-steps movement is not necessary.
            if (data.shiftKey || data.altKey || data.ctrlKey) {
                return;
            }
            const arrowRightPressed = data.keyCode == keyCodes.arrowright;
            const arrowLeftPressed = data.keyCode == keyCodes.arrowleft;
            // When neither left or right arrow has been pressed then do noting.
            if (!arrowRightPressed && !arrowLeftPressed) {
                return;
            }
            const contentDirection = locale.contentLanguageDirection;
            let isMovementHandled = false;
            if ((contentDirection === 'ltr' && arrowRightPressed) || (contentDirection === 'rtl' && arrowLeftPressed)) {
                isMovementHandled = this._handleForwardMovement(data);
            }
            else {
                isMovementHandled = this._handleBackwardMovement(data);
            }
            // Stop the keydown event if the two-step caret movement handled it. Avoid collisions
            // with other features which may also take over the caret movement (e.g. Widget).
            if (isMovementHandled === true) {
                evt.stop();
            }
        }, { context: '$text', priority: 'highest' });
        /**
         * A flag indicating that the automatic gravity restoration should not happen upon the next
         * gravity restoration.
         * {@link module:engine/model/selection~Selection#event:change:range} event.
         *
         * @private
         * @member {String}
         */
        this._isNextGravityRestorationSkipped = false;
        // The automatic gravity restoration logic.
        this.listenTo(modelSelection, 'change:range', (evt, data) => {
            // Skipping the automatic restoration is needed if the selection should change
            // but the gravity must remain overridden afterwards. See the #handleBackwardMovement
            // to learn more.
            if (this._isNextGravityRestorationSkipped) {
                this._isNextGravityRestorationSkipped = false;
                return;
            }
            // Skip automatic restore when the gravity is not overridden — simply, there's nothing to restore
            // at this moment.
            if (!this._isGravityOverridden) {
                return;
            }
            // Skip automatic restore when the change is indirect AND the selection is at the attribute boundary.
            // It means that e.g. if the change was external (collaboration) and the user had their
            // selection around the link, its gravity should remain intact in this change:range event.
            if (!data.directChange && isBetweenDifferentAttributes(modelSelection.getFirstPosition(), this.attributes)) {
                return;
            }
            this._restoreGravity();
        });
    }
    /**
     * Registers a given attribute for the two-step caret movement.
     *
     * @param {String} attribute Name of the attribute to handle.
     */
    registerAttribute(attribute) {
        this.attributes.add(attribute);
    }
    /**
     * Updates the document selection and the view according to the two–step caret movement state
     * when moving **forwards**. Executed upon `keypress` in the {@link module:engine/view/view~View}.
     *
     * @private
     * @param {module:engine/view/observer/domeventdata~DomEventData} data Data of the key press.
     * @returns {Boolean} `true` when the handler prevented caret movement
     */
    _handleForwardMovement(data) {
        const attributes = this.attributes;
        const model = this.editor.model;
        const selection = model.document.selection;
        const position = selection.getFirstPosition();
        // DON'T ENGAGE 2-SCM if gravity is already overridden. It means that we just entered
        //
        // 		<paragraph>foo<$text attribute>{}bar</$text>baz</paragraph>
        //
        // or left the attribute
        //
        // 		<paragraph>foo<$text attribute>bar</$text>{}baz</paragraph>
        //
        // and the gravity will be restored automatically.
        if (this._isGravityOverridden) {
            return false;
        }
        // DON'T ENGAGE 2-SCM when the selection is at the beginning of the block AND already has the
        // attribute:
        // * when the selection was initially set there using the mouse,
        // * when the editor has just started
        //
        //		<paragraph><$text attribute>{}bar</$text>baz</paragraph>
        //
        if (position.isAtStart && hasAnyAttribute(selection, attributes)) {
            return false;
        }
        // ENGAGE 2-SCM When at least one of the observed attributes changes its value (incl. starts, ends).
        //
        //		<paragraph>foo<$text attribute>bar{}</$text>baz</paragraph>
        //		<paragraph>foo<$text attribute>bar{}</$text><$text otherAttribute>baz</$text></paragraph>
        //		<paragraph>foo<$text attribute=1>bar{}</$text><$text attribute=2>baz</$text></paragraph>
        //		<paragraph>foo{}<$text attribute>bar</$text>baz</paragraph>
        //
        if (isBetweenDifferentAttributes(position, attributes)) {
            preventCaretMovement(data);
            this._overrideGravity();
            return true;
        }
        return false;
    }
    /**
     * Updates the document selection and the view according to the two–step caret movement state
     * when moving **backwards**. Executed upon `keypress` in the {@link module:engine/view/view~View}.
     *
     * @private
     * @param {module:engine/view/observer/domeventdata~DomEventData} data Data of the key press.
     * @returns {Boolean} `true` when the handler prevented caret movement
     */
    _handleBackwardMovement(data) {
        const attributes = this.attributes;
        const model = this.editor.model;
        const selection = model.document.selection;
        const position = selection.getFirstPosition();
        // When the gravity is already overridden (by this plugin), it means we are on the two-step position.
        // Prevent the movement, restore the gravity and update selection attributes.
        //
        //		<paragraph>foo<$text attribute=1>bar</$text><$text attribute=2>{}baz</$text></paragraph>
        //		<paragraph>foo<$text attribute>bar</$text><$text otherAttribute>{}baz</$text></paragraph>
        //		<paragraph>foo<$text attribute>{}bar</$text>baz</paragraph>
        //		<paragraph>foo<$text attribute>bar</$text>{}baz</paragraph>
        //
        if (this._isGravityOverridden) {
            preventCaretMovement(data);
            this._restoreGravity();
            setSelectionAttributesFromTheNodeBefore(model, attributes, position);
            return true;
        }
        else {
            // REMOVE SELECTION ATTRIBUTE when restoring gravity towards a non-existent content at the
            // beginning of the block.
            //
            // 		<paragraph>{}<$text attribute>bar</$text></paragraph>
            //
            if (position.isAtStart) {
                if (hasAnyAttribute(selection, attributes)) {
                    preventCaretMovement(data);
                    setSelectionAttributesFromTheNodeBefore(model, attributes, position);
                    return true;
                }
                return false;
            }
            // When we are moving from natural gravity, to the position of the 2SCM, we need to override the gravity,
            // and make sure it won't be restored. Unless it's at the end of the block and an observed attribute.
            // We need to check if the caret is a one position before the attribute boundary:
            //
            //		<paragraph>foo<$text attribute=1>bar</$text><$text attribute=2>b{}az</$text></paragraph>
            //		<paragraph>foo<$text attribute>bar</$text><$text otherAttribute>b{}az</$text></paragraph>
            //		<paragraph>foo<$text attribute>b{}ar</$text>baz</paragraph>
            //		<paragraph>foo<$text attribute>bar</$text>b{}az</paragraph>
            //
            if (isStepAfterAnyAttributeBoundary(position, attributes)) {
                // ENGAGE 2-SCM if the selection has no attribute. This may happen when the user
                // left the attribute using a FORWARD 2-SCM.
                //
                // 		<paragraph><$text attribute>bar</$text>{}</paragraph>
                //
                if (position.isAtEnd &&
                    !hasAnyAttribute(selection, attributes) &&
                    isBetweenDifferentAttributes(position, attributes)) {
                    preventCaretMovement(data);
                    setSelectionAttributesFromTheNodeBefore(model, attributes, position);
                    return true;
                }
                // Skip the automatic gravity restore upon the next selection#change:range event.
                // If not skipped, it would automatically restore the gravity, which should remain
                // overridden.
                this._isNextGravityRestorationSkipped = true;
                this._overrideGravity();
                // Don't return "true" here because we didn't call _preventCaretMovement.
                // Returning here will destabilize the filler logic, which also listens to
                // keydown (and the event would be stopped).
                return false;
            }
        }
        return false;
    }
    /**
     * `true` when the gravity is overridden for the plugin.
     *
     * @readonly
     * @private
     * @type {Boolean}
     */
    get _isGravityOverridden() {
        return !!this._overrideUid;
    }
    /**
     * Overrides the gravity using the {@link module:engine/model/writer~Writer model writer}
     * and stores the information about this fact in the {@link #_overrideUid}.
     *
     * A shorthand for {@link module:engine/model/writer~Writer#overrideSelectionGravity}.
     *
     * @private
     */
    _overrideGravity() {
        this._overrideUid = this.editor.model.change(writer => {
            return writer.overrideSelectionGravity();
        });
    }
    /**
     * Restores the gravity using the {@link module:engine/model/writer~Writer model writer}.
     *
     * A shorthand for {@link module:engine/model/writer~Writer#restoreSelectionGravity}.
     *
     * @private
     */
    _restoreGravity() {
        this.editor.model.change(writer => {
            writer.restoreSelectionGravity(this._overrideUid);
            this._overrideUid = null;
        });
    }
}
// Checks whether the selection has any of given attributes.
//
// @param {module:engine/model/documentselection~DocumentSelection} selection
// @param {Iterable.<String>} attributes
function hasAnyAttribute(selection, attributes) {
    for (const observedAttribute of attributes) {
        if (selection.hasAttribute(observedAttribute)) {
            return true;
        }
    }
    return false;
}
// Applies the given attributes to the current selection using using the
// values from the node before the current position. Uses
// the {@link module:engine/model/writer~Writer model writer}.
//
// @param {module:engine/model/model~Model}
// @param {Iterable.<String>} attributess
// @param {module:engine/model/position~Position} position
function setSelectionAttributesFromTheNodeBefore(model, attributes, position) {
    const nodeBefore = position.nodeBefore;
    model.change(writer => {
        if (nodeBefore) {
            writer.setSelectionAttribute(nodeBefore.getAttributes());
        }
        else {
            writer.removeSelectionAttribute(attributes);
        }
    });
}
// Prevents the caret movement in the view by calling `preventDefault` on the event data.
//
// @alias data.preventDefault
function preventCaretMovement(data) {
    data.preventDefault();
}
// Checks whether the step before `isBetweenDifferentAttributes()`.
//
// @param {module:engine/model/position~Position} position
// @param {String} attribute
function isStepAfterAnyAttributeBoundary(position, attributes) {
    const positionBefore = position.getShiftedBy(-1);
    return isBetweenDifferentAttributes(positionBefore, attributes);
}
// Checks whether the given position is between different values of given attributes.
//
// @param {module:engine/model/position~Position} position
// @param {Iterable.<String>} attributes
function isBetweenDifferentAttributes(position, attributes) {
    const { nodeBefore, nodeAfter } = position;
    for (const observedAttribute of attributes) {
        const attrBefore = nodeBefore ? nodeBefore.getAttribute(observedAttribute) : undefined;
        const attrAfter = nodeAfter ? nodeAfter.getAttribute(observedAttribute) : undefined;
        if (attrAfter !== attrBefore) {
            return true;
        }
    }
    return false;
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/escapeRegExp.js


/**
 * Used to match `RegExp`
 * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
 */
var escapeRegExp_reRegExpChar = /[\\^$.*+?()[\]{}|]/g,
    reHasRegExpChar = RegExp(escapeRegExp_reRegExpChar.source);

/**
 * Escapes the `RegExp` special characters "^", "$", "\", ".", "*", "+",
 * "?", "(", ")", "[", "]", "{", "}", and "|" in `string`.
 *
 * @static
 * @memberOf _
 * @since 3.0.0
 * @category String
 * @param {string} [string=''] The string to escape.
 * @returns {string} Returns the escaped string.
 * @example
 *
 * _.escapeRegExp('[lodash](https://lodash.com/)');
 * // => '\[lodash\]\(https://lodash\.com/\)'
 */
function escapeRegExp(string) {
  string = lodash_es_toString(string);
  return (string && reHasRegExpChar.test(string))
    ? string.replace(escapeRegExp_reRegExpChar, '\\$&')
    : string;
}

/* harmony default export */ const lodash_es_escapeRegExp = (escapeRegExp);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/texttransformation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing/texttransformation
 */



// All named transformations.
const TRANSFORMATIONS = {
    // Common symbols:
    copyright: { from: '(c)', to: '©' },
    registeredTrademark: { from: '(r)', to: '®' },
    trademark: { from: '(tm)', to: '™' },
    // Mathematical:
    oneHalf: { from: /(^|[^/a-z0-9])(1\/2)([^/a-z0-9])$/i, to: [null, '½', null] },
    oneThird: { from: /(^|[^/a-z0-9])(1\/3)([^/a-z0-9])$/i, to: [null, '⅓', null] },
    twoThirds: { from: /(^|[^/a-z0-9])(2\/3)([^/a-z0-9])$/i, to: [null, '⅔', null] },
    oneForth: { from: /(^|[^/a-z0-9])(1\/4)([^/a-z0-9])$/i, to: [null, '¼', null] },
    threeQuarters: { from: /(^|[^/a-z0-9])(3\/4)([^/a-z0-9])$/i, to: [null, '¾', null] },
    lessThanOrEqual: { from: '<=', to: '≤' },
    greaterThanOrEqual: { from: '>=', to: '≥' },
    notEqual: { from: '!=', to: '≠' },
    arrowLeft: { from: '<-', to: '←' },
    arrowRight: { from: '->', to: '→' },
    // Typography:
    horizontalEllipsis: { from: '...', to: '…' },
    enDash: { from: /(^| )(--)( )$/, to: [null, '–', null] },
    emDash: { from: /(^| )(---)( )$/, to: [null, '—', null] },
    // Quotations:
    // English, US
    quotesPrimary: { from: buildQuotesRegExp('"'), to: [null, '“', null, '”'] },
    quotesSecondary: { from: buildQuotesRegExp('\''), to: [null, '‘', null, '’'] },
    // English, UK
    quotesPrimaryEnGb: { from: buildQuotesRegExp('\''), to: [null, '‘', null, '’'] },
    quotesSecondaryEnGb: { from: buildQuotesRegExp('"'), to: [null, '“', null, '”'] },
    // Polish
    quotesPrimaryPl: { from: buildQuotesRegExp('"'), to: [null, '„', null, '”'] },
    quotesSecondaryPl: { from: buildQuotesRegExp('\''), to: [null, '‚', null, '’'] }
};
// Transformation groups.
const TRANSFORMATION_GROUPS = {
    symbols: ['copyright', 'registeredTrademark', 'trademark'],
    mathematical: [
        'oneHalf', 'oneThird', 'twoThirds', 'oneForth', 'threeQuarters',
        'lessThanOrEqual', 'greaterThanOrEqual', 'notEqual',
        'arrowLeft', 'arrowRight'
    ],
    typography: ['horizontalEllipsis', 'enDash', 'emDash'],
    quotes: ['quotesPrimary', 'quotesSecondary']
};
// A set of default transformations provided by the feature.
const DEFAULT_TRANSFORMATIONS = [
    'symbols',
    'mathematical',
    'typography',
    'quotes'
];
/**
 * The text transformation plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TextTransformation extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get requires() {
        return ['Delete', 'Input'];
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'TextTransformation';
    }
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        editor.config.define('typing', {
            transformations: {
                include: DEFAULT_TRANSFORMATIONS
            }
        });
    }
    /**
     * @inheritDoc
     */
    init() {
        const model = this.editor.model;
        const modelSelection = model.document.selection;
        modelSelection.on('change:range', () => {
            // Disable plugin when selection is inside a code block.
            this.isEnabled = !modelSelection.anchor.parent.is('element', 'codeBlock');
        });
        this._enableTransformationWatchers();
    }
    /**
     * Create new TextWatcher listening to the editor for typing and selection events.
     *
     * @private
     */
    _enableTransformationWatchers() {
        const editor = this.editor;
        const model = editor.model;
        const deletePlugin = editor.plugins.get('Delete');
        const normalizedTransformations = normalizeTransformations(editor.config.get('typing.transformations'));
        const testCallback = (text) => {
            for (const normalizedTransformation of normalizedTransformations) {
                const from = normalizedTransformation.from;
                const match = from.test(text);
                if (match) {
                    return { normalizedTransformation };
                }
            }
        };
        const watcher = new TextWatcher(editor.model, testCallback);
        watcher.on('matched:data', (evt, data) => {
            if (!data.batch.isTyping) {
                return;
            }
            const { from, to } = data.normalizedTransformation;
            const matches = from.exec(data.text);
            const replaces = to(matches.slice(1));
            const matchedRange = data.range;
            let changeIndex = matches.index;
            model.enqueueChange(writer => {
                for (let i = 1; i < matches.length; i++) {
                    const match = matches[i];
                    const replaceWith = replaces[i - 1];
                    if (replaceWith == null) {
                        changeIndex += match.length;
                        continue;
                    }
                    const replacePosition = matchedRange.start.getShiftedBy(changeIndex);
                    const replaceRange = model.createRange(replacePosition, replacePosition.getShiftedBy(match.length));
                    const attributes = getTextAttributesAfterPosition(replacePosition);
                    model.insertContent(writer.createText(replaceWith, attributes), replaceRange);
                    changeIndex += replaceWith.length;
                }
                model.enqueueChange(() => {
                    deletePlugin.requestUndoOnBackspace();
                });
            });
        });
        watcher.bind('isEnabled').to(this);
    }
}
// Normalizes the configuration `from` parameter value.
// The normalized value for the `from` parameter is a RegExp instance. If the passed `from` is already a RegExp instance,
// it is returned unchanged.
//
// @param {String|RegExp} from
// @returns {RegExp}
function normalizeFrom(from) {
    if (typeof from == 'string') {
        return new RegExp(`(${lodash_es_escapeRegExp(from)})$`);
    }
    // `from` is already a regular expression.
    return from;
}
// Normalizes the configuration `to` parameter value.
// The normalized value for the `to` parameter is a function that takes an array and returns an array. See more in the
// configuration description. If the passed `to` is already a function, it is returned unchanged.
//
// @param {String|Array.<null|String>|Function} to
// @returns {Function}
function normalizeTo(to) {
    if (typeof to == 'string') {
        return () => [to];
    }
    else if (to instanceof Array) {
        return () => to;
    }
    // `to` is already a function.
    return to;
}
// For given `position` returns attributes for the text that is after that position.
// The text can be in the same text node as the position (`foo[]bar`) or in the next text node (`foo[]<$text bold="true">bar</$text>`).
//
// @param {module:engine/model/position~Position} position
// @returns {Iterable.<*>}
function getTextAttributesAfterPosition(position) {
    const textNode = position.textNode ? position.textNode : position.nodeAfter;
    return textNode.getAttributes();
}
// Returns a RegExp pattern string that detects a sentence inside a quote.
//
// @param {String} quoteCharacter The character to create a pattern for.
// @returns {String}
function buildQuotesRegExp(quoteCharacter) {
    return new RegExp(`(^|\\s)(${quoteCharacter})([^${quoteCharacter}]*)(${quoteCharacter})$`);
}
// Reads text transformation config and returns normalized array of transformations objects.
//
// @param {module:typing/texttransformation~TextTransformationDescription} config
// @returns {Array.<{from:String,to:Function}>}
function normalizeTransformations(config) {
    const extra = config.extra || [];
    const remove = config.remove || [];
    const isNotRemoved = (transformation) => !remove.includes(transformation);
    const configured = config.include.concat(extra).filter(isNotRemoved);
    return expandGroupsAndRemoveDuplicates(configured)
        .filter(isNotRemoved) // Filter out 'remove' transformations as they might be set in group.
        .map(transformation => (typeof transformation == 'string' && TRANSFORMATIONS[transformation] ? TRANSFORMATIONS[transformation] : transformation))
        // Filter out transformations set as string that has not been found.
        .filter((transformation) => typeof transformation === 'object')
        .map(transformation => ({
        from: normalizeFrom(transformation.from),
        to: normalizeTo(transformation.to)
    }));
}
// Reads definitions and expands named groups if needed to transformation names.
// This method also removes duplicated named transformations if any.
//
// @param {Array.<String|Object>} definitions
// @returns {Array.<String|Object>}
function expandGroupsAndRemoveDuplicates(definitions) {
    // Set is using to make sure that transformation names are not duplicated.
    const definedTransformations = new Set();
    for (const transformationOrGroup of definitions) {
        if (typeof transformationOrGroup == 'string' && TRANSFORMATION_GROUPS[transformationOrGroup]) {
            for (const transformation of TRANSFORMATION_GROUPS[transformationOrGroup]) {
                definedTransformations.add(transformation);
            }
        }
        else {
            definedTransformations.add(transformationOrGroup);
        }
    }
    return Array.from(definedTransformations);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/utils/findattributerange.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Returns a model range that covers all consecutive nodes with the same `attributeName` and its `value`
 * that intersect the given `position`.
 *
 * It can be used e.g. to get the entire range on which the `linkHref` attribute needs to be changed when having a
 * selection inside a link.
 *
 * @param {module:engine/model/position~Position} position The start position.
 * @param {String} attributeName The attribute name.
 * @param {String} value The attribute value.
 * @param {module:engine/model/model~Model} model The model instance.
 * @returns {module:engine/model/range~Range} The link range.
 */
function findAttributeRange(position, attributeName, value, model) {
    return model.createRange(_findBound(position, attributeName, value, true, model), _findBound(position, attributeName, value, false, model));
}
// Walks forward or backward (depends on the `lookBack` flag), node by node, as long as they have the same attribute value
// and returns a position just before or after (depends on the `lookBack` flag) the last matched node.
//
// @param {module:engine/model/position~Position} position The start position.
// @param {String} attributeName The attribute name.
// @param {String} value The attribute value.
// @param {Boolean} lookBack Whether the walk direction is forward (`false`) or backward (`true`).
// @returns {module:engine/model/position~Position} The position just before the last matched node.
function _findBound(position, attributeName, value, lookBack, model) {
    // Get node before or after position (depends on `lookBack` flag).
    // When position is inside text node then start searching from text node.
    let node = position.textNode || (lookBack ? position.nodeBefore : position.nodeAfter);
    let lastNode = null;
    while (node && node.getAttribute(attributeName) == value) {
        lastNode = node;
        node = lookBack ? node.previousSibling : node.nextSibling;
    }
    return lastNode ? model.createPositionAt(lastNode, lookBack ? 'before' : 'after') : position;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/utils/inlinehighlight.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module typing/utils/inlinehighlight
 */
/**
 * Adds a visual highlight style to an attribute element in which the selection is anchored.
 * Together with two-step caret movement, they indicate that the user is typing inside the element.
 *
 * Highlight is turned on by adding the given class to the attribute element in the view:
 *
 * * The class is removed before the conversion has started, as callbacks added with the `'highest'` priority
 * to {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher} events.
 * * The class is added in the view post fixer, after other changes in the model tree were converted to the view.
 *
 * This way, adding and removing the highlight does not interfere with conversion.
 *
 * Usage:
 *
 *		import inlineHighlight from '@ckeditor/ckeditor5-typing/src/utils/inlinehighlight';
 *
 *		// Make `ck-link_selected` class be applied on an `a` element
 *		// whenever the corresponding `linkHref` attribute element is selected.
 *		inlineHighlight( editor, 'linkHref', 'a', 'ck-link_selected' );
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @param {String} attributeName The attribute name to check.
 * @param {String} tagName The tagName of a view item.
 * @param {String} className The class name to apply in the view.
 */
function inlineHighlight(editor, attributeName, tagName, className) {
    const view = editor.editing.view;
    const highlightedElements = new Set();
    // Adding the class.
    view.document.registerPostFixer(writer => {
        const selection = editor.model.document.selection;
        let changed = false;
        if (selection.hasAttribute(attributeName)) {
            const modelRange = findAttributeRange(selection.getFirstPosition(), attributeName, selection.getAttribute(attributeName), editor.model);
            const viewRange = editor.editing.mapper.toViewRange(modelRange);
            // There might be multiple view elements in the `viewRange`, for example, when the `a` element is
            // broken by a UIElement.
            for (const item of viewRange.getItems()) {
                if (item.is('element', tagName) && !item.hasClass(className)) {
                    writer.addClass(className, item);
                    highlightedElements.add(item);
                    changed = true;
                }
            }
        }
        return changed;
    });
    // Removing the class.
    editor.conversion.for('editingDowncast').add(dispatcher => {
        // Make sure the highlight is removed on every possible event, before conversion is started.
        dispatcher.on('insert', removeHighlight, { priority: 'highest' });
        dispatcher.on('remove', removeHighlight, { priority: 'highest' });
        dispatcher.on('attribute', removeHighlight, { priority: 'highest' });
        dispatcher.on('selection', removeHighlight, { priority: 'highest' });
        function removeHighlight() {
            view.change(writer => {
                for (const item of highlightedElements.values()) {
                    writer.removeClass(className, item);
                    highlightedElements.delete(item);
                }
            });
        }
    });
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-typing/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module typing
 */










;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/typing.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/typing
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-autoformat/src/blockautoformatediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */




/**
 * The block autoformatting engine. It allows to format various block patterns. For example,
 * it can be configured to turn a paragraph starting with `*` and followed by a space into a list item.
 *
 * The autoformatting operation is integrated with the undo manager,
 * so the autoformatting step can be undone if the user's intention was not to format the text.
 *
 * See the {@link module:autoformat/blockautoformatediting~blockAutoformatEditing `blockAutoformatEditing`} documentation
 * to learn how to create custom block autoformatters. You can also use
 * the {@link module:autoformat/autoformat~Autoformat} feature which enables a set of default autoformatters
 * (lists, headings, bold and italic).
 *
 * @module autoformat/blockautoformatediting
 */

/**
 * Creates a listener triggered on {@link module:engine/model/document~Document#event:change:data `change:data`} event in the document.
 * Calls the callback when inserted text matches the regular expression or the command name
 * if provided instead of the callback.
 *
 * Examples of usage:
 *
 * To convert a paragraph to heading 1 when `- ` is typed, using just the command name:
 *
 *		blockAutoformatEditing( editor, plugin, /^\- $/, 'heading1' );
 *
 * To convert a paragraph to heading 1 when `- ` is typed, using just the callback:
 *
 *		blockAutoformatEditing( editor, plugin, /^\- $/, ( context ) => {
 *			const { match } = context;
 *			const headingLevel = match[ 1 ].length;
 *
 *			editor.execute( 'heading', {
 *				formatId: `heading${ headingLevel }`
 *			} );
 * 		} );
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @param {module:autoformat/autoformat~Autoformat} plugin The autoformat plugin instance.
 * @param {RegExp} pattern The regular expression to execute on just inserted text. The regular expression is tested against the text
 * from the beginning until the caret position.
 * @param {Function|String} callbackOrCommand The callback to execute or the command to run when the text is matched.
 * In case of providing the callback, it receives the following parameter:
 * * {Object} match RegExp.exec() result of matching the pattern to inserted text.
 */
function blockAutoformatEditing( editor, plugin, pattern, callbackOrCommand ) {
	let callback;
	let command = null;

	if ( typeof callbackOrCommand == 'function' ) {
		callback = callbackOrCommand;
	} else {
		// We assume that the actual command name was provided.
		command = editor.commands.get( callbackOrCommand );

		callback = () => {
			editor.execute( callbackOrCommand );
		};
	}

	editor.model.document.on( 'change:data', ( evt, batch ) => {
		if ( command && !command.isEnabled || !plugin.isEnabled ) {
			return;
		}

		const range = first_first( editor.model.document.selection.getRanges() );

		if ( !range.isCollapsed ) {
			return;
		}

		if ( batch.isUndo || !batch.isLocal ) {
			return;
		}

		const changes = Array.from( editor.model.document.differ.getChanges() );
		const entry = changes[ 0 ];

		// Typing is represented by only a single change.
		if ( changes.length != 1 || entry.type !== 'insert' || entry.name != '$text' || entry.length != 1 ) {
			return;
		}

		const blockToFormat = entry.position.parent;

		// Block formatting should be disabled in codeBlocks (#5800).
		if ( blockToFormat.is( 'element', 'codeBlock' ) ) {
			return;
		}

		// Only list commands and custom callbacks can be applied inside a list.
		if ( blockToFormat.is( 'element', 'listItem' ) &&
			typeof callbackOrCommand !== 'function' &&
			![ 'numberedList', 'bulletedList', 'todoList' ].includes( callbackOrCommand )
		) {
			return;
		}

		// In case a command is bound, do not re-execute it over an existing block style which would result with a style removal.
		// Instead just drop processing so that autoformat trigger text is not lost. E.g. writing "# " in a level 1 heading.
		if ( command && command.value === true ) {
			return;
		}

		const firstNode = blockToFormat.getChild( 0 );
		const firstNodeRange = editor.model.createRangeOn( firstNode );

		// Range is only expected to be within or at the very end of the first text node.
		if ( !firstNodeRange.containsRange( range ) && !range.end.isEqual( firstNodeRange.end ) ) {
			return;
		}

		const match = pattern.exec( firstNode.data.substr( 0, range.end.offset ) );

		// ...and this text node's data match the pattern.
		if ( !match ) {
			return;
		}

		// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
		editor.model.enqueueChange( writer => {
			// Matched range.
			const start = writer.createPositionAt( blockToFormat, 0 );
			const end = writer.createPositionAt( blockToFormat, match[ 0 ].length );
			const range = new LiveRange( start, end );

			const wasChanged = callback( { match } );

			// Remove matched text.
			if ( wasChanged !== false ) {
				writer.remove( range );

				const selectionRange = editor.model.document.selection.getFirstRange();
				const blockRange = writer.createRangeIn( blockToFormat );

				// If the block is empty and the document selection has been moved when
				// applying formatting (e.g. is now in newly created block).
				if ( blockToFormat.isEmpty && !blockRange.isEqual( selectionRange ) && !blockRange.containsRange( selectionRange, true ) ) {
					writer.remove( blockToFormat );
				}
			}
			range.detach();

			editor.model.enqueueChange( () => {
				editor.plugins.get( 'Delete' ).requestUndoOnBackspace();
			} );
		} );
	} );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-autoformat/src/inlineautoformatediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * The inline autoformatting engine. It allows to format various inline patterns. For example,
 * it can be configured to make "foo" bold when typed `**foo**` (the `**` markers will be removed).
 *
 * The autoformatting operation is integrated with the undo manager,
 * so the autoformatting step can be undone if the user's intention was not to format the text.
 *
 * See the {@link module:autoformat/inlineautoformatediting~inlineAutoformatEditing `inlineAutoformatEditing`} documentation
 * to learn how to create custom inline autoformatters. You can also use
 * the {@link module:autoformat/autoformat~Autoformat} feature which enables a set of default autoformatters
 * (lists, headings, bold and italic).
 *
 * @module autoformat/inlineautoformatediting
 */

/**
 * Enables autoformatting mechanism for a given {@link module:core/editor/editor~Editor}.
 *
 * It formats the matched text by applying the given model attribute or by running the provided formatting callback.
 * On every {@link module:engine/model/document~Document#event:change:data data change} in the model document
 * the autoformatting engine checks the text on the left of the selection
 * and executes the provided action if the text matches given criteria (regular expression or callback).
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @param {module:autoformat/autoformat~Autoformat} plugin The autoformat plugin instance.
 * @param {Function|RegExp} testRegexpOrCallback The regular expression or callback to execute on text.
 * Provided regular expression *must* have three capture groups. The first and the third capture group
 * should match opening and closing delimiters. The second capture group should match the text to format.
 *
 *		// Matches the `**bold text**` pattern.
 *		// There are three capturing groups:
 *		// - The first to match the starting `**` delimiter.
 *		// - The second to match the text to format.
 *		// - The third to match the ending `**` delimiter.
 *		inlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, formatCallback );
 *
 * When a function is provided instead of the regular expression, it will be executed with the text to match as a parameter.
 * The function should return proper "ranges" to delete and format.
 *
 *		{
 *			remove: [
 *				[ 0, 1 ],	// Remove the first letter from the given text.
 *				[ 5, 6 ]	// Remove the 6th letter from the given text.
 *			],
 *			format: [
 *				[ 1, 5 ]	// Format all letters from 2nd to 5th.
 *			]
 *		}
 *
 * @param {Function} formatCallback A callback to apply actual formatting.
 * It should return `false` if changes should not be applied (e.g. if a command is disabled).
 *
 *		inlineAutoformatEditing( editor, plugin, /(\*\*)([^\*]+?)(\*\*)$/g, ( writer, rangesToFormat ) => {
 *			const command = editor.commands.get( 'bold' );
 *
 *			if ( !command.isEnabled ) {
 *				return false;
 *			}
 *
 *			const validRanges = editor.model.schema.getValidRanges( rangesToFormat, 'bold' );
 *
 *			for ( let range of validRanges ) {
 *				writer.setAttribute( 'bold', true, range );
 *			}
 *		} );
 */
function inlineAutoformatEditing( editor, plugin, testRegexpOrCallback, formatCallback ) {
	let regExp;
	let testCallback;

	if ( testRegexpOrCallback instanceof RegExp ) {
		regExp = testRegexpOrCallback;
	} else {
		testCallback = testRegexpOrCallback;
	}

	// A test callback run on changed text.
	testCallback = testCallback || ( text => {
		let result;
		const remove = [];
		const format = [];

		while ( ( result = regExp.exec( text ) ) !== null ) {
			// There should be full match and 3 capture groups.
			if ( result && result.length < 4 ) {
				break;
			}

			let {
				index,
				'1': leftDel,
				'2': content,
				'3': rightDel
			} = result;

			// Real matched string - there might be some non-capturing groups so we need to recalculate starting index.
			const found = leftDel + content + rightDel;
			index += result[ 0 ].length - found.length;

			// Start and End offsets of delimiters to remove.
			const delStart = [
				index,
				index + leftDel.length
			];
			const delEnd = [
				index + leftDel.length + content.length,
				index + leftDel.length + content.length + rightDel.length
			];

			remove.push( delStart );
			remove.push( delEnd );

			format.push( [ index + leftDel.length, index + leftDel.length + content.length ] );
		}

		return {
			remove,
			format
		};
	} );

	editor.model.document.on( 'change:data', ( evt, batch ) => {
		if ( batch.isUndo || !batch.isLocal || !plugin.isEnabled ) {
			return;
		}

		const model = editor.model;
		const selection = model.document.selection;

		// Do nothing if selection is not collapsed.
		if ( !selection.isCollapsed ) {
			return;
		}

		const changes = Array.from( model.document.differ.getChanges() );
		const entry = changes[ 0 ];

		// Typing is represented by only a single change.
		if ( changes.length != 1 || entry.type !== 'insert' || entry.name != '$text' || entry.length != 1 ) {
			return;
		}

		const focus = selection.focus;
		const block = focus.parent;
		const { text, range } = getTextAfterCode( model.createRange( model.createPositionAt( block, 0 ), focus ), model );
		const testOutput = testCallback( text );
		const rangesToFormat = testOutputToRanges( range.start, testOutput.format, model );
		const rangesToRemove = testOutputToRanges( range.start, testOutput.remove, model );

		if ( !( rangesToFormat.length && rangesToRemove.length ) ) {
			return;
		}

		// Use enqueueChange to create new batch to separate typing batch from the auto-format changes.
		model.enqueueChange( writer => {
			// Apply format.
			const hasChanged = formatCallback( writer, rangesToFormat );

			// Strict check on `false` to have backward compatibility (when callbacks were returning `undefined`).
			if ( hasChanged === false ) {
				return;
			}

			// Remove delimiters - use reversed order to not mix the offsets while removing.
			for ( const range of rangesToRemove.reverse() ) {
				writer.remove( range );
			}

			model.enqueueChange( () => {
				editor.plugins.get( 'Delete' ).requestUndoOnBackspace();
			} );
		} );
	} );
}

// Converts output of the test function provided to the inlineAutoformatEditing and converts it to the model ranges
// inside provided block.
//
// @private
// @param {module:engine/model/position~Position} start
// @param {Array.<Array>} arrays
// @param {module:engine/model/model~Model} model
function testOutputToRanges( start, arrays, model ) {
	return arrays
		.filter( array => ( array[ 0 ] !== undefined && array[ 1 ] !== undefined ) )
		.map( array => {
			return model.createRange( start.getShiftedBy( array[ 0 ] ), start.getShiftedBy( array[ 1 ] ) );
		} );
}

// Returns the last text line after the last code element from the given range.
// It is similar to {@link module:typing/utils/getlasttextline.getLastTextLine `getLastTextLine()`},
// but it ignores any text before the last `code`.
//
// @param {module:engine/model/range~Range} range
// @param {module:engine/model/model~Model} model
// @returns {module:typing/utils/getlasttextline~LastTextLineData}
function getTextAfterCode( range, model ) {
	let start = range.start;

	const text = Array.from( range.getItems() ).reduce( ( rangeText, node ) => {
		// Trim text to a last occurrence of an inline element and update range start.
		if ( !( node.is( '$text' ) || node.is( '$textProxy' ) ) || node.getAttribute( 'code' ) ) {
			start = model.createPositionAfter( node );

			return '';
		}

		return rangeText + node.data;
	}, '' );

	return { text, range: model.createRange( start, range.end ) };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-autoformat/src/autoformat.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module autoformat/autoformat
 */







/**
 * Enables a set of predefined autoformatting actions.
 *
 * For a detailed overview, check the {@glink features/autoformat Autoformatting feature documentation}
 * and the {@glink api/autoformat package page}.
 *
 * @extends module:core/plugin~Plugin
 */
class Autoformat extends plugin_Plugin {
	/**
	 * @inheritdoc
	 */
	static get requires() {
		return [ delete_Delete ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Autoformat';
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		this._addListAutoformats();
		this._addBasicStylesAutoformats();
		this._addHeadingAutoformats();
		this._addBlockQuoteAutoformats();
		this._addCodeBlockAutoformats();
		this._addHorizontalLineAutoformats();
	}

	/**
	 * Adds autoformatting related to the {@link module:list/list~List}.
	 *
	 * When typed:
	 * - `* ` or `- ` &ndash; A paragraph will be changed to a bulleted list.
	 * - `1. ` or `1) ` &ndash; A paragraph will be changed to a numbered list ("1" can be any digit or a list of digits).
	 * - `[] ` or `[ ] ` &ndash; A paragraph will be changed to a to-do list.
	 * - `[x] ` or `[ x ] ` &ndash; A paragraph will be changed to a checked to-do list.
	 *
	 * @private
	 */
	_addListAutoformats() {
		const commands = this.editor.commands;

		if ( commands.get( 'bulletedList' ) ) {
			blockAutoformatEditing( this.editor, this, /^[*-]\s$/, 'bulletedList' );
		}

		if ( commands.get( 'numberedList' ) ) {
			blockAutoformatEditing( this.editor, this, /^1[.|)]\s$/, 'numberedList' );
		}

		if ( commands.get( 'todoList' ) ) {
			blockAutoformatEditing( this.editor, this, /^\[\s?\]\s$/, 'todoList' );
		}

		if ( commands.get( 'checkTodoList' ) ) {
			blockAutoformatEditing( this.editor, this, /^\[\s?x\s?\]\s$/, () => {
				this.editor.execute( 'todoList' );
				this.editor.execute( 'checkTodoList' );
			} );
		}
	}

	/**
	 * Adds autoformatting related to the {@link module:basic-styles/bold~Bold},
	 * {@link module:basic-styles/italic~Italic}, {@link module:basic-styles/code~Code}
	 * and {@link module:basic-styles/strikethrough~Strikethrough}
	 *
	 * When typed:
	 * - `**foobar**` &ndash; `**` characters are removed and `foobar` is set to bold,
	 * - `__foobar__` &ndash; `__` characters are removed and `foobar` is set to bold,
	 * - `*foobar*` &ndash; `*` characters are removed and `foobar` is set to italic,
	 * - `_foobar_` &ndash; `_` characters are removed and `foobar` is set to italic,
	 * - ``` `foobar` &ndash; ``` ` ``` characters are removed and `foobar` is set to code,
	 * - `~~foobar~~` &ndash; `~~` characters are removed and `foobar` is set to strikethrough.
	 *
	 * @private
	 */
	_addBasicStylesAutoformats() {
		const commands = this.editor.commands;

		if ( commands.get( 'bold' ) ) {
			const boldCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'bold' );

			inlineAutoformatEditing( this.editor, this, /(?:^|\s)(\*\*)([^*]+)(\*\*)$/g, boldCallback );
			inlineAutoformatEditing( this.editor, this, /(?:^|\s)(__)([^_]+)(__)$/g, boldCallback );
		}

		if ( commands.get( 'italic' ) ) {
			const italicCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'italic' );

			// The italic autoformatter cannot be triggered by the bold markers, so we need to check the
			// text before the pattern (e.g. `(?:^|[^\*])`).
			inlineAutoformatEditing( this.editor, this, /(?:^|\s)(\*)([^*_]+)(\*)$/g, italicCallback );
			inlineAutoformatEditing( this.editor, this, /(?:^|\s)(_)([^_]+)(_)$/g, italicCallback );
		}

		if ( commands.get( 'code' ) ) {
			const codeCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'code' );

			inlineAutoformatEditing( this.editor, this, /(`)([^`]+)(`)$/g, codeCallback );
		}

		if ( commands.get( 'strikethrough' ) ) {
			const strikethroughCallback = getCallbackFunctionForInlineAutoformat( this.editor, 'strikethrough' );

			inlineAutoformatEditing( this.editor, this, /(~~)([^~]+)(~~)$/g, strikethroughCallback );
		}
	}

	/**
	 * Adds autoformatting related to {@link module:heading/heading~Heading}.
	 *
	 * It is using a number at the end of the command name to associate it with the proper trigger:
	 *
	 * * `heading` with value `heading1` will be executed when typing `#`,
	 * * `heading` with value `heading2` will be executed when typing `##`,
	 * * ... up to `heading6` and `######`.
	 *
	 * @private
	 */
	_addHeadingAutoformats() {
		const command = this.editor.commands.get( 'heading' );

		if ( command ) {
			command.modelElements
				.filter( name => name.match( /^heading[1-6]$/ ) )
				.forEach( modelName => {
					const level = modelName[ 7 ];
					const pattern = new RegExp( `^(#{${ level }})\\s$` );

					blockAutoformatEditing( this.editor, this, pattern, () => {
						// Should only be active if command is enabled and heading style associated with pattern is inactive.
						if ( !command.isEnabled || command.value === modelName ) {
							return false;
						}

						this.editor.execute( 'heading', { value: modelName } );
					} );
				} );
		}
	}

	/**
	 * Adds autoformatting related to {@link module:block-quote/blockquote~BlockQuote}.
	 *
	 * When typed:
	 * * `> ` &ndash; A paragraph will be changed to a block quote.
	 *
	 * @private
	 */
	_addBlockQuoteAutoformats() {
		if ( this.editor.commands.get( 'blockQuote' ) ) {
			blockAutoformatEditing( this.editor, this, /^>\s$/, 'blockQuote' );
		}
	}

	/**
	 * Adds autoformatting related to {@link module:code-block/codeblock~CodeBlock}.
	 *
	 * When typed:
	 * - `` ``` `` &ndash; A paragraph will be changed to a code block.
	 *
	 * @private
	 */
	_addCodeBlockAutoformats() {
		const editor = this.editor;
		const selection = editor.model.document.selection;

		if ( editor.commands.get( 'codeBlock' ) ) {
			blockAutoformatEditing( editor, this, /^```$/, () => {
				if ( selection.getFirstPosition().parent.is( 'element', 'listItem' ) ) {
					return false;
				}
				this.editor.execute( 'codeBlock', {
					usePreviousLanguageChoice: true
				} );
			} );
		}
	}

	/**
	 * Adds autoformatting related to {@link module:horizontal-line/horizontalline~HorizontalLine}.
	 *
	 * When typed:
	 * - `` --- `` &ndash; Will be replaced with a horizontal line.
	 *
	 * @private
	 */
	_addHorizontalLineAutoformats() {
		if ( this.editor.commands.get( 'horizontalLine' ) ) {
			blockAutoformatEditing( this.editor, this, /^---$/, 'horizontalLine' );
		}
	}
}

// Helper function for getting `inlineAutoformatEditing` callbacks that checks if command is enabled.
//
// @param {module:core/editor/editor~Editor} editor
// @param {String} attributeKey
// @returns {Function}
function getCallbackFunctionForInlineAutoformat( editor, attributeKey ) {
	return ( writer, rangesToFormat ) => {
		const command = editor.commands.get( attributeKey );

		if ( !command.isEnabled ) {
			return false;
		}

		const validRanges = editor.model.schema.getValidRanges( rangesToFormat, attributeKey );

		for ( const range of validRanges ) {
			writer.setAttribute( attributeKey, true, range );
		}

		// After applying attribute to the text, remove given attribute from the selection.
		// This way user is able to type a text without attribute used by auto formatter.
		writer.removeSelectionAttribute( attributeKey );
	};
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/clipboardobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard/clipboardobserver
 */



/**
 * Clipboard events observer.
 *
 * Fires the following events:
 *
 * * {@link module:engine/view/document~Document#event:clipboardInput},
 * * {@link module:engine/view/document~Document#event:paste},
 * * {@link module:engine/view/document~Document#event:copy},
 * * {@link module:engine/view/document~Document#event:cut},
 * * {@link module:engine/view/document~Document#event:drop},
 * * {@link module:engine/view/document~Document#event:dragover},
 * * {@link module:engine/view/document~Document#event:dragging},
 * * {@link module:engine/view/document~Document#event:dragstart},
 * * {@link module:engine/view/document~Document#event:dragend},
 * * {@link module:engine/view/document~Document#event:dragenter},
 * * {@link module:engine/view/document~Document#event:dragleave}.
 *
 * **Note**: This observer is not available by default (ckeditor5-engine does not add it on its own).
 * To make it available, it needs to be added to {@link module:engine/view/document~Document} by using
 * the {@link module:engine/view/view~View#addObserver `View#addObserver()`} method. Alternatively, you can load the
 * {@link module:clipboard/clipboard~Clipboard} plugin which adds this observer automatically (because it uses it).
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class ClipboardObserver extends DomEventObserver {
    constructor(view) {
        super(view);
        const viewDocument = this.document;
        this.domEventType = ['paste', 'copy', 'cut', 'drop', 'dragover', 'dragstart', 'dragend', 'dragenter', 'dragleave'];
        this.listenTo(viewDocument, 'paste', handleInput('clipboardInput'), { priority: 'low' });
        this.listenTo(viewDocument, 'drop', handleInput('clipboardInput'), { priority: 'low' });
        this.listenTo(viewDocument, 'dragover', handleInput('dragging'), { priority: 'low' });
        function handleInput(type) {
            return (evt, data) => {
                data.preventDefault();
                const targetRanges = data.dropRange ? [data.dropRange] : null;
                const eventInfo = new EventInfo(viewDocument, type);
                viewDocument.fire(eventInfo, {
                    dataTransfer: data.dataTransfer,
                    method: evt.name,
                    targetRanges,
                    target: data.target
                });
                // If CKEditor handled the input, do not bubble the original event any further.
                // This helps external integrations recognize that fact and act accordingly.
                // https://github.com/ckeditor/ckeditor5-upload/issues/92
                if (eventInfo.stop.called) {
                    data.stopPropagation();
                }
            };
        }
    }
    onDomEvent(domEvent) {
        const evtData = {
            dataTransfer: new DataTransfer('clipboardData' in domEvent ? domEvent.clipboardData : domEvent.dataTransfer)
        };
        if (domEvent.type == 'drop' || domEvent.type == 'dragover') {
            evtData.dropRange = getDropViewRange(this.view, domEvent);
        }
        this.fire(domEvent.type, domEvent, evtData);
    }
}
function getDropViewRange(view, domEvent) {
    const domDoc = domEvent.target.ownerDocument;
    const x = domEvent.clientX;
    const y = domEvent.clientY;
    let domRange;
    // Webkit & Blink.
    if (domDoc.caretRangeFromPoint && domDoc.caretRangeFromPoint(x, y)) {
        domRange = domDoc.caretRangeFromPoint(x, y);
    }
    // FF.
    else if (domEvent.rangeParent) {
        domRange = domDoc.createRange();
        domRange.setStart(domEvent.rangeParent, domEvent.rangeOffset);
        domRange.collapse(true);
    }
    if (domRange) {
        return view.domConverter.domRangeToView(domRange);
    }
    return null;
}
/**
 * Fired as a continuation of the {@link #event:paste} and {@link #event:drop} events.
 *
 * It is a part of the {@glink framework/guides/deep-dive/clipboard#input-pipeline clipboard input pipeline}.
 *
 * This event carries a `dataTransfer` object which comes from the clipboard and whose content should be processed
 * and inserted into the editor.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:clipboard/clipboardobserver~ClipboardObserver
 * @see module:clipboard/clipboard~Clipboard
 * @event module:engine/view/document~Document#event:clipboardInput
 * @param {Object} data The event data.
 * @param {module:engine/view/datatransfer~DataTransfer} data.dataTransfer Data transfer instance.
 * @param {'paste'|'drop'} method Whether the event was triggered by a paste or drop operation.
 * @param {module:engine/view/element~Element} target The tree view element representing the target.
 * @param {Array.<module:engine/view/range~Range>} data.targetRanges Ranges which are the target of the operation
 * (usually – into which the content should be inserted).
 * If the clipboard input was triggered by a paste operation, this property is not set. If by a drop operation,
 * then it is the drop position (which can be different than the selection at the moment of drop).
 */
/**
 * Fired when the user drags the content over one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:engine/view/document~Document#event:clipboardInput
 * @event module:engine/view/document~Document#event:dragover
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */
/**
 * Fired when the user dropped the content into one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:engine/view/document~Document#event:clipboardInput
 * @event module:engine/view/document~Document#event:drop
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 * @param {module:engine/view/range~Range} dropRange The position into which the content is dropped.
 */
/**
 * Fired when the user pasted the content into one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:engine/view/document~Document#event:clipboardInput
 * @event module:engine/view/document~Document#event:paste
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */
/**
 * Fired when the user copied the content from one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:clipboard/clipboardobserver~ClipboardObserver
 * @event module:engine/view/document~Document#event:copy
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */
/**
 * Fired when the user cut the content from one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:clipboard/clipboardobserver~ClipboardObserver
 * @event module:engine/view/document~Document#event:cut
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */
/**
 * The value of the {@link module:engine/view/document~Document#event:paste},
 * {@link module:engine/view/document~Document#event:copy} and {@link module:engine/view/document~Document#event:cut} events.
 *
 * In order to access the clipboard data, use the `dataTransfer` property.
 *
 * @class module:clipboard/clipboardobserver~ClipboardEventData
 * @extends module:engine/view/observer/domeventdata~DomEventData
 */
/**
 * The data transfer instance.
 *
 * @readonly
 * @member {module:engine/view/datatransfer~DataTransfer} module:clipboard/clipboardobserver~ClipboardEventData#dataTransfer
 */
/**
 * Fired as a continuation of the {@link #event:dragover} event.
 *
 * It is a part of the {@glink framework/guides/deep-dive/clipboard#input-pipeline clipboard input pipeline}.
 *
 * This event carries a `dataTransfer` object which comes from the clipboard and whose content should be processed
 * and inserted into the editor.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:clipboard/clipboardobserver~ClipboardObserver
 * @see module:clipboard/clipboard~Clipboard
 * @event module:engine/view/document~Document#event:dragging
 * @param {Object} data The event data.
 * @param {module:engine/view/datatransfer~DataTransfer} data.dataTransfer The data transfer instance.
 * @param {module:engine/view/element~Element} target The tree view element representing the target.
 * @param {Array.<module:engine/view/range~Range>} data.targetRanges Ranges which are the target of the operation
 * (usually – into which the content should be inserted).
 * It is the drop position (which can be different than the selection at the moment of drop).
 */
/**
 * Fired when the user starts dragging the content in one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:engine/view/document~Document#event:clipboardInput
 * @event module:engine/view/document~Document#event:dragstart
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */
/**
 * Fired when the user ended dragging the content.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:engine/view/document~Document#event:clipboardInput
 * @event module:engine/view/document~Document#event:dragend
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */
/**
 * Fired when the user drags the content into one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:engine/view/document~Document#event:clipboardInput
 * @event module:engine/view/document~Document#event:dragenter
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */
/**
 * Fired when the user drags the content out of one of the editing roots of the editor.
 *
 * Introduced by {@link module:clipboard/clipboardobserver~ClipboardObserver}.
 *
 * **Note**: This event is not available by default. To make it available, {@link module:clipboard/clipboardobserver~ClipboardObserver}
 * needs to be added to the {@link module:engine/view/document~Document} by using the {@link module:engine/view/view~View#addObserver}
 * method. This is usually done by the {@link module:clipboard/clipboard~Clipboard} plugin, but if for some reason it is not loaded,
 * the observer must be added manually.
 *
 * @see module:engine/view/document~Document#event:clipboardInput
 * @event module:engine/view/document~Document#event:dragleave
 * @param {module:clipboard/clipboardobserver~ClipboardEventData} data The event data.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/utils/plaintexttohtml.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard/utils/plaintexttohtml
 */
/**
 * Converts plain text to its HTML-ized version.
 *
 * @param {String} text The plain text to convert.
 * @returns {String} HTML generated from the plain text.
 */
function plainTextToHtml(text) {
    text = text
        // Encode <>.
        .replace(/</g, '&lt;')
        .replace(/>/g, '&gt;')
        // Creates a paragraph for each double line break.
        .replace(/\r?\n\r?\n/g, '</p><p>')
        // Creates a line break for each single line break.
        .replace(/\r?\n/g, '<br>')
        // Preserve trailing spaces (only the first and last one – the rest is handled below).
        .replace(/^\s/, '&nbsp;')
        .replace(/\s$/, '&nbsp;')
        // Preserve other subsequent spaces now.
        .replace(/\s\s/g, ' &nbsp;');
    if (text.includes('</p><p>') || text.includes('<br>')) {
        // If we created paragraphs above, add the trailing ones.
        text = `<p>${text}</p>`;
    }
    // TODO:
    // * What about '\nfoo' vs ' foo'?
    return text;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/utils/normalizeclipboarddata.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard/utils/normalizeclipboarddata
 */
/**
 * Removes some popular browser quirks out of the clipboard data (HTML).
 * Removes all HTML comments. These are considered an internal thing and it makes little sense if they leak into the editor data.
 *
 * @param {String} data The HTML data to normalize.
 * @returns {String} Normalized HTML.
 */
function normalizeClipboardData(data) {
    return data
        .replace(/<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g, (fullMatch, spaces) => {
        // Handle the most popular and problematic case when even a single space becomes an nbsp;.
        // Decode those to normal spaces. Read more in https://github.com/ckeditor/ckeditor5-clipboard/issues/2.
        if (spaces.length == 1) {
            return ' ';
        }
        return spaces;
    })
        // Remove all HTML comments.
        .replace(/<!--[\s\S]*?-->/g, '');
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/utils/viewtoplaintext.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
// Elements which should not have empty-line padding.
// Most `view.ContainerElement` want to be separate by new-line, but some are creating one structure
// together (like `<li>`) so it is better to separate them by only one "\n".
const smallPaddingElements = ['figcaption', 'li'];
/**
 * Converts {@link module:engine/view/item~Item view item} and all of its children to plain text.
 *
 * @param {module:engine/view/item~Item} viewItem View item to convert.
 * @returns {String} Plain text representation of `viewItem`.
 */
function viewToPlainText(viewItem) {
    let text = '';
    if (viewItem.is('$text') || viewItem.is('$textProxy')) {
        // If item is `Text` or `TextProxy` simple take its text data.
        text = viewItem.data;
    }
    else if (viewItem.is('element', 'img') && viewItem.hasAttribute('alt')) {
        // Special case for images - use alt attribute if it is provided.
        text = viewItem.getAttribute('alt');
    }
    else if (viewItem.is('element', 'br')) {
        // A soft break should be converted into a single line break (#8045).
        text = '\n';
    }
    else {
        // Other elements are document fragments, attribute elements or container elements.
        // They don't have their own text value, so convert their children.
        let prev = null;
        for (const child of viewItem.getChildren()) {
            const childText = viewToPlainText(child);
            // Separate container element children with one or more new-line characters.
            if (prev && (prev.is('containerElement') || child.is('containerElement'))) {
                if (smallPaddingElements.includes(prev.name) ||
                    smallPaddingElements.includes(child.name)) {
                    text += '\n';
                }
                else {
                    text += '\n\n';
                }
            }
            text += childText;
            prev = child;
        }
    }
    return text;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/clipboardpipeline.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard/clipboardpipeline
 */






// Input pipeline events overview:
//
//              ┌──────────────────────┐          ┌──────────────────────┐
//              │     view.Document    │          │     view.Document    │
//              │         paste        │          │         drop         │
//              └───────────┬──────────┘          └───────────┬──────────┘
//                          │                                 │
//                          └────────────────┌────────────────┘
//                                           │
//                                 ┌─────────V────────┐
//                                 │   view.Document  │   Retrieves text/html or text/plain from data.dataTransfer
//                                 │  clipboardInput  │   and processes it to view.DocumentFragment.
//                                 └─────────┬────────┘
//                                           │
//                               ┌───────────V───────────┐
//                               │   ClipboardPipeline   │   Converts view.DocumentFragment to model.DocumentFragment.
//                               │  inputTransformation  │
//                               └───────────┬───────────┘
//                                           │
//                                ┌──────────V──────────┐
//                                │  ClipboardPipeline  │   Calls model.insertContent().
//                                │   contentInsertion  │
//                                └─────────────────────┘
//
//
// Output pipeline events overview:
//
//              ┌──────────────────────┐          ┌──────────────────────┐
//              │     view.Document    │          │     view.Document    │   Retrieves the selected model.DocumentFragment
//              │         copy         │          │          cut         │   and converts it to view.DocumentFragment.
//              └───────────┬──────────┘          └───────────┬──────────┘
//                          │                                 │
//                          └────────────────┌────────────────┘
//                                           │
//                                 ┌─────────V────────┐
//                                 │   view.Document  │   Processes view.DocumentFragment to text/html and text/plain
//                                 │  clipboardOutput │   and stores the results in data.dataTransfer.
//                                 └──────────────────┘
//
/**
 * The clipboard pipeline feature. It is responsible for intercepting the `paste` and `drop` events and
 * passing the pasted content through a series of events in order to insert it into the editor's content.
 * It also handles the `cut` and `copy` events to fill the native clipboard with the serialized editor's data.
 *
 * # Input pipeline
 *
 * The behavior of the default handlers (all at a `low` priority):
 *
 * ## Event: `paste` or `drop`
 *
 * 1. Translates the event data.
 * 2. Fires the {@link module:engine/view/document~Document#event:clipboardInput `view.Document#clipboardInput`} event.
 *
 * ## Event: `view.Document#clipboardInput`
 *
 * 1. If the `data.content` event field is already set (by some listener on a higher priority), it takes this content and fires the event
 *    from the last point.
 * 2. Otherwise, it retrieves `text/html` or `text/plain` from `data.dataTransfer`.
 * 3. Normalizes the raw data by applying simple filters on string data.
 * 4. Processes the raw data to {@link module:engine/view/documentfragment~DocumentFragment `view.DocumentFragment`} with the
 *    {@link module:engine/controller/datacontroller~DataController#htmlProcessor `DataController#htmlProcessor`}.
 * 5. Fires the {@link module:clipboard/clipboardpipeline~ClipboardPipeline#event:inputTransformation
 *   `ClipboardPipeline#inputTransformation`} event with the view document fragment in the `data.content` event field.
 *
 * ## Event: `ClipboardPipeline#inputTransformation`
 *
 * 1. Converts {@link module:engine/view/documentfragment~DocumentFragment `view.DocumentFragment`} from the `data.content` field to
 *    {@link module:engine/model/documentfragment~DocumentFragment `model.DocumentFragment`}.
 * 2. Fires the {@link module:clipboard/clipboardpipeline~ClipboardPipeline#event:contentInsertion `ClipboardPipeline#contentInsertion`}
 *    event with the model document fragment in the `data.content` event field.
 *    **Note**: The `ClipboardPipeline#contentInsertion` event is fired within a model change block to allow other handlers
 *    to run in the same block without post-fixers called in between (i.e., the selection post-fixer).
 *
 * ## Event: `ClipboardPipeline#contentInsertion`
 *
 * 1. Calls {@link module:engine/model/model~Model#insertContent `model.insertContent()`} to insert `data.content`
 *    at the current selection position.
 *
 * # Output pipeline
 *
 * The behavior of the default handlers (all at a `low` priority):
 *
 * ## Event: `copy`, `cut` or `dragstart`
 *
 * 1. Retrieves the selected {@link module:engine/model/documentfragment~DocumentFragment `model.DocumentFragment`} by calling
 *    {@link module:engine/model/model~Model#getSelectedContent `model#getSelectedContent()`}.
 * 2. Converts the model document fragment to {@link module:engine/view/documentfragment~DocumentFragment `view.DocumentFragment`}.
 * 3. Fires the {@link module:engine/view/document~Document#event:clipboardOutput `view.Document#clipboardOutput`} event
 *    with the view document fragment in the `data.content` event field.
 *
 * ## Event: `view.Document#clipboardOutput`
 *
 * 1. Processes `data.content` to HTML and plain text with the
 *    {@link module:engine/controller/datacontroller~DataController#htmlProcessor `DataController#htmlProcessor`}.
 * 2. Updates the `data.dataTransfer` data for `text/html` and `text/plain` with the processed data.
 * 3. For the `cut` method, calls {@link module:engine/model/model~Model#deleteContent `model.deleteContent()`}
 *    on the current selection.
 *
 * Read more about the clipboard integration in the {@glink framework/guides/deep-dive/clipboard clipboard deep dive guide}.
 *
 * @extends module:core/plugin~Plugin
 */
class ClipboardPipeline extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'ClipboardPipeline';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const view = editor.editing.view;
        view.addObserver(ClipboardObserver);
        this._setupPasteDrop();
        this._setupCopyCut();
    }
    /**
     * The clipboard paste pipeline.
     *
     * @private
     */
    _setupPasteDrop() {
        const editor = this.editor;
        const model = editor.model;
        const view = editor.editing.view;
        const viewDocument = view.document;
        // Pasting and dropping is disabled when editor is in the read-only mode.
        // See: https://github.com/ckeditor/ckeditor5-clipboard/issues/26.
        this.listenTo(viewDocument, 'clipboardInput', evt => {
            if (editor.isReadOnly) {
                evt.stop();
            }
        }, { priority: 'highest' });
        this.listenTo(viewDocument, 'clipboardInput', (evt, data) => {
            const dataTransfer = data.dataTransfer;
            let content;
            // Some feature could already inject content in the higher priority event handler (i.e., codeBlock).
            if (data.content) {
                content = data.content;
            }
            else {
                let contentData = '';
                if (dataTransfer.getData('text/html')) {
                    contentData = normalizeClipboardData(dataTransfer.getData('text/html'));
                }
                else if (dataTransfer.getData('text/plain')) {
                    contentData = plainTextToHtml(dataTransfer.getData('text/plain'));
                }
                content = this.editor.data.htmlProcessor.toView(contentData);
            }
            const eventInfo = new EventInfo(this, 'inputTransformation');
            this.fire(eventInfo, {
                content,
                dataTransfer,
                targetRanges: data.targetRanges,
                method: data.method
            });
            // If CKEditor handled the input, do not bubble the original event any further.
            // This helps external integrations recognize this fact and act accordingly.
            // https://github.com/ckeditor/ckeditor5-upload/issues/92
            if (eventInfo.stop.called) {
                evt.stop();
            }
            view.scrollToTheSelection();
        }, { priority: 'low' });
        this.listenTo(this, 'inputTransformation', (evt, data) => {
            if (data.content.isEmpty) {
                return;
            }
            const dataController = this.editor.data;
            // Convert the pasted content into a model document fragment.
            // The conversion is contextual, but in this case an "all allowed" context is needed
            // and for that we use the $clipboardHolder item.
            const modelFragment = dataController.toModel(data.content, '$clipboardHolder');
            if (modelFragment.childCount == 0) {
                return;
            }
            evt.stop();
            // Fire content insertion event in a single change block to allow other handlers to run in the same block
            // without post-fixers called in between (i.e., the selection post-fixer).
            model.change(() => {
                this.fire('contentInsertion', {
                    content: modelFragment,
                    method: data.method,
                    dataTransfer: data.dataTransfer,
                    targetRanges: data.targetRanges
                });
            });
        }, { priority: 'low' });
        this.listenTo(this, 'contentInsertion', (evt, data) => {
            data.resultRange = model.insertContent(data.content);
        }, { priority: 'low' });
    }
    /**
     * The clipboard copy/cut pipeline.
     *
     * @private
     */
    _setupCopyCut() {
        const editor = this.editor;
        const modelDocument = editor.model.document;
        const view = editor.editing.view;
        const viewDocument = view.document;
        const onCopyCut = (evt, data) => {
            const dataTransfer = data.dataTransfer;
            data.preventDefault();
            const content = editor.data.toView(editor.model.getSelectedContent(modelDocument.selection));
            viewDocument.fire('clipboardOutput', {
                dataTransfer,
                content,
                method: evt.name
            });
        };
        this.listenTo(viewDocument, 'copy', onCopyCut, { priority: 'low' });
        this.listenTo(viewDocument, 'cut', (evt, data) => {
            // Cutting is disabled when editor is in the read-only mode.
            // See: https://github.com/ckeditor/ckeditor5-clipboard/issues/26.
            if (editor.isReadOnly) {
                data.preventDefault();
            }
            else {
                onCopyCut(evt, data);
            }
        }, { priority: 'low' });
        this.listenTo(viewDocument, 'clipboardOutput', (evt, data) => {
            if (!data.content.isEmpty) {
                data.dataTransfer.setData('text/html', this.editor.data.htmlProcessor.toData(data.content));
                data.dataTransfer.setData('text/plain', viewToPlainText(data.content));
            }
            if (data.method == 'cut') {
                editor.model.deleteContent(modelDocument.selection);
            }
        }, { priority: 'low' });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-enter/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * Returns attributes that should be preserved on the enter key.
 *
 * Filtering is realized based on `copyOnEnter` attribute property. Read more about attribute properties
 * {@link module:engine/model/schema~Schema#setAttributeProperties here}.
 *
 * @param {module:engine/model/schema~Schema} schema
 * @param {Iterable.<*>} allAttributes attributes to filter.
 * @returns {Iterable.<*>}
 */
function* getCopyOnEnterAttributes(schema, allAttributes) {
    for (const attribute of allAttributes) {
        if (attribute && schema.getAttributeProperties(attribute[0]).copyOnEnter) {
            yield attribute;
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-enter/src/entercommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module enter/entercommand
 */


/**
 * Enter command. It is used by the {@link module:enter/enter~Enter Enter feature} to handle the <kbd>Enter</kbd> key.
 *
 * @extends module:core/command~Command
 */
class EnterCommand extends command_Command {
    /**
     * @inheritDoc
     */
    execute() {
        const model = this.editor.model;
        const doc = model.document;
        model.change(writer => {
            enterBlock(this.editor.model, writer, doc.selection, model.schema);
            this.fire('afterExecute', { writer });
        });
    }
}
// Creates a new block in the way that the <kbd>Enter</kbd> key is expected to work.
//
// @param {module:engine/model~Model} model
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// Selection on which the action should be performed.
// @param {module:engine/model/schema~Schema} schema
function enterBlock(model, writer, selection, schema) {
    const isSelectionEmpty = selection.isCollapsed;
    const range = selection.getFirstRange();
    const startElement = range.start.parent;
    const endElement = range.end.parent;
    // Don't touch the roots and other limit elements.
    if (schema.isLimit(startElement) || schema.isLimit(endElement)) {
        // Delete the selected content but only if inside a single limit element.
        // Abort, when crossing limit elements boundary (e.g. <limit1>x[x</limit1>donttouchme<limit2>y]y</limit2>).
        // This is an edge case and it's hard to tell what should actually happen because such a selection
        // is not entirely valid.
        if (!isSelectionEmpty && startElement == endElement) {
            model.deleteContent(selection);
        }
        return;
    }
    if (isSelectionEmpty) {
        const attributesToCopy = getCopyOnEnterAttributes(writer.model.schema, selection.getAttributes());
        splitBlock(writer, range.start);
        writer.setSelectionAttribute(attributesToCopy);
    }
    else {
        const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
        const isContainedWithinOneElement = (startElement == endElement);
        model.deleteContent(selection, { leaveUnmerged });
        if (leaveUnmerged) {
            // Partially selected elements.
            //
            // <h>x[xx]x</h>		-> <h>x^x</h>			-> <h>x</h><h>^x</h>
            if (isContainedWithinOneElement) {
                splitBlock(writer, selection.focus);
            }
            // Selection over multiple elements.
            //
            // <h>x[x</h><p>y]y<p>	-> <h>x^</h><p>y</p>	-> <h>x</h><p>^y</p>
            else {
                writer.setSelection(endElement, 0);
            }
        }
    }
}
function splitBlock(writer, splitPos) {
    writer.split(splitPos);
    writer.setSelection(splitPos.parent.nextSibling, 0);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-enter/src/enterobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module enter/enterobserver
 */



const ENTER_EVENT_TYPES = {
    insertParagraph: { isSoft: false },
    insertLineBreak: { isSoft: true }
};
/**
 * Enter observer introduces the {@link module:engine/view/document~Document#event:enter `Document#enter`} event.
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class EnterObserver extends Observer {
    /**
     * @inheritDoc
     */
    constructor(view) {
        super(view);
        const doc = this.document;
        doc.on('beforeinput', (evt, data) => {
            if (!this.isEnabled) {
                return;
            }
            const domEvent = data.domEvent;
            const enterEventSpec = ENTER_EVENT_TYPES[data.inputType];
            if (!enterEventSpec) {
                return;
            }
            const event = new BubblingEventInfo(doc, 'enter', data.targetRanges[0]);
            doc.fire(event, new DomEventData(view, domEvent, {
                isSoft: enterEventSpec.isSoft
            }));
            // Stop `beforeinput` event if `enter` event was stopped.
            // https://github.com/ckeditor/ckeditor5/issues/753
            if (event.stop.called) {
                evt.stop();
            }
        });
    }
    /**
     * @inheritDoc
     */
    observe() { }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-enter/src/enter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module enter/enter
 */



/**
 * This plugin handles the <kbd>Enter</kbd> key (hard line break) in the editor.
 *
 * See also the {@link module:enter/shiftenter~ShiftEnter} plugin.
 *
 * For more information about this feature see the {@glink api/enter package page}.
 *
 * @extends module:core/plugin~Plugin
 */
class enter_Enter extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Enter';
    }
    init() {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        view.addObserver(EnterObserver);
        editor.commands.add('enter', new EnterCommand(editor));
        this.listenTo(viewDocument, 'enter', (evt, data) => {
            // When not in composition, we handle the action, so prevent the default one.
            // When in composition, it's the browser who modify the DOM (renderer is disabled).
            if (!viewDocument.isComposing) {
                data.preventDefault();
            }
            // The soft enter key is handled by the ShiftEnter plugin.
            if (data.isSoft) {
                return;
            }
            editor.execute('enter');
            view.scrollToTheSelection();
        }, { priority: 'low' });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/highlightstack.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/highlightstack
 */

/**
 * Class used to handle correct order of highlights on elements.
 *
 * When different highlights are applied to same element correct order should be preserved:
 *
 * * highlight with highest priority should be applied,
 * * if two highlights have same priority - sort by CSS class provided in
 * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor}.
 *
 * This way, highlight will be applied with the same rules it is applied on texts.
 */
class HighlightStack extends Emitter {
    /**
     * Creates class instance.
     */
    constructor() {
        super();
        this._stack = [];
    }
    /**
     * Adds highlight descriptor to the stack.
     *
     * @fires change:top
     * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer
     */
    add(descriptor, writer) {
        const stack = this._stack;
        // Save top descriptor and insert new one. If top is changed - fire event.
        const oldTop = stack[0];
        this._insertDescriptor(descriptor);
        const newTop = stack[0];
        // When new object is at the top and stores different information.
        if (oldTop !== newTop && !compareDescriptors(oldTop, newTop)) {
            this.fire('change:top', {
                oldDescriptor: oldTop,
                newDescriptor: newTop,
                writer
            });
        }
    }
    /**
     * Removes highlight descriptor from the stack.
     *
     * @fires change:top
     * @param {String} id Id of the descriptor to remove.
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer
     */
    remove(id, writer) {
        const stack = this._stack;
        const oldTop = stack[0];
        this._removeDescriptor(id);
        const newTop = stack[0];
        // When new object is at the top and stores different information.
        if (oldTop !== newTop && !compareDescriptors(oldTop, newTop)) {
            this.fire('change:top', {
                oldDescriptor: oldTop,
                newDescriptor: newTop,
                writer
            });
        }
    }
    /**
     * Inserts given descriptor in correct place in the stack. It also takes care about updating information when
     * descriptor with same id is already present.
     *
     * @private
     * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
     */
    _insertDescriptor(descriptor) {
        const stack = this._stack;
        const index = stack.findIndex(item => item.id === descriptor.id);
        // Inserting exact same descriptor - do nothing.
        if (compareDescriptors(descriptor, stack[index])) {
            return;
        }
        // If descriptor with same id but with different information is on the stack - remove it.
        if (index > -1) {
            stack.splice(index, 1);
        }
        // Find correct place to insert descriptor in the stack.
        // It have different information (for example priority) so it must be re-inserted in correct place.
        let i = 0;
        while (stack[i] && shouldABeBeforeB(stack[i], descriptor)) {
            i++;
        }
        stack.splice(i, 0, descriptor);
    }
    /**
     * Removes descriptor with given id from the stack.
     *
     * @private
     * @param {String} id Descriptor's id.
     */
    _removeDescriptor(id) {
        const stack = this._stack;
        const index = stack.findIndex(item => item.id === id);
        // If descriptor with same id is on the list - remove it.
        if (index > -1) {
            stack.splice(index, 1);
        }
    }
}
// Compares two descriptors by checking their priority and class list.
//
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} a
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} b
// @returns {Boolean} Returns true if both descriptors are defined and have same priority and classes.
function compareDescriptors(a, b) {
    return a && b && a.priority == b.priority && classesToString(a.classes) == classesToString(b.classes);
}
// Checks whenever first descriptor should be placed in the stack before second one.
//
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} a
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} b
// @returns {Boolean}
function shouldABeBeforeB(a, b) {
    if (a.priority > b.priority) {
        return true;
    }
    else if (a.priority < b.priority) {
        return false;
    }
    // When priorities are equal and names are different - use classes to compare.
    return classesToString(a.classes) > classesToString(b.classes);
}
// Converts CSS classes passed with {@link module:engine/conversion/downcasthelpers~HighlightDescriptor} to
// sorted string.
//
// @param {String|Array<String>} descriptor
// @returns {String}
function classesToString(classes) {
    return Array.isArray(classes) ? classes.sort().join(',') : classes;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/theme/icons/drag-handle.svg
/* harmony default export */ const drag_handle = ("<svg viewBox=\"0 0 16 16\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M4 0v1H1v3H0V.5A.5.5 0 0 1 .5 0H4zm8 0h3.5a.5.5 0 0 1 .5.5V4h-1V1h-3V0zM4 16H.5a.5.5 0 0 1-.5-.5V12h1v3h3v1zm8 0v-1h3v-3h1v3.5a.5.5 0 0 1-.5.5H12z\"/><path fill-opacity=\".256\" d=\"M1 1h14v14H1z\"/><g class=\"ck-icon__selected-indicator\"><path d=\"M7 0h2v1H7V0zM0 7h1v2H0V7zm15 0h1v2h-1V7zm-8 8h2v1H7v-1z\"/><path fill-opacity=\".254\" d=\"M1 1h14v14H1z\"/></g></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/utils
 */







/**
 * CSS class added to each widget element.
 *
 * @const {String}
 */
const WIDGET_CLASS_NAME = 'ck-widget';
/**
 * CSS class added to currently selected widget element.
 *
 * @const {String}
 */
const WIDGET_SELECTED_CLASS_NAME = 'ck-widget_selected';
/**
 * Returns `true` if given {@link module:engine/view/node~Node} is an {@link module:engine/view/element~Element} and a widget.
 *
 * @param {module:engine/view/node~Node} node
 * @returns {Boolean}
 */
function isWidget(node) {
    if (!node.is('element')) {
        return false;
    }
    return !!node.getCustomProperty('widget');
}
/**
 * Converts the given {@link module:engine/view/element~Element} to a widget in the following way:
 *
 * * sets the `contenteditable` attribute to `"false"`,
 * * adds the `ck-widget` CSS class,
 * * adds a custom {@link module:engine/view/element~Element#getFillerOffset `getFillerOffset()`} method returning `null`,
 * * adds a custom property allowing to recognize widget elements by using {@link ~isWidget `isWidget()`},
 * * implements the {@link ~setHighlightHandling view highlight on widgets}.
 *
 * This function needs to be used in conjunction with
 * {@link module:engine/conversion/downcasthelpers~DowncastHelpers downcast conversion helpers}
 * like {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement `elementToElement()`}.
 * Moreover, typically you will want to use `toWidget()` only for `editingDowncast`, while keeping the `dataDowncast` clean.
 *
 * For example, in order to convert a `<widget>` model element to `<div class="widget">` in the view, you can define
 * such converters:
 *
 *		editor.conversion.for( 'editingDowncast' )
 *			.elementToElement( {
 *				model: 'widget',
 *				view: ( modelItem, { writer } ) => {
 *					const div = writer.createContainerElement( 'div', { class: 'widget' } );
 *
 *					return toWidget( div, writer, { label: 'some widget' } );
 *				}
 *			} );
 *
 *		editor.conversion.for( 'dataDowncast' )
 *			.elementToElement( {
 *				model: 'widget',
 *				view: ( modelItem, { writer } ) => {
 *					return writer.createContainerElement( 'div', { class: 'widget' } );
 *				}
 *			} );
 *
 * See the full source code of the widget (with a nested editable) schema definition and converters in
 * [this sample](https://github.com/ckeditor/ckeditor5-widget/blob/master/tests/manual/widget-with-nestededitable.js).
 *
 * @param {module:engine/view/element~Element} element
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @param {Object} [options={}]
 * @param {String|Function} [options.label] Element's label provided to the {@link ~setLabel} function. It can be passed as
 * a plain string or a function returning a string. It represents the widget for assistive technologies (like screen readers).
 * @param {Boolean} [options.hasSelectionHandle=false] If `true`, the widget will have a selection handle added.
 * @returns {module:engine/view/element~Element} Returns the same element.
 */
function toWidget(element, writer, options = {}) {
    if (!element.is('containerElement')) {
        /**
         * The element passed to `toWidget()` must be a {@link module:engine/view/containerelement~ContainerElement}
         * instance.
         *
         * @error widget-to-widget-wrong-element-type
         * @param {String} element The view element passed to `toWidget()`.
         */
        throw new CKEditorError('widget-to-widget-wrong-element-type', null, { element });
    }
    writer.setAttribute('contenteditable', 'false', element);
    writer.addClass(WIDGET_CLASS_NAME, element);
    writer.setCustomProperty('widget', true, element);
    element.getFillerOffset = utils_getFillerOffset;
    if (options.label) {
        setLabel(element, options.label, writer);
    }
    if (options.hasSelectionHandle) {
        addSelectionHandle(element, writer);
    }
    setHighlightHandling(element, writer);
    return element;
}
// Default handler for adding a highlight on a widget.
// It adds CSS class and attributes basing on the given highlight descriptor.
//
// @param {module:engine/view/element~Element} element
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function addHighlight(element, descriptor, writer) {
    if (descriptor.classes) {
        writer.addClass(toArray(descriptor.classes), element);
    }
    if (descriptor.attributes) {
        for (const key in descriptor.attributes) {
            writer.setAttribute(key, descriptor.attributes[key], element);
        }
    }
}
// Default handler for removing a highlight from a widget.
// It removes CSS class and attributes basing on the given highlight descriptor.
//
// @param {module:engine/view/element~Element} element
// @param {module:engine/conversion/downcasthelpers~HighlightDescriptor} descriptor
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function utils_removeHighlight(element, descriptor, writer) {
    if (descriptor.classes) {
        writer.removeClass(toArray(descriptor.classes), element);
    }
    if (descriptor.attributes) {
        for (const key in descriptor.attributes) {
            writer.removeAttribute(key, element);
        }
    }
}
/**
 * Sets highlight handling methods. Uses {@link module:widget/highlightstack~HighlightStack} to
 * properly determine which highlight descriptor should be used at given time.
 *
 * @param {module:engine/view/element~Element} element
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @param {Function} [add]
 * @param {Function} [remove]
 */
function setHighlightHandling(element, writer, add = addHighlight, remove = utils_removeHighlight) {
    const stack = new HighlightStack();
    stack.on('change:top', (evt, data) => {
        if (data.oldDescriptor) {
            remove(element, data.oldDescriptor, data.writer);
        }
        if (data.newDescriptor) {
            add(element, data.newDescriptor, data.writer);
        }
    });
    const addHighlightCallback = (element, descriptor, writer) => stack.add(descriptor, writer);
    const removeHighlightCallback = (element, id, writer) => stack.remove(id, writer);
    writer.setCustomProperty('addHighlight', addHighlightCallback, element);
    writer.setCustomProperty('removeHighlight', removeHighlightCallback, element);
}
/**
 * Sets label for given element.
 * It can be passed as a plain string or a function returning a string. Function will be called each time label is retrieved by
 * {@link ~getLabel `getLabel()`}.
 *
 * @param {module:engine/view/element~Element} element
 * @param {String|Function} labelOrCreator
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 */
function setLabel(element, labelOrCreator, writer) {
    writer.setCustomProperty('widgetLabel', labelOrCreator, element);
}
/**
 * Returns the label of the provided element.
 *
 * @param {module:engine/view/element~Element} element
 * @returns {String}
 */
function getLabel(element) {
    const labelCreator = element.getCustomProperty('widgetLabel');
    if (!labelCreator) {
        return '';
    }
    return typeof labelCreator == 'function' ? labelCreator() : labelCreator;
}
/**
 * Adds functionality to the provided {@link module:engine/view/editableelement~EditableElement} to act as a widget's editable:
 *
 * * sets the `contenteditable` attribute to `true` when {@link module:engine/view/editableelement~EditableElement#isReadOnly} is `false`,
 * otherwise sets it to `false`,
 * * adds the `ck-editor__editable` and `ck-editor__nested-editable` CSS classes,
 * * adds the `ck-editor__nested-editable_focused` CSS class when the editable is focused and removes it when it is blurred.
 * * implements the {@link ~setHighlightHandling view highlight on widget's editable}.
 *
 * Similarly to {@link ~toWidget `toWidget()`} this function should be used in `editingDowncast` only and it is usually
 * used together with {@link module:engine/conversion/downcasthelpers~DowncastHelpers#elementToElement `elementToElement()`}.
 *
 * For example, in order to convert a `<nested>` model element to `<div class="nested">` in the view, you can define
 * such converters:
 *
 *		editor.conversion.for( 'editingDowncast' )
 *			.elementToElement( {
 *				model: 'nested',
 *				view: ( modelItem, { writer } ) => {
 *					const div = writer.createEditableElement( 'div', { class: 'nested' } );
 *
 *					return toWidgetEditable( nested, writer, { label: 'label for editable' } );
 *				}
 *			} );
 *
 *		editor.conversion.for( 'dataDowncast' )
 *			.elementToElement( {
 *				model: 'nested',
 *				view: ( modelItem, { writer } ) => {
 *					return writer.createContainerElement( 'div', { class: 'nested' } );
 *				}
 *			} );
 *
 * See the full source code of the widget (with nested editable) schema definition and converters in
 * [this sample](https://github.com/ckeditor/ckeditor5-widget/blob/master/tests/manual/widget-with-nestededitable.js).
 *
 * @param {module:engine/view/editableelement~EditableElement} editable
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @param {Object} [options] Additional options.
 * @param {String} [options.label] Editable's label used by assistive technologies (e.g. screen readers).
 * @returns {module:engine/view/editableelement~EditableElement} Returns the same element that was provided in the `editable` parameter
 */
function toWidgetEditable(editable, writer, options = {}) {
    writer.addClass(['ck-editor__editable', 'ck-editor__nested-editable'], editable);
    writer.setAttribute('role', 'textbox', editable);
    if (options.label) {
        writer.setAttribute('aria-label', options.label, editable);
    }
    // Set initial contenteditable value.
    writer.setAttribute('contenteditable', editable.isReadOnly ? 'false' : 'true', editable);
    // Bind the contenteditable property to element#isReadOnly.
    editable.on('change:isReadOnly', (evt, property, is) => {
        writer.setAttribute('contenteditable', is ? 'false' : 'true', editable);
    });
    editable.on('change:isFocused', (evt, property, is) => {
        if (is) {
            writer.addClass('ck-editor__nested-editable_focused', editable);
        }
        else {
            writer.removeClass('ck-editor__nested-editable_focused', editable);
        }
    });
    setHighlightHandling(editable, writer);
    return editable;
}
/**
 * Returns a model range which is optimal (in terms of UX) for inserting a widget block.
 *
 * For instance, if a selection is in the middle of a paragraph, the collapsed range before this paragraph
 * will be returned so that it is not split. If the selection is at the end of a paragraph,
 * the collapsed range after this paragraph will be returned.
 *
 * Note: If the selection is placed in an empty block, the range in that block will be returned. If that range
 * is then passed to {@link module:engine/model/model~Model#insertContent}, the block will be fully replaced
 * by the inserted widget block.
 *
 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
 * The selection based on which the insertion position should be calculated.
 * @param {module:engine/model/model~Model} model Model instance.
 * @returns {module:engine/model/range~Range} The optimal range.
 */
function utils_findOptimalInsertionRange(selection, model) {
    const selectedElement = selection.getSelectedElement();
    if (selectedElement) {
        const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(selection);
        // If the WidgetTypeAround "fake caret" is displayed, use its position for the insertion
        // to provide the most predictable UX (https://github.com/ckeditor/ckeditor5/issues/7438).
        if (typeAroundFakeCaretPosition) {
            return model.createRange(model.createPositionAt(selectedElement, typeAroundFakeCaretPosition));
        }
    }
    return findOptimalInsertionRange(selection, model);
}
/**
 * A util to be used in order to map view positions to correct model positions when implementing a widget
 * which renders non-empty view element for an empty model element.
 *
 * For example:
 *
 *		// Model:
 *		<placeholder type="name"></placeholder>
 *
 *		// View:
 *		<span class="placeholder">name</span>
 *
 * In such case, view positions inside `<span>` cannot be correct mapped to the model (because the model element is empty).
 * To handle mapping positions inside `<span class="placeholder">` to the model use this util as follows:
 *
 *		editor.editing.mapper.on(
 *			'viewToModelPosition',
 *			viewToModelPositionOutsideModelElement( model, viewElement => viewElement.hasClass( 'placeholder' ) )
 *		);
 *
 * The callback will try to map the view offset of selection to an expected model position.
 *
 * 1. When the position is at the end (or in the middle) of the inline widget:
 *
 *		// View:
 *		<p>foo <span class="placeholder">name|</span> bar</p>
 *
 *		// Model:
 *		<paragraph>foo <placeholder type="name"></placeholder>| bar</paragraph>
 *
 * 2. When the position is at the beginning of the inline widget:
 *
 *		// View:
 *		<p>foo <span class="placeholder">|name</span> bar</p>
 *
 *		// Model:
 *		<paragraph>foo |<placeholder type="name"></placeholder> bar</paragraph>
 *
 * @param {module:engine/model/model~Model} model Model instance on which the callback operates.
 * @param {Function} viewElementMatcher Function that is passed a view element and should return `true` if the custom mapping
 * should be applied to the given view element.
 * @return {Function}
 */
function viewToModelPositionOutsideModelElement(model, viewElementMatcher) {
    return (evt, data) => {
        const { mapper, viewPosition } = data;
        const viewParent = mapper.findMappedViewAncestor(viewPosition);
        if (!viewElementMatcher(viewParent)) {
            return;
        }
        const modelParent = mapper.toModelElement(viewParent);
        data.modelPosition = model.createPositionAt(modelParent, viewPosition.isAtStart ? 'before' : 'after');
    };
}
// Default filler offset function applied to all widget elements.
//
// @returns {null}
function utils_getFillerOffset() {
    return null;
}
// Adds a drag handle to the widget.
//
// @param {module:engine/view/containerelement~ContainerElement}
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function addSelectionHandle(widgetElement, writer) {
    const selectionHandle = writer.createUIElement('div', { class: 'ck ck-widget__selection-handle' }, function (domDocument) {
        const domElement = this.toDomElement(domDocument);
        // Use the IconView from the ui library.
        const icon = new IconView();
        icon.set('content', drag_handle);
        // Render the icon view right away to append its #element to the selectionHandle DOM element.
        icon.render();
        domElement.appendChild(icon.element);
        return domElement;
    });
    // Append the selection handle into the widget wrapper.
    writer.insert(writer.createPositionAt(widgetElement, 0), selectionHandle);
    writer.addClass(['ck-widget_with-selection-handle'], widgetElement);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widgettypearound/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/widgettypearound/utils
 */

/**
 * The name of the type around model selection attribute responsible for
 * displaying a fake caret next to a selected widget.
 */
const TYPE_AROUND_SELECTION_ATTRIBUTE = 'widget-type-around';
/**
 * Checks if an element is a widget that qualifies to get the widget type around UI.
 *
 * @param {module:engine/view/element~Element} viewElement
 * @param {module:engine/model/element~Element} modelElement
 * @param {module:engine/model/schema~Schema} schema
 * @returns {Boolean}
 */
function isTypeAroundWidget(viewElement, modelElement, schema) {
    return !!viewElement && isWidget(viewElement) && !schema.isInline(modelElement);
}
/**
 * For the passed HTML element, this helper finds the closest widget type around button ancestor.
 *
 * @param {HTMLElement} domElement
 * @returns {HTMLElement|null}
 */
function getClosestTypeAroundDomButton(domElement) {
    return domElement.closest('.ck-widget__type-around__button');
}
/**
 * For the passed widget type around button element, this helper determines at which position
 * the paragraph would be inserted into the content if, for instance, the button was
 * clicked by the user.
 *
 * @param {HTMLElement} domElement
 * @returns {'before'|'after'} The position of the button.
 */
function getTypeAroundButtonPosition(domElement) {
    return domElement.classList.contains('ck-widget__type-around__button_before') ? 'before' : 'after';
}
/**
 * For the passed HTML element, this helper returns the closest view widget ancestor.
 *
 * @param {HTMLElement} domElement
 * @param {module:engine/view/domconverter~DomConverter} domConverter
 * @returns {module:engine/view/element~Element}
 */
function getClosestWidgetViewElement(domElement, domConverter) {
    const widgetDomElement = domElement.closest('.ck-widget');
    return domConverter.mapDomToView(widgetDomElement);
}
/**
 * For the passed selection instance, it returns the position of the fake caret displayed next to a widget.
 *
 * **Note**: If the fake caret is not currently displayed, `null` is returned.
 *
 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
 * @returns {'before'|'after'|null} The position of the fake caret or `null` when none is present.
 */
function getTypeAroundFakeCaretPosition(selection) {
    return selection.getAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/theme/icons/return-arrow.svg
/* harmony default export */ const return_arrow = ("<svg viewBox=\"0 0 10 8\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M9.055.263v3.972h-6.77M1 4.216l2-2.038m-2 2 2 2.038\"/></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-widget/theme/widgettypearound.css
var widgettypearound = __webpack_require__(5137);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/theme/widgettypearound.css

            

var widgettypearound_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

widgettypearound_options.insert = "head";
widgettypearound_options.singleton = true;

var widgettypearound_update = injectStylesIntoStyleTag_default()(widgettypearound/* default */.Z, widgettypearound_options);



/* harmony default export */ const theme_widgettypearound = (widgettypearound/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widgettypearound/widgettypearound.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/* global DOMParser */
/**
 * @module widget/widgettypearound
 */










const POSSIBLE_INSERTION_POSITIONS = ['before', 'after'];
// Do the SVG parsing once and then clone the result <svg> DOM element for each new button.
const RETURN_ARROW_ICON_ELEMENT = new DOMParser().parseFromString(return_arrow, 'image/svg+xml').firstChild;
const PLUGIN_DISABLED_EDITING_ROOT_CLASS = 'ck-widget__type-around_disabled';
/**
 * A plugin that allows users to type around widgets where normally it is impossible to place the caret due
 * to limitations of web browsers. These "tight spots" occur, for instance, before (or after) a widget being
 * the first (or last) child of its parent or between two block widgets.
 *
 * This plugin extends the {@link module:widget/widget~Widget `Widget`} plugin and injects the user interface
 * with two buttons into each widget instance in the editor. Each of the buttons can be clicked by the
 * user if the widget is next to the "tight spot". Once clicked, a paragraph is created with the selection anchored
 * in it so that users can type (or insert content, paste, etc.) straight away.
 *
 * @extends module:core/plugin~Plugin
 */
class WidgetTypeAround extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'WidgetTypeAround';
    }
    /**
     * @inheritDoc
     */
    static get requires() {
        return [enter_Enter, delete_Delete];
    }
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        /**
         * A reference to the model widget element that has the fake caret active
         * on either side of it. It is later used to remove CSS classes associated with the fake caret
         * when the widget no longer needs it.
         *
         * @private
         * @member {module:engine/model/element~Element|null}
         */
        this._currentFakeCaretModelElement = null;
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const editingView = editor.editing.view;
        // Set a CSS class on the view editing root when the plugin is disabled so all the buttons
        // and lines visually disappear. All the interactions are disabled in individual plugin methods.
        this.on('change:isEnabled', (evt, data, isEnabled) => {
            editingView.change(writer => {
                for (const root of editingView.document.roots) {
                    if (isEnabled) {
                        writer.removeClass(PLUGIN_DISABLED_EDITING_ROOT_CLASS, root);
                    }
                    else {
                        writer.addClass(PLUGIN_DISABLED_EDITING_ROOT_CLASS, root);
                    }
                }
            });
            if (!isEnabled) {
                editor.model.change(writer => {
                    writer.removeSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE);
                });
            }
        });
        this._enableTypeAroundUIInjection();
        this._enableInsertingParagraphsOnButtonClick();
        this._enableInsertingParagraphsOnEnterKeypress();
        this._enableInsertingParagraphsOnTypingKeystroke();
        this._enableTypeAroundFakeCaretActivationUsingKeyboardArrows();
        this._enableDeleteIntegration();
        this._enableInsertContentIntegration();
        this._enableInsertObjectIntegration();
        this._enableDeleteContentIntegration();
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this._currentFakeCaretModelElement = null;
    }
    /**
     * Inserts a new paragraph next to a widget element with the selection anchored in it.
     *
     * **Note**: This method is heavily user-oriented and will both focus the editing view and scroll
     * the viewport to the selection in the inserted paragraph.
     *
     * @protected
     * @param {module:engine/model/element~Element} widgetModelElement The model widget element next to which a paragraph is inserted.
     * @param {'before'|'after'} position The position where the paragraph is inserted. Either `'before'` or `'after'` the widget.
     */
    _insertParagraph(widgetModelElement, position) {
        const editor = this.editor;
        const editingView = editor.editing.view;
        const attributesToCopy = editor.model.schema.getAttributesWithProperty(widgetModelElement, 'copyOnReplace', true);
        editor.execute('insertParagraph', {
            position: editor.model.createPositionAt(widgetModelElement, position),
            attributes: attributesToCopy
        });
        editingView.focus();
        editingView.scrollToTheSelection();
    }
    /**
     * A wrapper for the {@link module:utils/emittermixin~EmitterMixin#listenTo} method that executes the callbacks only
     * when the plugin {@link #isEnabled is enabled}.
     *
     * @private
     * @param {module:utils/emittermixin~Emitter} emitter The object that fires the event.
     * @param {String} event The name of the event.
     * @param {Function} callback The function to be called on event.
     * @param {Object} [options={}] Additional options.
     * @param {module:utils/priorities~PriorityString|Number} [options.priority='normal'] The priority of this event callback. The higher
     * the priority value the sooner the callback will be fired. Events having the same priority are called in the
     * order they were added.
     */
    _listenToIfEnabled(emitter, event, callback, options) {
        this.listenTo(emitter, event, (...args) => {
            // Do not respond if the plugin is disabled.
            if (this.isEnabled) {
                callback(...args);
            }
        }, options);
    }
    /**
     * Similar to {@link #_insertParagraph}, this method inserts a paragraph except that it
     * does not expect a position. Instead, it performs the insertion next to a selected widget
     * according to the `widget-type-around` model selection attribute value (fake caret position).
     *
     * Because this method requires the `widget-type-around` attribute to be set,
     * the insertion can only happen when the widget's fake caret is active (e.g. activated
     * using the keyboard).
     *
     * @private
     * @returns {Boolean} Returns `true` when the paragraph was inserted (the attribute was present) and `false` otherwise.
     */
    _insertParagraphAccordingToFakeCaretPosition() {
        const editor = this.editor;
        const model = editor.model;
        const modelSelection = model.document.selection;
        const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(modelSelection);
        if (!typeAroundFakeCaretPosition) {
            return false;
        }
        // @if CK_DEBUG_TYPING // if ( window.logCKETyping ) {
        // @if CK_DEBUG_TYPING // 	console.info( '%c[WidgetTypeAround]%c Fake caret -> insert paragraph',
        // @if CK_DEBUG_TYPING // 		'font-weight: bold; color: green', ''
        // @if CK_DEBUG_TYPING // 	);
        // @if CK_DEBUG_TYPING // }
        const selectedModelElement = modelSelection.getSelectedElement();
        this._insertParagraph(selectedModelElement, typeAroundFakeCaretPosition);
        return true;
    }
    /**
     * Creates a listener in the editing conversion pipeline that injects the widget type around
     * UI into every single widget instance created in the editor.
     *
     * The UI is delivered as a {@link module:engine/view/uielement~UIElement}
     * wrapper which renders DOM buttons that users can use to insert paragraphs.
     *
     * @private
     */
    _enableTypeAroundUIInjection() {
        const editor = this.editor;
        const schema = editor.model.schema;
        const t = editor.locale.t;
        const buttonTitles = {
            before: t('Insert paragraph before block'),
            after: t('Insert paragraph after block')
        };
        editor.editing.downcastDispatcher.on('insert', (evt, data, conversionApi) => {
            const viewElement = conversionApi.mapper.toViewElement(data.item);
            // Filter out non-widgets and inline widgets.
            if (isTypeAroundWidget(viewElement, data.item, schema)) {
                injectUIIntoWidget(conversionApi.writer, buttonTitles, viewElement);
            }
        }, { priority: 'low' });
    }
    /**
     * Brings support for the fake caret that appears when either:
     *
     * * the selection moves to a widget from a position next to it using arrow keys,
     * * the arrow key is pressed when the widget is already selected.
     *
     * The fake caret lets the user know that they can start typing or just press
     * <kbd>Enter</kbd> to insert a paragraph at the position next to a widget as suggested by the fake caret.
     *
     * The fake caret disappears when the user changes the selection or the editor
     * gets blurred.
     *
     * The whole idea is as follows:
     *
     * 1. A user does one of the 2 scenarios described at the beginning.
     * 2. The "keydown" listener is executed and the decision is made whether to show or hide the fake caret.
     * 3. If it should show up, the `widget-type-around` model selection attribute is set indicating
     *    on which side of the widget it should appear.
     * 4. The selection dispatcher reacts to the selection attribute and sets CSS classes responsible for the
     *    fake caret on the view widget.
     * 5. If the fake caret should disappear, the selection attribute is removed and the dispatcher
     *    does the CSS class clean-up in the view.
     * 6. Additionally, `change:range` and `FocusTracker#isFocused` listeners also remove the selection
     *    attribute (the former also removes widget CSS classes).
     *
     * @private
     */
    _enableTypeAroundFakeCaretActivationUsingKeyboardArrows() {
        const editor = this.editor;
        const model = editor.model;
        const modelSelection = model.document.selection;
        const schema = model.schema;
        const editingView = editor.editing.view;
        // This is the main listener responsible for the fake caret.
        // Note: The priority must precede the default Widget class keydown handler ("high").
        this._listenToIfEnabled(editingView.document, 'arrowKey', (evt, domEventData) => {
            this._handleArrowKeyPress(evt, domEventData);
        }, { context: [isWidget, '$text'], priority: 'high' });
        // This listener makes sure the widget type around selection attribute will be gone from the model
        // selection as soon as the model range changes. This attribute only makes sense when a widget is selected
        // (and the "fake horizontal caret" is visible) so whenever the range changes (e.g. selection moved somewhere else),
        // let's get rid of the attribute so that the selection downcast dispatcher isn't even bothered.
        this._listenToIfEnabled(modelSelection, 'change:range', (evt, data) => {
            // Do not reset the selection attribute when the change was indirect.
            if (!data.directChange) {
                return;
            }
            // Get rid of the widget type around attribute of the selection on every change:range.
            // If the range changes, it means for sure, the user is no longer in the active ("fake horizontal caret") mode.
            editor.model.change(writer => {
                writer.removeSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE);
            });
        });
        // Get rid of the widget type around attribute of the selection on every document change
        // that makes widget not selected any more (i.e. widget was removed).
        this._listenToIfEnabled(model.document, 'change:data', () => {
            const selectedModelElement = modelSelection.getSelectedElement();
            if (selectedModelElement) {
                const selectedViewElement = editor.editing.mapper.toViewElement(selectedModelElement);
                if (isTypeAroundWidget(selectedViewElement, selectedModelElement, schema)) {
                    return;
                }
            }
            editor.model.change(writer => {
                writer.removeSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE);
            });
        });
        // React to changes of the model selection attribute made by the arrow keys listener.
        // If the block widget is selected and the attribute changes, downcast the attribute to special
        // CSS classes associated with the active ("fake horizontal caret") mode of the widget.
        this._listenToIfEnabled(editor.editing.downcastDispatcher, 'selection', (evt, data, conversionApi) => {
            const writer = conversionApi.writer;
            if (this._currentFakeCaretModelElement) {
                const selectedViewElement = conversionApi.mapper.toViewElement(this._currentFakeCaretModelElement);
                if (selectedViewElement) {
                    // Get rid of CSS classes associated with the active ("fake horizontal caret") mode from the view widget.
                    writer.removeClass(POSSIBLE_INSERTION_POSITIONS.map(positionToWidgetCssClass), selectedViewElement);
                    this._currentFakeCaretModelElement = null;
                }
            }
            const selectedModelElement = data.selection.getSelectedElement();
            if (!selectedModelElement) {
                return;
            }
            const selectedViewElement = conversionApi.mapper.toViewElement(selectedModelElement);
            if (!isTypeAroundWidget(selectedViewElement, selectedModelElement, schema)) {
                return;
            }
            const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(data.selection);
            if (!typeAroundFakeCaretPosition) {
                return;
            }
            writer.addClass(positionToWidgetCssClass(typeAroundFakeCaretPosition), selectedViewElement);
            // Remember the view widget that got the "fake-caret" CSS class. This class should be removed ASAP when the
            // selection changes
            this._currentFakeCaretModelElement = selectedModelElement;
        });
        this._listenToIfEnabled(editor.ui.focusTracker, 'change:isFocused', (evt, name, isFocused) => {
            if (!isFocused) {
                editor.model.change(writer => {
                    writer.removeSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE);
                });
            }
        });
        function positionToWidgetCssClass(position) {
            return `ck-widget_type-around_show-fake-caret_${position}`;
        }
    }
    /**
     * A listener executed on each "keydown" in the view document, a part of
     * {@link #_enableTypeAroundFakeCaretActivationUsingKeyboardArrows}.
     *
     * It decides whether the arrow keypress should activate the fake caret or not (also whether it should
     * be deactivated).
     *
     * The fake caret activation is done by setting the `widget-type-around` model selection attribute
     * in this listener, and stopping and preventing the event that would normally be handled by the widget
     * plugin that is responsible for the regular keyboard navigation near/across all widgets (that
     * includes inline widgets, which are ignored by the widget type around plugin).
     *
     * @private
     */
    _handleArrowKeyPress(evt, domEventData) {
        const editor = this.editor;
        const model = editor.model;
        const modelSelection = model.document.selection;
        const schema = model.schema;
        const editingView = editor.editing.view;
        const keyCode = domEventData.keyCode;
        const isForward = isForwardArrowKeyCode(keyCode, editor.locale.contentLanguageDirection);
        const selectedViewElement = editingView.document.selection.getSelectedElement();
        const selectedModelElement = editor.editing.mapper.toModelElement(selectedViewElement);
        let shouldStopAndPreventDefault;
        // Handle keyboard navigation when a type-around-compatible widget is currently selected.
        if (isTypeAroundWidget(selectedViewElement, selectedModelElement, schema)) {
            shouldStopAndPreventDefault = this._handleArrowKeyPressOnSelectedWidget(isForward);
        }
        // Handle keyboard arrow navigation when the selection is next to a type-around-compatible widget
        // and the widget is about to be selected.
        else if (modelSelection.isCollapsed) {
            shouldStopAndPreventDefault = this._handleArrowKeyPressWhenSelectionNextToAWidget(isForward);
        }
        // Handle collapsing a non-collapsed selection that is wider than on a single widget.
        else if (!domEventData.shiftKey) {
            shouldStopAndPreventDefault = this._handleArrowKeyPressWhenNonCollapsedSelection(isForward);
        }
        if (shouldStopAndPreventDefault) {
            domEventData.preventDefault();
            evt.stop();
        }
    }
    /**
     * Handles the keyboard navigation on "keydown" when a widget is currently selected and activates or deactivates
     * the fake caret for that widget, depending on the current value of the `widget-type-around` model
     * selection attribute and the direction of the pressed arrow key.
     *
     * @private
     * @param {Boolean} isForward `true` when the pressed arrow key was responsible for the forward model selection movement
     * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
     * @returns {Boolean} Returns `true` when the keypress was handled and no other keydown listener of the editor should
     * process the event any further. Returns `false` otherwise.
     */
    _handleArrowKeyPressOnSelectedWidget(isForward) {
        const editor = this.editor;
        const model = editor.model;
        const modelSelection = model.document.selection;
        const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(modelSelection);
        return model.change(writer => {
            // If the fake caret is displayed...
            if (typeAroundFakeCaretPosition) {
                const isLeavingWidget = typeAroundFakeCaretPosition === (isForward ? 'after' : 'before');
                // If the keyboard arrow works against the value of the selection attribute...
                // then remove the selection attribute but prevent default DOM actions
                // and do not let the Widget plugin listener move the selection. This brings
                // the widget back to the state, for instance, like if was selected using the mouse.
                //
                // **Note**: If leaving the widget when the fake caret is active, then the default
                // Widget handler will change the selection and, in turn, this will automatically discard
                // the selection attribute.
                if (!isLeavingWidget) {
                    writer.removeSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE);
                    return true;
                }
            }
            // If the fake caret wasn't displayed, let's set it now according to the direction of the arrow
            // key press. This also means we cannot let the Widget plugin listener move the selection.
            else {
                writer.setSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE, isForward ? 'after' : 'before');
                return true;
            }
            return false;
        });
    }
    /**
     * Handles the keyboard navigation on "keydown" when **no** widget is selected but the selection is **directly** next
     * to one and upon the fake caret should become active for this widget upon arrow keypress
     * (AKA entering/selecting the widget).
     *
     * **Note**: This code mirrors the implementation from the widget plugin but also adds the selection attribute.
     * Unfortunately, there is no safe way to let the widget plugin do the selection part first and then just set the
     * selection attribute here in the widget type around plugin. This is why this code must duplicate some from the widget plugin.
     *
     * @private
     * @param {Boolean} isForward `true` when the pressed arrow key was responsible for the forward model selection movement
     * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
     * @returns {Boolean} Returns `true` when the keypress was handled and no other keydown listener of the editor should
     * process the event any further. Returns `false` otherwise.
     */
    _handleArrowKeyPressWhenSelectionNextToAWidget(isForward) {
        const editor = this.editor;
        const model = editor.model;
        const schema = model.schema;
        const widgetPlugin = editor.plugins.get('Widget');
        // This is the widget the selection is about to be set on.
        const modelElementNextToSelection = widgetPlugin._getObjectElementNextToSelection(isForward);
        const viewElementNextToSelection = editor.editing.mapper.toViewElement(modelElementNextToSelection);
        if (isTypeAroundWidget(viewElementNextToSelection, modelElementNextToSelection, schema)) {
            model.change(writer => {
                widgetPlugin._setSelectionOverElement(modelElementNextToSelection);
                writer.setSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE, isForward ? 'before' : 'after');
            });
            // The change() block above does the same job as the Widget plugin. The event can
            // be safely canceled.
            return true;
        }
        return false;
    }
    /**
     * Handles the keyboard navigation on "keydown" when a widget is currently selected (together with some other content)
     * and the widget is the first or last element in the selection. It activates or deactivates the fake caret for that widget.
     *
     * @private
     * @param {Boolean} isForward `true` when the pressed arrow key was responsible for the forward model selection movement
     * as in {@link module:utils/keyboard~isForwardArrowKeyCode}.
     * @returns {Boolean} Returns `true` when the keypress was handled and no other keydown listener of the editor should
     * process the event any further. Returns `false` otherwise.
     */
    _handleArrowKeyPressWhenNonCollapsedSelection(isForward) {
        const editor = this.editor;
        const model = editor.model;
        const schema = model.schema;
        const mapper = editor.editing.mapper;
        const modelSelection = model.document.selection;
        const selectedModelNode = isForward ?
            modelSelection.getLastPosition().nodeBefore :
            modelSelection.getFirstPosition().nodeAfter;
        const selectedViewNode = mapper.toViewElement(selectedModelNode);
        // There is a widget at the collapse position so collapse the selection to the fake caret on it.
        if (isTypeAroundWidget(selectedViewNode, selectedModelNode, schema)) {
            model.change(writer => {
                writer.setSelection(selectedModelNode, 'on');
                writer.setSelectionAttribute(TYPE_AROUND_SELECTION_ATTRIBUTE, isForward ? 'after' : 'before');
            });
            return true;
        }
        return false;
    }
    /**
     * Registers a `mousedown` listener for the view document which intercepts events
     * coming from the widget type around UI, which happens when a user clicks one of the buttons
     * that insert a paragraph next to a widget.
     *
     * @private
     */
    _enableInsertingParagraphsOnButtonClick() {
        const editor = this.editor;
        const editingView = editor.editing.view;
        this._listenToIfEnabled(editingView.document, 'mousedown', (evt, domEventData) => {
            const button = getClosestTypeAroundDomButton(domEventData.domTarget);
            if (!button) {
                return;
            }
            const buttonPosition = getTypeAroundButtonPosition(button);
            const widgetViewElement = getClosestWidgetViewElement(button, editingView.domConverter);
            const widgetModelElement = editor.editing.mapper.toModelElement(widgetViewElement);
            this._insertParagraph(widgetModelElement, buttonPosition);
            domEventData.preventDefault();
            evt.stop();
        });
    }
    /**
     * Creates the <kbd>Enter</kbd> key listener on the view document that allows the user to insert a paragraph
     * near the widget when either:
     *
     * * The fake caret was first activated using the arrow keys,
     * * The entire widget is selected in the model.
     *
     * In the first case, the new paragraph is inserted according to the `widget-type-around` selection
     * attribute (see {@link #_handleArrowKeyPress}).
     *
     * In the second case, the new paragraph is inserted based on whether a soft (<kbd>Shift</kbd>+<kbd>Enter</kbd>) keystroke
     * was pressed or not.
     *
     * @private
     */
    _enableInsertingParagraphsOnEnterKeypress() {
        const editor = this.editor;
        const selection = editor.model.document.selection;
        const editingView = editor.editing.view;
        this._listenToIfEnabled(editingView.document, 'enter', (evt, domEventData) => {
            // This event could be triggered from inside the widget but we are interested
            // only when the widget is selected itself.
            if (evt.eventPhase != 'atTarget') {
                return;
            }
            const selectedModelElement = selection.getSelectedElement();
            const selectedViewElement = editor.editing.mapper.toViewElement(selectedModelElement);
            const schema = editor.model.schema;
            let wasHandled;
            // First check if the widget is selected and there's a type around selection attribute associated
            // with the fake caret that would tell where to insert a new paragraph.
            if (this._insertParagraphAccordingToFakeCaretPosition()) {
                wasHandled = true;
            }
            // Then, if there is no selection attribute associated with the fake caret, check if the widget
            // simply is selected and create a new paragraph according to the keystroke (Shift+)Enter.
            else if (isTypeAroundWidget(selectedViewElement, selectedModelElement, schema)) {
                this._insertParagraph(selectedModelElement, domEventData.isSoft ? 'before' : 'after');
                wasHandled = true;
            }
            if (wasHandled) {
                domEventData.preventDefault();
                evt.stop();
            }
        }, { context: isWidget });
    }
    /**
     * Similar to the {@link #_enableInsertingParagraphsOnEnterKeypress}, it allows the user
     * to insert a paragraph next to a widget when the fake caret was activated using arrow
     * keys but it responds to typing instead of <kbd>Enter</kbd>.
     *
     * Listener enabled by this method will insert a new paragraph according to the `widget-type-around`
     * model selection attribute as the user simply starts typing, which creates the impression that the fake caret
     * behaves like a real one rendered by the browser (AKA your text appears where the caret was).
     *
     * **Note**: At the moment this listener creates 2 undo steps: one for the `insertParagraph` command
     * and another one for actual typing. It is not a disaster but this may need to be fixed
     * sooner or later.
     *
     * @private
     */
    _enableInsertingParagraphsOnTypingKeystroke() {
        const editor = this.editor;
        const viewDocument = editor.editing.view.document;
        // Note: The priority must precede the default Input plugin insertText handler.
        this._listenToIfEnabled(viewDocument, 'insertText', (evt, data) => {
            if (this._insertParagraphAccordingToFakeCaretPosition()) {
                // The view selection in the event data contains the widget. If the new paragraph
                // was inserted, modify the view selection passed along with the insertText event
                // so the default event handler in the Input plugin starts typing inside the paragraph.
                // Otherwise, the typing would be over the widget.
                data.selection = viewDocument.selection;
            }
        }, { priority: 'high' });
        if (src_env.isAndroid) {
            // On Android with English keyboard, the composition starts just by putting caret
            // at the word end or by selecting a table column. This is not a real composition started.
            // Trigger delete content on first composition key pressed.
            this._listenToIfEnabled(viewDocument, 'keydown', (evt, data) => {
                if (data.keyCode == 229) {
                    this._insertParagraphAccordingToFakeCaretPosition();
                }
            });
        }
        else {
            // Note: The priority must precede the default Input plugin compositionstart handler (to call it before delete content).
            this._listenToIfEnabled(viewDocument, 'compositionstart', () => {
                this._insertParagraphAccordingToFakeCaretPosition();
            }, { priority: 'high' });
        }
    }
    /**
     * It creates a "delete" event listener on the view document to handle cases when the <kbd>Delete</kbd> or <kbd>Backspace</kbd>
     * is pressed and the fake caret is currently active.
     *
     * The fake caret should create an illusion of a real browser caret so that when it appears before or after
     * a widget, pressing <kbd>Delete</kbd> or <kbd>Backspace</kbd> should remove a widget or delete the content
     * before or after a widget (depending on the content surrounding the widget).
     *
     * @private
     */
    _enableDeleteIntegration() {
        const editor = this.editor;
        const editingView = editor.editing.view;
        const model = editor.model;
        const schema = model.schema;
        this._listenToIfEnabled(editingView.document, 'delete', (evt, domEventData) => {
            // This event could be triggered from inside the widget but we are interested
            // only when the widget is selected itself.
            if (evt.eventPhase != 'atTarget') {
                return;
            }
            const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(model.document.selection);
            // This listener handles only these cases when the fake caret is active.
            if (!typeAroundFakeCaretPosition) {
                return;
            }
            const direction = domEventData.direction;
            const selectedModelWidget = model.document.selection.getSelectedElement();
            const isFakeCaretBefore = typeAroundFakeCaretPosition === 'before';
            const isDeleteForward = direction == 'forward';
            const shouldDeleteEntireWidget = isFakeCaretBefore === isDeleteForward;
            if (shouldDeleteEntireWidget) {
                editor.execute('delete', {
                    selection: model.createSelection(selectedModelWidget, 'on')
                });
            }
            else {
                const range = schema.getNearestSelectionRange(model.createPositionAt(selectedModelWidget, typeAroundFakeCaretPosition), direction);
                // If there is somewhere to move selection to, then there will be something to delete.
                if (range) {
                    // If the range is NOT collapsed, then we know that the range contains an object (see getNearestSelectionRange() docs).
                    if (!range.isCollapsed) {
                        model.change(writer => {
                            writer.setSelection(range);
                            editor.execute(isDeleteForward ? 'deleteForward' : 'delete');
                        });
                    }
                    else {
                        const probe = model.createSelection(range.start);
                        model.modifySelection(probe, { direction });
                        // If the range is collapsed, let's see if a non-collapsed range exists that can could be deleted.
                        // If such range exists, use the editor command because it it safe for collaboration (it merges where it can).
                        if (!probe.focus.isEqual(range.start)) {
                            model.change(writer => {
                                writer.setSelection(range);
                                editor.execute(isDeleteForward ? 'deleteForward' : 'delete');
                            });
                        }
                        // If there is no non-collapsed range to be deleted then we are sure that there is an empty element
                        // next to a widget that should be removed. "delete" and "deleteForward" commands cannot get rid of it
                        // so calling Model#deleteContent here manually.
                        else {
                            const deepestEmptyRangeAncestor = getDeepestEmptyElementAncestor(schema, range.start.parent);
                            model.deleteContent(model.createSelection(deepestEmptyRangeAncestor, 'on'), {
                                doNotAutoparagraph: true
                            });
                        }
                    }
                }
            }
            // If some content was deleted, don't let the handler from the Widget plugin kick in.
            // If nothing was deleted, then the default handler will have nothing to do anyway.
            domEventData.preventDefault();
            evt.stop();
        }, { context: isWidget });
    }
    /**
     * Attaches the {@link module:engine/model/model~Model#event:insertContent} event listener that, for instance, allows the user to paste
     * content near a widget when the fake caret is first activated using the arrow keys.
     *
     * The content is inserted according to the `widget-type-around` selection attribute (see {@link #_handleArrowKeyPress}).
     *
     * @private
     */
    _enableInsertContentIntegration() {
        const editor = this.editor;
        const model = this.editor.model;
        const documentSelection = model.document.selection;
        this._listenToIfEnabled(editor.model, 'insertContent', (evt, [content, selectable]) => {
            if (selectable && !selectable.is('documentSelection')) {
                return;
            }
            const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(documentSelection);
            if (!typeAroundFakeCaretPosition) {
                return;
            }
            evt.stop();
            return model.change(writer => {
                const selectedElement = documentSelection.getSelectedElement();
                const position = model.createPositionAt(selectedElement, typeAroundFakeCaretPosition);
                const selection = writer.createSelection(position);
                const result = model.insertContent(content, selection);
                writer.setSelection(selection);
                return result;
            });
        }, { priority: 'high' });
    }
    /**
     * Attaches the {@link module:engine/model/model~Model#event:insertObject} event listener that modifies the
     * `options.findOptimalPosition`parameter to position of fake caret in relation to selected element
     * to reflect user's intent of desired insertion position.
     *
     * The object is inserted according to the `widget-type-around` selection attribute (see {@link #_handleArrowKeyPress}).
     *
     * @private
     */
    _enableInsertObjectIntegration() {
        const editor = this.editor;
        const model = this.editor.model;
        const documentSelection = model.document.selection;
        this._listenToIfEnabled(editor.model, 'insertObject', (evt, args) => {
            const [, selectable, , options = {}] = args;
            if (selectable && !selectable.is('documentSelection')) {
                return;
            }
            const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(documentSelection);
            if (!typeAroundFakeCaretPosition) {
                return;
            }
            options.findOptimalPosition = typeAroundFakeCaretPosition;
            args[3] = options;
        }, { priority: 'high' });
    }
    /**
     * Attaches the {@link module:engine/model/model~Model#event:deleteContent} event listener to block the event when the fake
     * caret is active.
     *
     * This is required for cases that trigger {@link module:engine/model/model~Model#deleteContent `model.deleteContent()`}
     * before calling {@link module:engine/model/model~Model#insertContent `model.insertContent()`} like, for instance,
     * plain text pasting.
     *
     * @private
     */
    _enableDeleteContentIntegration() {
        const editor = this.editor;
        const model = this.editor.model;
        const documentSelection = model.document.selection;
        this._listenToIfEnabled(editor.model, 'deleteContent', (evt, [selection]) => {
            if (selection && !selection.is('documentSelection')) {
                return;
            }
            const typeAroundFakeCaretPosition = getTypeAroundFakeCaretPosition(documentSelection);
            // Disable removing the selection content while pasting plain text.
            if (typeAroundFakeCaretPosition) {
                evt.stop();
            }
        }, { priority: 'high' });
    }
}
// Injects the type around UI into a view widget instance.
//
// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
// @param {Object.<String,String>} buttonTitles
// @param {module:engine/view/element~Element} widgetViewElement
function injectUIIntoWidget(viewWriter, buttonTitles, widgetViewElement) {
    const typeAroundWrapper = viewWriter.createUIElement('div', {
        class: 'ck ck-reset_all ck-widget__type-around'
    }, function (domDocument) {
        const wrapperDomElement = this.toDomElement(domDocument);
        injectButtons(wrapperDomElement, buttonTitles);
        injectFakeCaret(wrapperDomElement);
        return wrapperDomElement;
    });
    // Inject the type around wrapper into the widget's wrapper.
    viewWriter.insert(viewWriter.createPositionAt(widgetViewElement, 'end'), typeAroundWrapper);
}
// FYI: Not using the IconView class because each instance would need to be destroyed to avoid memory leaks
// and it's pretty hard to figure out when a view (widget) is gone for good so it's cheaper to use raw
// <svg> here.
//
// @param {HTMLElement} wrapperDomElement
// @param {Object.<String,String>} buttonTitles
function injectButtons(wrapperDomElement, buttonTitles) {
    for (const position of POSSIBLE_INSERTION_POSITIONS) {
        const buttonTemplate = new Template({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-widget__type-around__button',
                    `ck-widget__type-around__button_${position}`
                ],
                title: buttonTitles[position]
            },
            children: [
                wrapperDomElement.ownerDocument.importNode(RETURN_ARROW_ICON_ELEMENT, true)
            ]
        });
        wrapperDomElement.appendChild(buttonTemplate.render());
    }
}
// @param {HTMLElement} wrapperDomElement
function injectFakeCaret(wrapperDomElement) {
    const caretTemplate = new Template({
        tag: 'div',
        attributes: {
            class: [
                'ck',
                'ck-widget__type-around__fake-caret'
            ]
        }
    });
    wrapperDomElement.appendChild(caretTemplate.render());
}
// Returns the ancestor of an element closest to the root which is empty. For instance,
// for `<baz>`:
//
//		<foo>abc<bar><baz></baz></bar></foo>
//
// it returns `<bar>`.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/model/element~Element} element
// @returns {module:engine/model/element~Element|null}
function getDeepestEmptyElementAncestor(schema, element) {
    let deepestEmptyAncestor = element;
    for (const ancestor of element.getAncestors({ parentFirst: true })) {
        if (ancestor.childCount > 1 || schema.isLimit(ancestor)) {
            break;
        }
        deepestEmptyAncestor = ancestor;
    }
    return deepestEmptyAncestor;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/verticalnavigation.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */


/**
 * @module widget/verticalnavigationhandler
 */
/**
 * Returns 'keydown' handler for up/down arrow keys that modifies the caret movement if it's in a text line next to an object.
 *
 * @param {module:engine/controller/editingcontroller~EditingController} editing The editing controller.
 * @returns {Function}
 */
function verticalNavigationHandler(editing) {
    const model = editing.model;
    return (evt, data) => {
        const arrowUpPressed = data.keyCode == keyCodes.arrowup;
        const arrowDownPressed = data.keyCode == keyCodes.arrowdown;
        const expandSelection = data.shiftKey;
        const selection = model.document.selection;
        if (!arrowUpPressed && !arrowDownPressed) {
            return;
        }
        const isForward = arrowDownPressed;
        // Navigation is in the opposite direction than the selection direction so this is shrinking of the selection.
        // Selection for sure will not approach any object.
        if (expandSelection && selectionWillShrink(selection, isForward)) {
            return;
        }
        // Find a range between selection and closest limit element.
        const range = findTextRangeFromSelection(editing, selection, isForward);
        // There is no selection position inside the limit element.
        if (!range) {
            return;
        }
        // If already at the edge of a limit element.
        if (range.isCollapsed) {
            // A collapsed selection at limit edge - nothing more to do.
            if (selection.isCollapsed) {
                return;
            }
            // A non collapsed selection is at the limit edge while expanding the selection - let others do their stuff.
            else if (expandSelection) {
                return;
            }
        }
        // If the range is a single line (there is no word wrapping) then move the selection to the position closest to the limit element.
        //
        // We can't move the selection directly to the isObject element (eg. table cell) because of dual position at the end/beginning
        // of wrapped line (it's at the same time at the end of one line and at the start of the next line).
        if (range.isCollapsed || isSingleLineRange(editing, range, isForward)) {
            model.change(writer => {
                const newPosition = isForward ? range.end : range.start;
                if (expandSelection) {
                    const newSelection = model.createSelection(selection.anchor);
                    newSelection.setFocus(newPosition);
                    writer.setSelection(newSelection);
                }
                else {
                    writer.setSelection(newPosition);
                }
            });
            evt.stop();
            data.preventDefault();
            data.stopPropagation();
        }
    };
}
// Finds the range between selection and closest limit element (in the direction of navigation).
// The position next to limit element is adjusted to the closest allowed `$text` position.
//
// Returns `null` if, according to the schema, the resulting range cannot contain a `$text` element.
//
// @param {module:engine/controller/editingcontroller~EditingController} editing The editing controller.
// @param {module:engine/model/selection~Selection} selection The current selection.
// @param {Boolean} isForward The expected navigation direction.
// @returns {module:engine/model/range~Range|null}
//
function findTextRangeFromSelection(editing, selection, isForward) {
    const model = editing.model;
    if (isForward) {
        const startPosition = selection.isCollapsed ? selection.focus : selection.getLastPosition();
        const endPosition = getNearestNonInlineLimit(model, startPosition, 'forward');
        // There is no limit element, browser should handle this.
        if (!endPosition) {
            return null;
        }
        const range = model.createRange(startPosition, endPosition);
        const lastRangePosition = getNearestTextPosition(model.schema, range, 'backward');
        if (lastRangePosition) {
            return model.createRange(startPosition, lastRangePosition);
        }
        return null;
    }
    else {
        const endPosition = selection.isCollapsed ? selection.focus : selection.getFirstPosition();
        const startPosition = getNearestNonInlineLimit(model, endPosition, 'backward');
        // There is no limit element, browser should handle this.
        if (!startPosition) {
            return null;
        }
        const range = model.createRange(startPosition, endPosition);
        const firstRangePosition = getNearestTextPosition(model.schema, range, 'forward');
        if (firstRangePosition) {
            return model.createRange(firstRangePosition, endPosition);
        }
        return null;
    }
}
// Finds the limit element position that is closest to startPosition.
//
// @param {module:engine/model/model~Model} model
// @param {<module:engine/model/position~Position>} startPosition
// @param {'forward'|'backward'} direction Search direction.
// @returns {<module:engine/model/position~Position>|null}
//
function getNearestNonInlineLimit(model, startPosition, direction) {
    const schema = model.schema;
    const range = model.createRangeIn(startPosition.root);
    const walkerValueType = direction == 'forward' ? 'elementStart' : 'elementEnd';
    for (const { previousPosition, item, type } of range.getWalker({ startPosition, direction })) {
        if (schema.isLimit(item) && !schema.isInline(item)) {
            return previousPosition;
        }
        // Stop looking for isLimit element if the next element is a block element (it is for sure not single line).
        if (type == walkerValueType && schema.isBlock(item)) {
            return null;
        }
    }
    return null;
}
// Basing on the provided range, finds the first or last (depending on `direction`) position inside the range
// that can contain `$text` (according to schema).
//
// @param {module:engine/model/schema~Schema} schema The schema.
// @param {module:engine/model/range~Range} range The range to find the position in.
// @param {'forward'|'backward'} direction Search direction.
// @returns {module:engine/model/position~Position|null} The nearest selection position.
//
function getNearestTextPosition(schema, range, direction) {
    const position = direction == 'backward' ? range.end : range.start;
    if (schema.checkChild(position, '$text')) {
        return position;
    }
    for (const { nextPosition } of range.getWalker({ direction })) {
        if (schema.checkChild(nextPosition, '$text')) {
            return nextPosition;
        }
    }
    return null;
}
// Checks if the DOM range corresponding to the provided model range renders as a single line by analyzing DOMRects
// (verifying if they visually wrap content to the next line).
//
// @param {module:engine/controller/editingcontroller~EditingController} editing The editing controller.
// @param {module:engine/model/range~Range} modelRange The current table cell content range.
// @param {Boolean} isForward The expected navigation direction.
// @returns {Boolean}
//
function isSingleLineRange(editing, modelRange, isForward) {
    const model = editing.model;
    const domConverter = editing.view.domConverter;
    // Wrapped lines contain exactly the same position at the end of current line
    // and at the beginning of next line. That position's client rect is at the end
    // of current line. In case of caret at first position of the last line that 'dual'
    // position would be detected as it's not the last line.
    if (isForward) {
        const probe = model.createSelection(modelRange.start);
        model.modifySelection(probe);
        // If the new position is at the end of the container then we can't use this position
        // because it would provide incorrect result for eg caption of image and selection
        // just before end of it. Also in this case there is no "dual" position.
        if (!probe.focus.isAtEnd && !modelRange.start.isEqual(probe.focus)) {
            modelRange = model.createRange(probe.focus, modelRange.end);
        }
    }
    const viewRange = editing.mapper.toViewRange(modelRange);
    const domRange = domConverter.viewRangeToDom(viewRange);
    const rects = rect_Rect.getDomRangeRects(domRange);
    let boundaryVerticalPosition;
    for (const rect of rects) {
        if (boundaryVerticalPosition === undefined) {
            boundaryVerticalPosition = Math.round(rect.bottom);
            continue;
        }
        // Let's check if this rect is in new line.
        if (Math.round(rect.top) >= boundaryVerticalPosition) {
            return false;
        }
        boundaryVerticalPosition = Math.max(boundaryVerticalPosition, Math.round(rect.bottom));
    }
    return true;
}
function selectionWillShrink(selection, isForward) {
    return !selection.isCollapsed && selection.isBackward == isForward;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-widget/theme/widget.css
var widget = __webpack_require__(6507);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/theme/widget.css

            

var widget_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

widget_options.insert = "head";
widget_options.singleton = true;

var widget_update = injectStylesIntoStyleTag_default()(widget/* default */.Z, widget_options);



/* harmony default export */ const theme_widget = (widget/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widget.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/widget
 */









/**
 * The widget plugin. It enables base support for widgets.
 *
 * See {@glink api/widget package page} for more details and documentation.
 *
 * This plugin enables multiple behaviors required by widgets:
 *
 * * The model to view selection converter for the editing pipeline (it handles widget custom selection rendering).
 * If a converted selection wraps around a widget element, that selection is marked as
 * {@link module:engine/view/selection~Selection#isFake fake}. Additionally, the `ck-widget_selected` CSS class
 * is added to indicate that widget has been selected.
 * * The mouse and keyboard events handling on and around widget elements.
 *
 * @extends module:core/plugin~Plugin
 */
class Widget extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Widget';
    }
    /**
     * @inheritDoc
     */
    static get requires() {
        return [WidgetTypeAround, delete_Delete];
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        /**
         * Holds previously selected widgets.
         *
         * @private
         * @type {Set.<module:engine/view/element~Element>}
         */
        this._previouslySelected = new Set();
        // Model to view selection converter.
        // Converts selection placed over widget element to fake selection.
        //
        // By default, the selection is downcasted by the engine to surround the attribute element, even though its only
        // child is an inline widget. A similar thing also happens when a collapsed marker is rendered as a UI element
        // next to an inline widget: the view selection contains both the widget and the marker.
        //
        // This prevents creating a correct fake selection when this inline widget is selected. Normalize the selection
        // in these cases based on the model:
        //
        //		[<attributeElement><inlineWidget /></attributeElement>] -> <attributeElement>[<inlineWidget />]</attributeElement>
        //		[<uiElement></uiElement><inlineWidget />] -> <uiElement></uiElement>[<inlineWidget />]
        //
        // Thanks to this:
        //
        // * fake selection can be set correctly,
        // * any logic depending on (View)Selection#getSelectedElement() also works OK.
        //
        // See https://github.com/ckeditor/ckeditor5/issues/9524.
        this.editor.editing.downcastDispatcher.on('selection', (evt, data, conversionApi) => {
            const viewWriter = conversionApi.writer;
            const modelSelection = data.selection;
            // The collapsed selection can't contain any widget.
            if (modelSelection.isCollapsed) {
                return;
            }
            const selectedModelElement = modelSelection.getSelectedElement();
            if (!selectedModelElement) {
                return;
            }
            const selectedViewElement = editor.editing.mapper.toViewElement(selectedModelElement);
            if (!isWidget(selectedViewElement)) {
                return;
            }
            if (!conversionApi.consumable.consume(modelSelection, 'selection')) {
                return;
            }
            viewWriter.setSelection(viewWriter.createRangeOn(selectedViewElement), {
                fake: true,
                label: getLabel(selectedViewElement)
            });
        });
        // Mark all widgets inside the selection with the css class.
        // This handler is registered at the 'low' priority so it's triggered after the real selection conversion.
        this.editor.editing.downcastDispatcher.on('selection', (evt, data, conversionApi) => {
            // Remove selected class from previously selected widgets.
            this._clearPreviouslySelectedWidgets(conversionApi.writer);
            const viewWriter = conversionApi.writer;
            const viewSelection = viewWriter.document.selection;
            let lastMarked = null;
            for (const range of viewSelection.getRanges()) {
                // Note: There could be multiple selected widgets in a range but no fake selection.
                // All of them must be marked as selected, for instance [<widget></widget><widget></widget>]
                for (const value of range) {
                    const node = value.item;
                    // Do not mark nested widgets in selected one. See: #4594
                    if (isWidget(node) && !isChild(node, lastMarked)) {
                        viewWriter.addClass(WIDGET_SELECTED_CLASS_NAME, node);
                        this._previouslySelected.add(node);
                        lastMarked = node;
                    }
                }
            }
        }, { priority: 'low' });
        // If mouse down is pressed on widget - create selection over whole widget.
        view.addObserver(MouseObserver);
        this.listenTo(viewDocument, 'mousedown', (...args) => this._onMousedown(...args));
        // There are two keydown listeners working on different priorities. This allows other
        // features such as WidgetTypeAround or TableKeyboard to attach their listeners in between
        // and customize the behavior even further in different content/selection scenarios.
        //
        // * The first listener handles changing the selection on arrow key press
        // if the widget is selected or if the selection is next to a widget and the widget
        // should become selected upon the arrow key press.
        //
        // * The second (late) listener makes sure the default browser action on arrow key press is
        // prevented when a widget is selected. This prevents the selection from being moved
        // from a fake selection container.
        this.listenTo(viewDocument, 'arrowKey', (...args) => {
            this._handleSelectionChangeOnArrowKeyPress(...args);
        }, { context: [isWidget, '$text'] });
        this.listenTo(viewDocument, 'arrowKey', (...args) => {
            this._preventDefaultOnArrowKeyPress(...args);
        }, { context: '$root' });
        this.listenTo(viewDocument, 'arrowKey', verticalNavigationHandler(this.editor.editing), { context: '$text' });
        // Handle custom delete behaviour.
        this.listenTo(viewDocument, 'delete', (evt, data) => {
            if (this._handleDelete(data.direction == 'forward')) {
                data.preventDefault();
                evt.stop();
            }
        }, { context: '$root' });
    }
    /**
     * Handles {@link module:engine/view/document~Document#event:mousedown mousedown} events on widget elements.
     *
     * @private
     * @param {module:utils/eventinfo~EventInfo} eventInfo
     * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
     */
    _onMousedown(eventInfo, domEventData) {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        let element = domEventData.target;
        // Do nothing for single or double click inside nested editable.
        if (isInsideNestedEditable(element)) {
            // But at least triple click inside nested editable causes broken selection in Safari.
            // For such event, we select the entire nested editable element.
            // See: https://github.com/ckeditor/ckeditor5/issues/1463.
            if ((src_env.isSafari || src_env.isGecko) && domEventData.domEvent.detail >= 3) {
                const mapper = editor.editing.mapper;
                const viewElement = element.is('attributeElement') ?
                    element.findAncestor(element => !element.is('attributeElement')) : element;
                const modelElement = mapper.toModelElement(viewElement);
                domEventData.preventDefault();
                this.editor.model.change(writer => {
                    writer.setSelection(modelElement, 'in');
                });
            }
            return;
        }
        // If target is not a widget element - check if one of the ancestors is.
        if (!isWidget(element)) {
            element = element.findAncestor(isWidget);
            if (!element) {
                return;
            }
        }
        // On Android selection would jump to the first table cell, on other devices
        // we can't block it (and don't need to) because of drag and drop support.
        if (src_env.isAndroid) {
            domEventData.preventDefault();
        }
        // Focus editor if is not focused already.
        if (!viewDocument.isFocused) {
            view.focus();
        }
        // Create model selection over widget.
        const modelElement = editor.editing.mapper.toModelElement(element);
        this._setSelectionOverElement(modelElement);
    }
    /**
     * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and changes
     * the model selection when:
     *
     * * arrow key is pressed when the widget is selected,
     * * the selection is next to a widget and the widget should become selected upon the arrow key press.
     *
     * See {@link #_preventDefaultOnArrowKeyPress}.
     *
     * @private
     * @param {module:utils/eventinfo~EventInfo} eventInfo
     * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
     */
    _handleSelectionChangeOnArrowKeyPress(eventInfo, domEventData) {
        const keyCode = domEventData.keyCode;
        const model = this.editor.model;
        const schema = model.schema;
        const modelSelection = model.document.selection;
        const objectElement = modelSelection.getSelectedElement();
        const direction = getLocalizedArrowKeyCodeDirection(keyCode, this.editor.locale.contentLanguageDirection);
        const isForward = direction == 'down' || direction == 'right';
        const isVerticalNavigation = direction == 'up' || direction == 'down';
        // If object element is selected.
        if (objectElement && schema.isObject(objectElement)) {
            const position = isForward ? modelSelection.getLastPosition() : modelSelection.getFirstPosition();
            const newRange = schema.getNearestSelectionRange(position, isForward ? 'forward' : 'backward');
            if (newRange) {
                model.change(writer => {
                    writer.setSelection(newRange);
                });
                domEventData.preventDefault();
                eventInfo.stop();
            }
            return;
        }
        // Handle collapsing of the selection when there is any widget on the edge of selection.
        // This is needed because browsers have problems with collapsing such selection.
        if (!modelSelection.isCollapsed && !domEventData.shiftKey) {
            const firstPosition = modelSelection.getFirstPosition();
            const lastPosition = modelSelection.getLastPosition();
            const firstSelectedNode = firstPosition.nodeAfter;
            const lastSelectedNode = lastPosition.nodeBefore;
            if (firstSelectedNode && schema.isObject(firstSelectedNode) || lastSelectedNode && schema.isObject(lastSelectedNode)) {
                model.change(writer => {
                    writer.setSelection(isForward ? lastPosition : firstPosition);
                });
                domEventData.preventDefault();
                eventInfo.stop();
            }
            return;
        }
        // Return if not collapsed.
        if (!modelSelection.isCollapsed) {
            return;
        }
        // If selection is next to object element.
        const objectElementNextToSelection = this._getObjectElementNextToSelection(isForward);
        if (objectElementNextToSelection && schema.isObject(objectElementNextToSelection)) {
            // Do not select an inline widget while handling up/down arrow.
            if (schema.isInline(objectElementNextToSelection) && isVerticalNavigation) {
                return;
            }
            this._setSelectionOverElement(objectElementNextToSelection);
            domEventData.preventDefault();
            eventInfo.stop();
        }
    }
    /**
     * Handles {@link module:engine/view/document~Document#event:keydown keydown} events and prevents
     * the default browser behavior to make sure the fake selection is not being moved from a fake selection
     * container.
     *
     * See {@link #_handleSelectionChangeOnArrowKeyPress}.
     *
     * @private
     * @param {module:utils/eventinfo~EventInfo} eventInfo
     * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
     */
    _preventDefaultOnArrowKeyPress(eventInfo, domEventData) {
        const model = this.editor.model;
        const schema = model.schema;
        const objectElement = model.document.selection.getSelectedElement();
        // If object element is selected.
        if (objectElement && schema.isObject(objectElement)) {
            domEventData.preventDefault();
            eventInfo.stop();
        }
    }
    /**
     * Handles delete keys: backspace and delete.
     *
     * @private
     * @param {Boolean} isForward Set to true if delete was performed in forward direction.
     * @returns {Boolean|undefined} Returns `true` if keys were handled correctly.
     */
    _handleDelete(isForward) {
        // Do nothing when the read only mode is enabled.
        if (this.editor.isReadOnly) {
            return;
        }
        const modelDocument = this.editor.model.document;
        const modelSelection = modelDocument.selection;
        // Do nothing on non-collapsed selection.
        if (!modelSelection.isCollapsed) {
            return;
        }
        const objectElement = this._getObjectElementNextToSelection(isForward);
        if (objectElement) {
            this.editor.model.change(writer => {
                let previousNode = modelSelection.anchor.parent;
                // Remove previous element if empty.
                while (previousNode.isEmpty) {
                    const nodeToRemove = previousNode;
                    previousNode = nodeToRemove.parent;
                    writer.remove(nodeToRemove);
                }
                this._setSelectionOverElement(objectElement);
            });
            return true;
        }
    }
    /**
     * Sets {@link module:engine/model/selection~Selection document's selection} over given element.
     *
     * @internal
     * @protected
     * @param {module:engine/model/element~Element} element
     */
    _setSelectionOverElement(element) {
        this.editor.model.change(writer => {
            writer.setSelection(writer.createRangeOn(element));
        });
    }
    /**
     * Checks if {@link module:engine/model/element~Element element} placed next to the current
     * {@link module:engine/model/selection~Selection model selection} exists and is marked in
     * {@link module:engine/model/schema~Schema schema} as `object`.
     *
     * @internal
     * @protected
     * @param {Boolean} forward Direction of checking.
     * @returns {module:engine/model/element~Element|null}
     */
    _getObjectElementNextToSelection(forward) {
        const model = this.editor.model;
        const schema = model.schema;
        const modelSelection = model.document.selection;
        // Clone current selection to use it as a probe. We must leave default selection as it is so it can return
        // to its current state after undo.
        const probe = model.createSelection(modelSelection);
        model.modifySelection(probe, { direction: forward ? 'forward' : 'backward' });
        // The selection didn't change so there is nothing there.
        if (probe.isEqual(modelSelection)) {
            return null;
        }
        const objectElement = forward ? probe.focus.nodeBefore : probe.focus.nodeAfter;
        if (!!objectElement && schema.isObject(objectElement)) {
            return objectElement;
        }
        return null;
    }
    /**
     * Removes CSS class from previously selected widgets.
     *
     * @private
     * @param {module:engine/view/downcastwriter~DowncastWriter} writer
     */
    _clearPreviouslySelectedWidgets(writer) {
        for (const widget of this._previouslySelected) {
            writer.removeClass(WIDGET_SELECTED_CLASS_NAME, widget);
        }
        this._previouslySelected.clear();
    }
}
// Returns `true` when element is a nested editable or is placed inside one.
//
// @param {module:engine/view/element~Element}
// @returns {Boolean}
function isInsideNestedEditable(element) {
    let currentElement = element;
    while (currentElement) {
        if (currentElement.is('editableElement') && !currentElement.is('rootElement')) {
            return true;
        }
        // Click on nested widget should select it.
        if (isWidget(currentElement)) {
            return false;
        }
        currentElement = currentElement.parent;
    }
    return false;
}
// Checks whether the specified `element` is a child of the `parent` element.
//
// @param {module:engine/view/element~Element} element An element to check.
// @param {module:engine/view/element~Element|null} parent A parent for the element.
// @returns {Boolean}
function isChild(element, parent) {
    if (!parent) {
        return false;
    }
    return Array.from(element.getAncestors()).includes(parent);
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/throttle.js



/** Error message constants. */
var throttle_FUNC_ERROR_TEXT = 'Expected a function';

/**
 * Creates a throttled function that only invokes `func` at most once per
 * every `wait` milliseconds. The throttled function comes with a `cancel`
 * method to cancel delayed `func` invocations and a `flush` method to
 * immediately invoke them. Provide `options` to indicate whether `func`
 * should be invoked on the leading and/or trailing edge of the `wait`
 * timeout. The `func` is invoked with the last arguments provided to the
 * throttled function. Subsequent calls to the throttled function return the
 * result of the last `func` invocation.
 *
 * **Note:** If `leading` and `trailing` options are `true`, `func` is
 * invoked on the trailing edge of the timeout only if the throttled function
 * is invoked more than once during the `wait` timeout.
 *
 * If `wait` is `0` and `leading` is `false`, `func` invocation is deferred
 * until to the next tick, similar to `setTimeout` with a timeout of `0`.
 *
 * See [David Corbacho's article](https://css-tricks.com/debouncing-throttling-explained-examples/)
 * for details over the differences between `_.throttle` and `_.debounce`.
 *
 * @static
 * @memberOf _
 * @since 0.1.0
 * @category Function
 * @param {Function} func The function to throttle.
 * @param {number} [wait=0] The number of milliseconds to throttle invocations to.
 * @param {Object} [options={}] The options object.
 * @param {boolean} [options.leading=true]
 *  Specify invoking on the leading edge of the timeout.
 * @param {boolean} [options.trailing=true]
 *  Specify invoking on the trailing edge of the timeout.
 * @returns {Function} Returns the new throttled function.
 * @example
 *
 * // Avoid excessively updating the position while scrolling.
 * jQuery(window).on('scroll', _.throttle(updatePosition, 100));
 *
 * // Invoke `renewToken` when the click event is fired, but not more than once every 5 minutes.
 * var throttled = _.throttle(renewToken, 300000, { 'trailing': false });
 * jQuery(element).on('click', throttled);
 *
 * // Cancel the trailing throttled invocation.
 * jQuery(window).on('popstate', throttled.cancel);
 */
function throttle(func, wait, options) {
  var leading = true,
      trailing = true;

  if (typeof func != 'function') {
    throw new TypeError(throttle_FUNC_ERROR_TEXT);
  }
  if (lodash_es_isObject(options)) {
    leading = 'leading' in options ? !!options.leading : leading;
    trailing = 'trailing' in options ? !!options.trailing : trailing;
  }
  return lodash_es_debounce(func, wait, {
    'leading': leading,
    'maxWait': wait,
    'trailing': trailing
  });
}

/* harmony default export */ const lodash_es_throttle = (throttle);

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-clipboard/theme/clipboard.css
var clipboard = __webpack_require__(390);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/theme/clipboard.css

            

var clipboard_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

clipboard_options.insert = "head";
clipboard_options.singleton = true;

var clipboard_update = injectStylesIntoStyleTag_default()(clipboard/* default */.Z, clipboard_options);



/* harmony default export */ const theme_clipboard = (clipboard/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/dragdrop.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard/dragdrop
 */
/* globals setTimeout, clearTimeout */











// Drag and drop events overview:
//
//                ┌──────────────────┐
//                │     mousedown    │   Sets the draggable attribute.
//                └─────────┬────────┘
//                          │
//                          └─────────────────────┐
//                          │                     │
//                          │           ┌─────────V────────┐
//                          │           │      mouseup     │   Dragging did not start, removes the draggable attribute.
//                          │           └──────────────────┘
//                          │
//                ┌─────────V────────┐   Retrieves the selected model.DocumentFragment
//                │     dragstart    │   and converts it to view.DocumentFragment.
//                └─────────┬────────┘
//                          │
//                ┌─────────V────────┐   Processes view.DocumentFragment to text/html and text/plain
//                │  clipboardOutput │   and stores the results in data.dataTransfer.
//                └─────────┬────────┘
//                          │
//                          │   DOM dragover
//                          ┌────────────┐
//                          │            │
//                ┌─────────V────────┐   │
//                │     dragging     │   │   Updates the drop target marker.
//                └─────────┬────────┘   │
//                          │            │
//            ┌─────────────└────────────┘
//            │             │            │
//            │   ┌─────────V────────┐   │
//            │   │     dragleave    │   │   Removes the drop target marker.
//            │   └─────────┬────────┘   │
//            │             │            │
//        ┌───│─────────────┘            │
//        │   │             │            │
//        │   │   ┌─────────V────────┐   │
//        │   │   │     dragenter    │   │   Focuses the editor view.
//        │   │   └─────────┬────────┘   │
//        │   │             │            │
//        │   │             └────────────┘
//        │   │
//        │   └─────────────┐
//        │   │             │
//        │   │   ┌─────────V────────┐
//        └───┐   │       drop       │   (The default handler of the clipboard pipeline).
//            │   └─────────┬────────┘
//            │             │
//            │   ┌─────────V────────┐   Resolves the final data.targetRanges.
//            │   │  clipboardInput  │   Aborts if dropping on dragged content.
//            │   └─────────┬────────┘
//            │             │
//            │   ┌─────────V────────┐
//            │   │  clipboardInput  │   (The default handler of the clipboard pipeline).
//            │   └─────────┬────────┘
//            │             │
//            │ ┌───────────V───────────┐
//            │ │  inputTransformation  │   (The default handler of the clipboard pipeline).
//            │ └───────────┬───────────┘
//            │             │
//            │  ┌──────────V──────────┐
//            │  │   contentInsertion  │   Updates the document selection to drop range.
//            │  └──────────┬──────────┘
//            │             │
//            │  ┌──────────V──────────┐
//            │  │   contentInsertion  │   (The default handler of the clipboard pipeline).
//            │  └──────────┬──────────┘
//            │             │
//            │  ┌──────────V──────────┐
//            │  │   contentInsertion  │   Removes the content from the original range if the insertion was successful.
//            │  └──────────┬──────────┘
//            │             │
//            └─────────────┐
//                          │
//                ┌─────────V────────┐
//                │      dragend     │   Removes the drop marker and cleans the state.
//                └──────────────────┘
//
/**
 * The drag and drop feature. It works on top of the {@link module:clipboard/clipboardpipeline~ClipboardPipeline}.
 *
 * Read more about the clipboard integration in the {@glink framework/guides/deep-dive/clipboard clipboard deep dive guide}.
 *
 * @extends module:core/plugin~Plugin
 */
class DragDrop extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'DragDrop';
    }
    /**
     * @inheritDoc
     */
    static get requires() {
        return [ClipboardPipeline, Widget];
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const view = editor.editing.view;
        /**
         * The live range over the original content that is being dragged.
         *
         * @private
         * @type {module:engine/model/liverange~LiveRange}
         */
        this._draggedRange = null;
        /**
         * The UID of current dragging that is used to verify if the drop started in the same editor as the drag start.
         *
         * **Note**: This is a workaround for broken 'dragend' events (they are not fired if the source text node got removed).
         *
         * @private
         * @type {String}
         */
        this._draggingUid = '';
        /**
         * The reference to the model element that currently has a `draggable` attribute set (it is set while dragging).
         *
         * @private
         * @type {module:engine/model/element~Element}
         */
        this._draggableElement = null;
        /**
         * A throttled callback updating the drop marker.
         *
         * @private
         * @type {Function}
         */
        this._updateDropMarkerThrottled = lodash_es_throttle(targetRange => this._updateDropMarker(targetRange), 40);
        /**
         * A delayed callback removing the drop marker.
         *
         * @private
         * @type {Function}
         */
        this._removeDropMarkerDelayed = delay(() => this._removeDropMarker(), 40);
        /**
         * A delayed callback removing draggable attributes.
         *
         * @private
         * @type {Function}
         */
        this._clearDraggableAttributesDelayed = delay(() => this._clearDraggableAttributes(), 40);
        view.addObserver(ClipboardObserver);
        view.addObserver(MouseObserver);
        this._setupDragging();
        this._setupContentInsertionIntegration();
        this._setupClipboardInputIntegration();
        this._setupDropMarker();
        this._setupDraggableAttributeHandling();
        this.listenTo(editor, 'change:isReadOnly', (evt, name, isReadOnly) => {
            if (isReadOnly) {
                this.forceDisabled('readOnlyMode');
            }
            else {
                this.clearForceDisabled('readOnlyMode');
            }
        });
        this.on('change:isEnabled', (evt, name, isEnabled) => {
            if (!isEnabled) {
                this._finalizeDragging(false);
            }
        });
        if (src_env.isAndroid) {
            this.forceDisabled('noAndroidSupport');
        }
    }
    /**
     * @inheritDoc
     */
    destroy() {
        if (this._draggedRange) {
            this._draggedRange.detach();
            this._draggedRange = null;
        }
        this._updateDropMarkerThrottled.cancel();
        this._removeDropMarkerDelayed.cancel();
        this._clearDraggableAttributesDelayed.cancel();
        return super.destroy();
    }
    /**
     * Drag and drop events handling.
     *
     * @private
     */
    _setupDragging() {
        const editor = this.editor;
        const model = editor.model;
        const modelDocument = model.document;
        const view = editor.editing.view;
        const viewDocument = view.document;
        // The handler for the drag start; it is responsible for setting data transfer object.
        this.listenTo(viewDocument, 'dragstart', (evt, data) => {
            const selection = modelDocument.selection;
            // Don't drag the editable element itself.
            if (data.target && data.target.is('editableElement')) {
                data.preventDefault();
                return;
            }
            // TODO we could clone this node somewhere and style it to match editing view but without handles,
            //  selection outline, WTA buttons, etc.
            // data.dataTransfer._native.setDragImage( data.domTarget, 0, 0 );
            // Check if this is dragstart over the widget (but not a nested editable).
            const draggableWidget = data.target ? findDraggableWidget(data.target) : null;
            if (draggableWidget) {
                const modelElement = editor.editing.mapper.toModelElement(draggableWidget);
                this._draggedRange = LiveRange.fromRange(model.createRangeOn(modelElement));
                // Disable toolbars so they won't obscure the drop area.
                if (editor.plugins.has('WidgetToolbarRepository')) {
                    editor.plugins.get('WidgetToolbarRepository').forceDisabled('dragDrop');
                }
            }
            // If this was not a widget we should check if we need to drag some text content.
            else if (!viewDocument.selection.isCollapsed) {
                const selectedElement = viewDocument.selection.getSelectedElement();
                if (!selectedElement || !isWidget(selectedElement)) {
                    this._draggedRange = LiveRange.fromRange(selection.getFirstRange());
                }
            }
            if (!this._draggedRange) {
                data.preventDefault();
                return;
            }
            this._draggingUid = uid();
            data.dataTransfer.effectAllowed = this.isEnabled ? 'copyMove' : 'copy';
            data.dataTransfer.setData('application/ckeditor5-dragging-uid', this._draggingUid);
            const draggedSelection = model.createSelection(this._draggedRange.toRange());
            const content = editor.data.toView(model.getSelectedContent(draggedSelection));
            viewDocument.fire('clipboardOutput', {
                dataTransfer: data.dataTransfer,
                content,
                method: 'dragstart'
            });
            if (!this.isEnabled) {
                this._draggedRange.detach();
                this._draggedRange = null;
                this._draggingUid = '';
            }
        }, { priority: 'low' });
        // The handler for finalizing drag and drop. It should always be triggered after dragging completes
        // even if it was completed in a different application.
        // Note: This is not fired if source text node got removed while downcasting a marker.
        this.listenTo(viewDocument, 'dragend', (evt, data) => {
            this._finalizeDragging(!data.dataTransfer.isCanceled && data.dataTransfer.dropEffect == 'move');
        }, { priority: 'low' });
        // Dragging over the editable.
        this.listenTo(viewDocument, 'dragenter', () => {
            if (!this.isEnabled) {
                return;
            }
            view.focus();
        });
        // Dragging out of the editable.
        this.listenTo(viewDocument, 'dragleave', () => {
            // We do not know if the mouse left the editor or just some element in it, so let us wait a few milliseconds
            // to check if 'dragover' is not fired.
            this._removeDropMarkerDelayed();
        });
        // Handler for moving dragged content over the target area.
        this.listenTo(viewDocument, 'dragging', (evt, data) => {
            if (!this.isEnabled) {
                data.dataTransfer.dropEffect = 'none';
                return;
            }
            this._removeDropMarkerDelayed.cancel();
            const targetRange = findDropTargetRange(editor, data.targetRanges, data.target);
            // If this is content being dragged from another editor, moving out of current editor instance
            // is not possible until 'dragend' event case will be fixed.
            if (!this._draggedRange) {
                data.dataTransfer.dropEffect = 'copy';
            }
            // In Firefox it is already set and effect allowed remains the same as originally set.
            if (!src_env.isGecko) {
                if (data.dataTransfer.effectAllowed == 'copy') {
                    data.dataTransfer.dropEffect = 'copy';
                }
                else if (['all', 'copyMove'].includes(data.dataTransfer.effectAllowed)) {
                    data.dataTransfer.dropEffect = 'move';
                }
            }
            /* istanbul ignore else */
            if (targetRange) {
                this._updateDropMarkerThrottled(targetRange);
            }
        }, { priority: 'low' });
    }
    /**
     * Integration with the `clipboardInput` event.
     *
     * @private
     */
    _setupClipboardInputIntegration() {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        // Update the event target ranges and abort dropping if dropping over itself.
        this.listenTo(viewDocument, 'clipboardInput', (evt, data) => {
            if (data.method != 'drop') {
                return;
            }
            const targetRange = findDropTargetRange(editor, data.targetRanges, data.target);
            // The dragging markers must be removed after searching for the target range because sometimes
            // the target lands on the marker itself.
            this._removeDropMarker();
            /* istanbul ignore if */
            if (!targetRange) {
                this._finalizeDragging(false);
                evt.stop();
                return;
            }
            // Since we cannot rely on the drag end event, we must check if the local drag range is from the current drag and drop
            // or it is from some previous not cleared one.
            if (this._draggedRange && this._draggingUid != data.dataTransfer.getData('application/ckeditor5-dragging-uid')) {
                this._draggedRange.detach();
                this._draggedRange = null;
                this._draggingUid = '';
            }
            // Do not do anything if some content was dragged within the same document to the same position.
            const isMove = getFinalDropEffect(data.dataTransfer) == 'move';
            if (isMove && this._draggedRange && this._draggedRange.containsRange(targetRange, true)) {
                this._finalizeDragging(false);
                evt.stop();
                return;
            }
            // Override the target ranges with the one adjusted to the best one for a drop.
            data.targetRanges = [editor.editing.mapper.toViewRange(targetRange)];
        }, { priority: 'high' });
    }
    /**
     * Integration with the `contentInsertion` event of the clipboard pipeline.
     *
     * @private
     */
    _setupContentInsertionIntegration() {
        const clipboardPipeline = this.editor.plugins.get(ClipboardPipeline);
        clipboardPipeline.on('contentInsertion', (evt, data) => {
            if (!this.isEnabled || data.method !== 'drop') {
                return;
            }
            // Update the selection to the target range in the same change block to avoid selection post-fixing
            // and to be able to clone text attributes for plain text dropping.
            const ranges = data.targetRanges.map(viewRange => this.editor.editing.mapper.toModelRange(viewRange));
            this.editor.model.change(writer => writer.setSelection(ranges));
        }, { priority: 'high' });
        clipboardPipeline.on('contentInsertion', (evt, data) => {
            if (!this.isEnabled || data.method !== 'drop') {
                return;
            }
            // Remove dragged range content, remove markers, clean after dragging.
            const isMove = getFinalDropEffect(data.dataTransfer) == 'move';
            // Whether any content was inserted (insertion might fail if the schema is disallowing some elements
            // (for example an image caption allows only the content of a block but not blocks themselves.
            // Some integrations might not return valid range (i.e., table pasting).
            const isSuccess = !data.resultRange || !data.resultRange.isCollapsed;
            this._finalizeDragging(isSuccess && isMove);
        }, { priority: 'lowest' });
    }
    /**
     * Adds listeners that add the `draggable` attribute to the elements while the mouse button is down so the dragging could start.
     *
     * @private
     */
    _setupDraggableAttributeHandling() {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        // Add the 'draggable' attribute to the widget while pressing the selection handle.
        // This is required for widgets to be draggable. In Chrome it will enable dragging text nodes.
        this.listenTo(viewDocument, 'mousedown', (evt, data) => {
            // The lack of data can be caused by editor tests firing fake mouse events. This should not occur
            // in real-life scenarios but this greatly simplifies editor tests that would otherwise fail a lot.
            if (src_env.isAndroid || !data) {
                return;
            }
            this._clearDraggableAttributesDelayed.cancel();
            // Check if this is a mousedown over the widget (but not a nested editable).
            let draggableElement = findDraggableWidget(data.target);
            // Note: There is a limitation that if more than a widget is selected (a widget and some text)
            // and dragging starts on the widget, then only the widget is dragged.
            // If this was not a widget then we should check if we need to drag some text content.
            // In Chrome set a 'draggable' attribute on closest editable to allow immediate dragging of the selected text range.
            // In Firefox this is not needed. In Safari it makes the whole editable draggable (not just textual content).
            // Disabled in read-only mode because draggable="true" + contenteditable="false" results
            // in not firing selectionchange event ever, which makes the selection stuck in read-only mode.
            if (src_env.isBlink && !editor.isReadOnly && !draggableElement && !viewDocument.selection.isCollapsed) {
                const selectedElement = viewDocument.selection.getSelectedElement();
                if (!selectedElement || !isWidget(selectedElement)) {
                    draggableElement = viewDocument.selection.editableElement;
                }
            }
            if (draggableElement) {
                view.change(writer => {
                    writer.setAttribute('draggable', 'true', draggableElement);
                });
                // Keep the reference to the model element in case the view element gets removed while dragging.
                this._draggableElement = editor.editing.mapper.toModelElement(draggableElement);
            }
        });
        // Remove the draggable attribute in case no dragging started (only mousedown + mouseup).
        this.listenTo(viewDocument, 'mouseup', () => {
            if (!src_env.isAndroid) {
                this._clearDraggableAttributesDelayed();
            }
        });
    }
    /**
     * Removes the `draggable` attribute from the element that was used for dragging.
     *
     * @private
     */
    _clearDraggableAttributes() {
        const editing = this.editor.editing;
        editing.view.change(writer => {
            // Remove 'draggable' attribute.
            if (this._draggableElement && this._draggableElement.root.rootName != '$graveyard') {
                writer.removeAttribute('draggable', editing.mapper.toViewElement(this._draggableElement));
            }
            this._draggableElement = null;
        });
    }
    /**
     * Creates downcast conversion for the drop target marker.
     *
     * @private
     */
    _setupDropMarker() {
        const editor = this.editor;
        // Drop marker conversion for hovering over widgets.
        editor.conversion.for('editingDowncast').markerToHighlight({
            model: 'drop-target',
            view: {
                classes: ['ck-clipboard-drop-target-range']
            }
        });
        // Drop marker conversion for in text drop target.
        editor.conversion.for('editingDowncast').markerToElement({
            model: 'drop-target',
            view: (data, { writer }) => {
                const inText = editor.model.schema.checkChild(data.markerRange.start, '$text');
                if (!inText) {
                    return;
                }
                return writer.createUIElement('span', { class: 'ck ck-clipboard-drop-target-position' }, function (domDocument) {
                    const domElement = this.toDomElement(domDocument);
                    // Using word joiner to make this marker as high as text and also making text not break on marker.
                    domElement.append('\u2060', domDocument.createElement('span'), '\u2060');
                    return domElement;
                });
            }
        });
    }
    /**
     * Updates the drop target marker to the provided range.
     *
     * @private
     * @param {module:engine/model/range~Range} targetRange The range to set the marker to.
     */
    _updateDropMarker(targetRange) {
        const editor = this.editor;
        const markers = editor.model.markers;
        editor.model.change(writer => {
            if (markers.has('drop-target')) {
                if (!markers.get('drop-target').getRange().isEqual(targetRange)) {
                    writer.updateMarker('drop-target', { range: targetRange });
                }
            }
            else {
                writer.addMarker('drop-target', {
                    range: targetRange,
                    usingOperation: false,
                    affectsData: false
                });
            }
        });
    }
    /**
     * Removes the drop target marker.
     *
     * @private
     */
    _removeDropMarker() {
        const model = this.editor.model;
        this._removeDropMarkerDelayed.cancel();
        this._updateDropMarkerThrottled.cancel();
        if (model.markers.has('drop-target')) {
            model.change(writer => {
                writer.removeMarker('drop-target');
            });
        }
    }
    /**
     * Deletes the dragged content from its original range and clears the dragging state.
     *
     * @private
     * @param {Boolean} moved Whether the move succeeded.
     */
    _finalizeDragging(moved) {
        const editor = this.editor;
        const model = editor.model;
        this._removeDropMarker();
        this._clearDraggableAttributes();
        if (editor.plugins.has('WidgetToolbarRepository')) {
            editor.plugins.get('WidgetToolbarRepository').clearForceDisabled('dragDrop');
        }
        this._draggingUid = '';
        if (!this._draggedRange) {
            return;
        }
        // Delete moved content.
        if (moved && this.isEnabled) {
            model.deleteContent(model.createSelection(this._draggedRange), { doNotAutoparagraph: true });
        }
        this._draggedRange.detach();
        this._draggedRange = null;
    }
}
// Returns fixed selection range for given position and target element.
//
// @param {module:core/editor/editor~Editor} editor
// @param {Array.<module:engine/view/range~Range>} targetViewRanges
// @param {module:engine/view/element~Element} targetViewElement
// @returns {module:engine/model/range~Range|null}
function findDropTargetRange(editor, targetViewRanges, targetViewElement) {
    const model = editor.model;
    const mapper = editor.editing.mapper;
    let range = null;
    const targetViewPosition = targetViewRanges ? targetViewRanges[0].start : null;
    // A UIElement is not a valid drop element, use parent (this could be a drop marker or any other UIElement).
    if (targetViewElement.is('uiElement')) {
        targetViewElement = targetViewElement.parent;
    }
    // Quick win if the target is a widget (but not a nested editable).
    range = findDropTargetRangeOnWidget(editor, targetViewElement);
    if (range) {
        return range;
    }
    // The easiest part is over, now we need to move to the model space.
    // Find target model element and position.
    const targetModelElement = getClosestMappedModelElement(editor, targetViewElement);
    const targetModelPosition = targetViewPosition ? mapper.toModelPosition(targetViewPosition) : null;
    // There is no target position while hovering over an empty table cell.
    // In Safari, target position can be empty while hovering over a widget (e.g., a page-break).
    // Find the drop position inside the element.
    if (!targetModelPosition) {
        return findDropTargetRangeInElement(editor, targetModelElement);
    }
    // Check if target position is between blocks and adjust drop position to the next object.
    // This is because while hovering over a root element next to a widget the target position can jump in crazy places.
    range = findDropTargetRangeBetweenBlocks(editor, targetModelPosition, targetModelElement);
    if (range) {
        return range;
    }
    // Try fixing selection position.
    // In Firefox, the target position lands before widgets but in other browsers it tends to land after a widget.
    range = model.schema.getNearestSelectionRange(targetModelPosition, src_env.isGecko ? 'forward' : 'backward');
    if (range) {
        return range;
    }
    // There is no valid selection position inside the current limit element so find a closest object ancestor.
    // This happens if the model position lands directly in the <table> element itself (view target element was a `<td>`
    // so a nested editable, but view target position was directly in the `<figure>` element).
    return findDropTargetRangeOnAncestorObject(editor, targetModelPosition.parent);
}
// Returns fixed selection range for a given position and a target element if it is over the widget but not over its nested editable.
//
// @param {module:core/editor/editor~Editor} editor
// @param {module:engine/view/element~Element} targetViewElement
// @returns {module:engine/model/range~Range|null}
function findDropTargetRangeOnWidget(editor, targetViewElement) {
    const model = editor.model;
    const mapper = editor.editing.mapper;
    // Quick win if the target is a widget.
    if (isWidget(targetViewElement)) {
        return model.createRangeOn(mapper.toModelElement(targetViewElement));
    }
    // Check if we are deeper over a widget (but not over a nested editable).
    if (!targetViewElement.is('editableElement')) {
        // Find a closest ancestor that is either a widget or an editable element...
        const ancestor = targetViewElement.findAncestor(node => isWidget(node) || node.is('editableElement'));
        // ...and if the widget was closer then it is a drop target.
        if (isWidget(ancestor)) {
            return model.createRangeOn(mapper.toModelElement(ancestor));
        }
    }
    return null;
}
// Returns fixed selection range inside a model element.
//
// @param {module:core/editor/editor~Editor} editor
// @param {module:engine/model/element~Element} targetModelElement
// @returns {module:engine/model/range~Range}
function findDropTargetRangeInElement(editor, targetModelElement) {
    const model = editor.model;
    const schema = model.schema;
    const positionAtElementStart = model.createPositionAt(targetModelElement, 0);
    return schema.getNearestSelectionRange(positionAtElementStart, 'forward');
}
// Returns fixed selection range for a given position and a target element if the drop is between blocks.
//
// @param {module:core/editor/editor~Editor} editor
// @param {module:engine/model/position~Position} targetModelPosition
// @param {module:engine/model/element~Element} targetModelElement
// @returns {module:engine/model/range~Range|null}
function findDropTargetRangeBetweenBlocks(editor, targetModelPosition, targetModelElement) {
    const model = editor.model;
    // Check if target is between blocks.
    if (!model.schema.checkChild(targetModelElement, '$block')) {
        return null;
    }
    // Find position between blocks.
    const positionAtElementStart = model.createPositionAt(targetModelElement, 0);
    // Get the common part of the path (inside the target element and the target position).
    const commonPath = targetModelPosition.path.slice(0, positionAtElementStart.path.length);
    // Position between the blocks.
    const betweenBlocksPosition = model.createPositionFromPath(targetModelPosition.root, commonPath);
    const nodeAfter = betweenBlocksPosition.nodeAfter;
    // Adjust drop position to the next object.
    // This is because while hovering over a root element next to a widget the target position can jump in crazy places.
    if (nodeAfter && model.schema.isObject(nodeAfter)) {
        return model.createRangeOn(nodeAfter);
    }
    return null;
}
// Returns a selection range on the ancestor object.
//
// @param {module:core/editor/editor~Editor} editor
// @param {module:engine/model/element~Element} element
// @returns {module:engine/model/range~Range}
function findDropTargetRangeOnAncestorObject(editor, element) {
    const model = editor.model;
    let currentElement = element;
    while (currentElement) {
        if (model.schema.isObject(currentElement)) {
            return model.createRangeOn(currentElement);
        }
        currentElement = currentElement.parent;
    }
    /* istanbul ignore next */
    return null;
}
// Returns the closest model element for the specified view element.
//
// @param {module:core/editor/editor~Editor} editor
// @param {module:engine/view/element~Element} element
// @returns {module:engine/model/element~Element}
function getClosestMappedModelElement(editor, element) {
    const mapper = editor.editing.mapper;
    const view = editor.editing.view;
    const targetModelElement = mapper.toModelElement(element);
    if (targetModelElement) {
        return targetModelElement;
    }
    // Find mapped ancestor if the target is inside not mapped element (for example inline code element).
    const viewPosition = view.createPositionBefore(element);
    const viewElement = mapper.findMappedViewAncestor(viewPosition);
    return mapper.toModelElement(viewElement);
}
// Returns the drop effect that should be a result of dragging the content.
// This function is handling a quirk when checking the effect in the 'drop' DOM event.
function getFinalDropEffect(dataTransfer) {
    if (src_env.isGecko) {
        return dataTransfer.dropEffect;
    }
    return ['all', 'copyMove'].includes(dataTransfer.effectAllowed) ? 'move' : 'copy';
}
// Returns a function wrapper that will trigger a function after a specified wait time.
// The timeout can be canceled by calling the cancel function on the returned wrapped function.
//
// @param {Function} func The function to wrap.
// @param {Number} wait The timeout in ms.
// @returns {Function}
function delay(func, wait) {
    let timer;
    function delayed(...args) {
        delayed.cancel();
        timer = setTimeout(() => func(...args), wait);
    }
    delayed.cancel = () => {
        clearTimeout(timer);
    };
    return delayed;
}
// Returns a widget element that should be dragged.
//
// @param {module:engine/view/element~Element} target
// @returns {module:engine/view/element~Element}
function findDraggableWidget(target) {
    // This is directly an editable so not a widget for sure.
    if (target.is('editableElement')) {
        return null;
    }
    // TODO: Let's have a isWidgetSelectionHandleDomElement() helper in ckeditor5-widget utils.
    if (target.hasClass('ck-widget__selection-handle')) {
        return target.findAncestor(isWidget);
    }
    // Direct hit on a widget.
    if (isWidget(target)) {
        return target;
    }
    // Find closest ancestor that is either a widget or an editable element...
    const ancestor = target.findAncestor(node => isWidget(node) || node.is('editableElement'));
    // ...and if closer was the widget then enable dragging it.
    if (isWidget(ancestor)) {
        return ancestor;
    }
    return null;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/pasteplaintext.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard/pasteplaintext
 */



/**
 * The plugin detects the user's intention to paste plain text.
 *
 * For example, it detects the <kbd>Ctrl/Cmd</kbd> + <kbd>Shift</kbd> + <kbd>V</kbd> keystroke.
 *
 * @extends module:core/plugin~Plugin
 */
class PastePlainText extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'PastePlainText';
    }
    /**
     * @inheritDoc
     */
    static get requires() {
        return [ClipboardPipeline];
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const model = editor.model;
        const view = editor.editing.view;
        const viewDocument = view.document;
        const selection = model.document.selection;
        let shiftPressed = false;
        view.addObserver(ClipboardObserver);
        this.listenTo(viewDocument, 'keydown', (evt, data) => {
            shiftPressed = data.shiftKey;
        });
        editor.plugins.get(ClipboardPipeline).on('contentInsertion', (evt, data) => {
            // Plain text can be determined based on the event flag (#7799) or auto-detection (#1006). If detected,
            // preserve selection attributes on pasted items.
            if (!shiftPressed && !isPlainTextFragment(data.content, model.schema)) {
                return;
            }
            model.change(writer => {
                // Formatting attributes should be preserved.
                const textAttributes = Array.from(selection.getAttributes())
                    .filter(([key]) => model.schema.getAttributeProperties(key).isFormatting);
                if (!selection.isCollapsed) {
                    model.deleteContent(selection, { doNotAutoparagraph: true });
                }
                // Also preserve other attributes if they survived the content deletion (because they were not fully selected).
                // For example linkHref is not a formatting attribute but it should be preserved if pasted text was in the middle
                // of a link.
                textAttributes.push(...selection.getAttributes());
                const range = writer.createRangeIn(data.content);
                for (const item of range.getItems()) {
                    if (item.is('$textProxy')) {
                        writer.setAttributes(textAttributes, item);
                    }
                }
            });
        });
    }
}
// Returns true if specified `documentFragment` represents a plain text.
//
// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment
// @param {module:engine/model/schema~Schema} schema
// @returns {Boolean}
function isPlainTextFragment(documentFragment, schema) {
    if (documentFragment.childCount > 1) {
        return false;
    }
    const child = documentFragment.getChild(0);
    if (schema.isObject(child)) {
        return false;
    }
    return Array.from(child.getAttributeKeys()).length == 0;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/clipboard.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard/clipboard
 */




/**
 * The clipboard feature.
 *
 * Read more about the clipboard integration in the {@glink framework/guides/deep-dive/clipboard clipboard deep dive guide}.
 *
 * This is a "glue" plugin which loads the following plugins:
 * * {@link module:clipboard/clipboardpipeline~ClipboardPipeline}
 * * {@link module:clipboard/dragdrop~DragDrop}
 * * {@link module:clipboard/pasteplaintext~PastePlainText}
 *
 * @extends module:core/plugin~Plugin
 */
class Clipboard extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Clipboard';
    }
    /**
     * @inheritDoc
     */
    static get requires() {
        return [ClipboardPipeline, DragDrop, PastePlainText];
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-clipboard/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module clipboard
 */





;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/clipboard.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/clipboard
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/src/basecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module undo/basecommand
 */


/**
 * Base class for undo feature commands: {@link module:undo/undocommand~UndoCommand} and {@link module:undo/redocommand~RedoCommand}.
 *
 * @protected
 * @extends module:core/command~Command
 */
class BaseCommand extends command_Command {
    constructor(editor) {
        super(editor);
        /**
         * Stack of items stored by the command. These are pairs of:
         *
         * * {@link module:engine/model/batch~Batch batch} saved by the command,
         * * {@link module:engine/model/selection~Selection selection} state at the moment of saving the batch.
         *
         * @protected
         * @member {Array} #_stack
         */
        this._stack = [];
        /**
         * Stores all batches that were created by this command.
         *
         * @protected
         * @member {WeakSet.<module:engine/model/batch~Batch>} #_createdBatches
         */
        this._createdBatches = new WeakSet();
        // Refresh state, so the command is inactive right after initialization.
        this.refresh();
        // Set the transparent batch for the `editor.data.set()` call if the
        // batch type is not set already.
        this.listenTo(editor.data, 'set', (evt, data) => {
            // Create a shallow copy of the options to not change the original args.
            // And make sure that an object is assigned to data[ 1 ].
            data[1] = { ...data[1] };
            const options = data[1];
            // If batch type is not set, default to non-undoable batch.
            if (!options.batchType) {
                options.batchType = { isUndoable: false };
            }
        }, { priority: 'high' });
        // Clear the stack for the `transparent` batches.
        this.listenTo(editor.data, 'set', (evt, data) => {
            // We can assume that the object exists and it has a `batchType` property.
            // It was ensured with a higher priority listener before.
            const options = data[1];
            if (!options.batchType.isUndoable) {
                this.clearStack();
            }
        });
    }
    /**
     * @inheritDoc
     */
    refresh() {
        this.isEnabled = this._stack.length > 0;
    }
    /**
     * Stores a batch in the command, together with the selection state of the {@link module:engine/model/document~Document document}
     * created by the editor which this command is registered to.
     *
     * @param {module:engine/model/batch~Batch} batch The batch to add.
     */
    addBatch(batch) {
        const docSelection = this.editor.model.document.selection;
        const selection = {
            ranges: docSelection.hasOwnRange ? Array.from(docSelection.getRanges()) : [],
            isBackward: docSelection.isBackward
        };
        this._stack.push({ batch, selection });
        this.refresh();
    }
    /**
     * Removes all items from the stack.
     */
    clearStack() {
        this._stack = [];
        this.refresh();
    }
    /**
     * Restores the {@link module:engine/model/document~Document#selection document selection} state after a batch was undone.
     *
     * @protected
     * @param {Array.<module:engine/model/range~Range>} ranges Ranges to be restored.
     * @param {Boolean} isBackward A flag describing whether the restored range was selected forward or backward.
     * @param {Array.<module:engine/model/operation/operation~Operation>} operations Operations which has been applied
     * since selection has been stored.
     */
    _restoreSelection(ranges, isBackward, operations) {
        const model = this.editor.model;
        const document = model.document;
        // This will keep the transformed selection ranges.
        const selectionRanges = [];
        // Transform all ranges from the restored selection.
        const transformedRangeGroups = ranges.map(range => range.getTransformedByOperations(operations));
        const allRanges = transformedRangeGroups.flat();
        for (const rangeGroup of transformedRangeGroups) {
            // While transforming there could appear ranges that are contained by other ranges, we shall ignore them.
            const transformed = rangeGroup
                .filter(range => range.root != document.graveyard)
                .filter(range => !isRangeContainedByAnyOtherRange(range, allRanges));
            // All the transformed ranges ended up in graveyard.
            if (!transformed.length) {
                continue;
            }
            // After the range got transformed, we have an array of ranges. Some of those
            // ranges may be "touching" -- they can be next to each other and could be merged.
            normalizeRanges(transformed);
            // For each `range` from `ranges`, we take only one transformed range.
            // This is because we want to prevent situation where single-range selection
            // got transformed to multi-range selection.
            selectionRanges.push(transformed[0]);
        }
        // @if CK_DEBUG_ENGINE // console.log( `Restored selection by undo: ${ selectionRanges.join( ', ' ) }` );
        // `selectionRanges` may be empty if all ranges ended up in graveyard. If that is the case, do not restore selection.
        if (selectionRanges.length) {
            model.change(writer => {
                writer.setSelection(selectionRanges, { backward: isBackward });
            });
        }
    }
    /**
     * Undoes a batch by reversing that batch, transforming reversed batch and finally applying it.
     * This is a helper method for {@link #execute}.
     *
     * @protected
     * @param {module:engine/model/batch~Batch} batchToUndo The batch to be undone.
     * @param {module:engine/model/batch~Batch} undoingBatch The batch that will contain undoing changes.
     */
    _undo(batchToUndo, undoingBatch) {
        const model = this.editor.model;
        const document = model.document;
        // All changes done by the command execution will be saved as one batch.
        this._createdBatches.add(undoingBatch);
        const operationsToUndo = batchToUndo.operations.slice().filter(operation => operation.isDocumentOperation);
        operationsToUndo.reverse();
        // We will process each operation from `batchToUndo`, in reverse order. If there were operations A, B and C in undone batch,
        // we need to revert them in reverse order, so first C' (reversed C), then B', then A'.
        for (const operationToUndo of operationsToUndo) {
            const nextBaseVersion = operationToUndo.baseVersion + 1;
            const historyOperations = Array.from(document.history.getOperations(nextBaseVersion));
            const transformedSets = transformSets([operationToUndo.getReversed()], historyOperations, {
                useRelations: true,
                document: this.editor.model.document,
                padWithNoOps: false,
                forceWeakRemove: true
            });
            const reversedOperations = transformedSets.operationsA;
            // After reversed operation has been transformed by all history operations, apply it.
            for (const operation of reversedOperations) {
                // Before applying, add the operation to the `undoingBatch`.
                undoingBatch.addOperation(operation);
                model.applyOperation(operation);
                document.history.setOperationAsUndone(operationToUndo, operation);
            }
        }
    }
}
// Normalizes list of ranges by joining intersecting or "touching" ranges.
//
// @param {Array.<module:engine/model/range~Range>} ranges
//
function normalizeRanges(ranges) {
    ranges.sort((a, b) => a.start.isBefore(b.start) ? -1 : 1);
    for (let i = 1; i < ranges.length; i++) {
        const previousRange = ranges[i - 1];
        const joinedRange = previousRange.getJoined(ranges[i], true);
        if (joinedRange) {
            // Replace the ranges on the list with the new joined range.
            i--;
            ranges.splice(i, 2, joinedRange);
        }
    }
}
function isRangeContainedByAnyOtherRange(range, ranges) {
    return ranges.some(otherRange => otherRange !== range && otherRange.containsRange(range, true));
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/src/undocommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module undo/undocommand
 */

/**
 * The undo command stores {@link module:engine/model/batch~Batch batches} applied to the
 * {@link module:engine/model/document~Document document} and is able to undo a batch by reversing it and transforming by
 * batches from {@link module:engine/model/document~Document#history history} that happened after the reversed batch.
 *
 * The undo command also takes care of restoring the {@link module:engine/model/document~Document#selection document selection}.
 *
 * @extends module:undo/basecommand~BaseCommand
 */
class UndoCommand extends BaseCommand {
    /**
     * Executes the command. This method reverts a {@link module:engine/model/batch~Batch batch} added to the command's stack, transforms
     * and applies the reverted version on the {@link module:engine/model/document~Document document} and removes the batch from the stack.
     * Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
     *
     * @fires execute
     * @fires revert
     * @param {module:engine/model/batch~Batch} [batch] A batch that should be undone. If not set, the last added batch will be undone.
     */
    execute(batch = null) {
        // If batch is not given, set `batchIndex` to the last index in command stack.
        const batchIndex = batch ? this._stack.findIndex(a => a.batch == batch) : this._stack.length - 1;
        const item = this._stack.splice(batchIndex, 1)[0];
        const undoingBatch = this.editor.model.createBatch({ isUndo: true });
        // All changes has to be done in one `enqueueChange` callback so other listeners will not
        // step between consecutive operations, or won't do changes to the document before selection is properly restored.
        this.editor.model.enqueueChange(undoingBatch, () => {
            this._undo(item.batch, undoingBatch);
            const operations = this.editor.model.document.history.getOperations(item.batch.baseVersion);
            this._restoreSelection(item.selection.ranges, item.selection.isBackward, operations);
            this.fire('revert', item.batch, undoingBatch);
        });
        this.refresh();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/src/redocommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module undo/redocommand
 */

/**
 * The redo command stores {@link module:engine/model/batch~Batch batches} that were used to undo a batch by
 * {@link module:undo/undocommand~UndoCommand}. It is able to redo a previously undone batch by reversing the undoing
 * batches created by `UndoCommand`. The reversed batch is transformed by all the batches from
 * {@link module:engine/model/document~Document#history history} that happened after the reversed undo batch.
 *
 * The redo command also takes care of restoring the {@link module:engine/model/document~Document#selection document selection}.
 *
 * @extends module:undo/basecommand~BaseCommand
 */
class RedoCommand extends BaseCommand {
    /**
     * Executes the command. This method reverts the last {@link module:engine/model/batch~Batch batch} added to
     * the command's stack, applies the reverted and transformed version on the
     * {@link module:engine/model/document~Document document} and removes the batch from the stack.
     * Then, it restores the {@link module:engine/model/document~Document#selection document selection}.
     *
     * @fires execute
     */
    execute() {
        const item = this._stack.pop();
        const redoingBatch = this.editor.model.createBatch({ isUndo: true });
        // All changes have to be done in one `enqueueChange` callback so other listeners will not step between consecutive
        // operations, or won't do changes to the document before selection is properly restored.
        this.editor.model.enqueueChange(redoingBatch, () => {
            const lastOperation = item.batch.operations[item.batch.operations.length - 1];
            const nextBaseVersion = lastOperation.baseVersion + 1;
            const operations = this.editor.model.document.history.getOperations(nextBaseVersion);
            this._restoreSelection(item.selection.ranges, item.selection.isBackward, operations);
            this._undo(item.batch, redoingBatch);
        });
        this.refresh();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/src/undoediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module undo/undoediting
 */



/**
 * The undo engine feature.
 *
 * It introduces the `'undo'` and `'redo'` commands to the editor.
 *
 * @extends module:core/plugin~Plugin
 */
class UndoEditing extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'UndoEditing';
    }
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        /**
         * The command that manages undo {@link module:engine/model/batch~Batch batches} stack (history).
         * Created and registered during the {@link #init feature initialization}.
         *
         * @private
         * @member {module:undo/undocommand~UndoCommand} #_undoCommand
         */
        /**
         * The command that manages redo {@link module:engine/model/batch~Batch batches} stack (history).
         * Created and registered during the {@link #init feature initialization}.
         *
         * @private
         * @member {module:undo/undocommand~UndoCommand} #_redoCommand
         */
        /**
         * Keeps track of which batches were registered in undo.
         *
         * @private
         * @member {WeakSet.<module:engine/model/batch~Batch>}
         */
        this._batchRegistry = new WeakSet();
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        // Create commands.
        this._undoCommand = new UndoCommand(editor);
        this._redoCommand = new RedoCommand(editor);
        // Register command to the editor.
        editor.commands.add('undo', this._undoCommand);
        editor.commands.add('redo', this._redoCommand);
        this.listenTo(editor.model, 'applyOperation', (evt, args) => {
            const operation = args[0];
            // Do not register batch if the operation is not a document operation.
            // This prevents from creating empty undo steps, where all operations where non-document operations.
            // Non-document operations creates and alters content in detached tree fragments (for example, document fragments).
            // Most of time this is preparing data before it is inserted into actual tree (for example during copy & paste).
            // Such operations should not be reversed.
            if (!operation.isDocumentOperation) {
                return;
            }
            const batch = operation.batch;
            const isRedoBatch = this._redoCommand._createdBatches.has(batch);
            const isUndoBatch = this._undoCommand._createdBatches.has(batch);
            const wasProcessed = this._batchRegistry.has(batch);
            // Skip the batch if it was already processed.
            if (wasProcessed) {
                return;
            }
            // Add the batch to the registry so it will not be processed again.
            this._batchRegistry.add(batch);
            if (!batch.isUndoable) {
                return;
            }
            if (isRedoBatch) {
                // If this batch comes from `redoCommand`, add it to the `undoCommand` stack.
                this._undoCommand.addBatch(batch);
            }
            else if (!isUndoBatch) {
                // If the batch comes neither  from `redoCommand` nor from `undoCommand` then it is a new, regular batch.
                // Add the batch to the `undoCommand` stack and clear the `redoCommand` stack.
                this._undoCommand.addBatch(batch);
                this._redoCommand.clearStack();
            }
        }, { priority: 'highest' });
        this.listenTo(this._undoCommand, 'revert', (evt, undoneBatch, undoingBatch) => {
            this._redoCommand.addBatch(undoingBatch);
        });
        editor.keystrokes.set('CTRL+Z', 'undo');
        editor.keystrokes.set('CTRL+Y', 'redo');
        editor.keystrokes.set('CTRL+SHIFT+Z', 'redo');
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/theme/icons/undo.svg
/* harmony default export */ const undo = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m5.042 9.367 2.189 1.837a.75.75 0 0 1-.965 1.149l-3.788-3.18a.747.747 0 0 1-.21-.284.75.75 0 0 1 .17-.945L6.23 4.762a.75.75 0 1 1 .964 1.15L4.863 7.866h8.917A.75.75 0 0 1 14 7.9a4 4 0 1 1-1.477 7.718l.344-1.489a2.5 2.5 0 1 0 1.094-4.73l.008-.032H5.042z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/theme/icons/redo.svg
/* harmony default export */ const redo = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m14.958 9.367-2.189 1.837a.75.75 0 0 0 .965 1.149l3.788-3.18a.747.747 0 0 0 .21-.284.75.75 0 0 0-.17-.945L13.77 4.762a.75.75 0 1 0-.964 1.15l2.331 1.955H6.22A.75.75 0 0 0 6 7.9a4 4 0 1 0 1.477 7.718l-.344-1.489A2.5 2.5 0 1 1 6.039 9.4l-.008-.032h8.927z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/src/undoui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module undo/undoui
 */




/**
 * The undo UI feature. It introduces the `'undo'` and `'redo'` buttons to the editor.
 *
 * @extends module:core/plugin~Plugin
 */
class UndoUI extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'UndoUI';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const locale = editor.locale;
        const t = editor.t;
        const localizedUndoIcon = locale.uiLanguageDirection == 'ltr' ? undo : redo;
        const localizedRedoIcon = locale.uiLanguageDirection == 'ltr' ? redo : undo;
        this._addButton('undo', t('Undo'), 'CTRL+Z', localizedUndoIcon);
        this._addButton('redo', t('Redo'), 'CTRL+Y', localizedRedoIcon);
    }
    /**
     * Creates a button for the specified command.
     *
     * @private
     * @param {String} name Command name.
     * @param {String} label Button label.
     * @param {String} keystroke Command keystroke.
     * @param {String} Icon Source of the icon.
     */
    _addButton(name, label, keystroke, Icon) {
        const editor = this.editor;
        editor.ui.componentFactory.add(name, locale => {
            const command = editor.commands.get(name);
            const view = new buttonview_ButtonView(locale);
            view.set({
                label,
                icon: Icon,
                keystroke,
                tooltip: true
            });
            view.bind('isEnabled').to(command, 'isEnabled');
            this.listenTo(view, 'execute', () => {
                editor.execute(name);
                editor.editing.view.focus();
            });
            return view;
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/src/undo.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module undo/undo
 */



/**
 * The undo feature.
 *
 * This is a "glue" plugin which loads the {@link module:undo/undoediting~UndoEditing undo editing feature}
 * and {@link module:undo/undoui~UndoUI undo UI feature}.
 *
 * Below is the explanation of the undo mechanism working together with {@link module:engine/model/history~History History}:
 *
 * Whenever a {@link module:engine/model/operation/operation~Operation operation} is applied to the
 * {@link module:engine/model/document~Document document}, it is saved to `History` as is.
 * The {@link module:engine/model/batch~Batch batch} that owns that operation is also saved, in
 * {@link module:undo/undocommand~UndoCommand}, together with the selection that was present in the document before the
 * operation was applied. A batch is saved instead of the operation because changes are undone batch-by-batch, not operation-by-operation
 * and a batch is seen as one undo step.
 *
 * After some changes happen to the document, the `History` and `UndoCommand` stack can be represented as follows:
 *
 *		    History                            Undo stack
 *		==============             ==================================
 *		[operation A1]                         [batch A]
 *		[operation B1]                         [batch B]
 *		[operation B2]                         [batch C]
 *		[operation C1]
 *		[operation C2]
 *		[operation B3]
 *		[operation C3]
 *
 * Where operations starting with the same letter are from same batch.
 *
 * Undoing a batch means that a set of operations which will reverse the effects of that batch needs to be generated.
 * For example, if a batch added several letters, undoing the batch should remove them. It is important to apply undoing
 * operations in the reversed order, so if a batch has operation `X`, `Y`, `Z`, reversed operations `Zr`, `Yr` and `Xr`
 * need to be applied. Otherwise reversed operation `Xr` would operate on a wrong document state, because operation `X`
 * does not know that operations `Y` and `Z` happened.
 *
 * After operations from an undone batch got {@link module:engine/model/operation/operation~Operation#getReversed reversed},
 * one needs to make sure if they are ready to be applied. In the scenario above, operation `C3` is the last operation and `C3r`
 * bases on up-to-date document state, so it can be applied to the document.
 *
 *		     History                             Undo stack
 *		=================             ==================================
 *		[ operation A1  ]                      [  batch A  ]
 *		[ operation B1  ]                      [  batch B  ]
 *		[ operation B2  ]             [   processing undoing batch C   ]
 *		[ operation C1  ]
 *		[ operation C2  ]
 *		[ operation B3  ]
 *		[ operation C3  ]
 *		[ operation C3r ]
 *
 * Next is operation `C2`, reversed to `C2r`. `C2r` bases on `C2`, so it bases on the wrong document state. It needs to be
 * transformed by operations from history that happened after it, so it "knows" about them. Let us assume that `C2' = C2r * B3 * C3 * C3r`,
 * where `*` means "transformed by". Rest of operations from that batch are processed in the same fashion.
 *
 *		     History                             Undo stack                                      Redo stack
 *		=================             ==================================             ==================================
 *		[ operation A1  ]                      [  batch A  ]                                    [ batch Cr ]
 *		[ operation B1  ]                      [  batch B  ]
 *		[ operation B2  ]
 *		[ operation C1  ]
 *		[ operation C2  ]
 *		[ operation B3  ]
 *		[ operation C3  ]
 *		[ operation C3r ]
 *		[ operation C2' ]
 *		[ operation C1' ]
 *
 * Selective undo works on the same basis, however, instead of undoing the last batch in the undo stack, any batch can be undone.
 * The same algorithm applies: operations from a batch (i.e. `A1`) are reversed and then transformed by operations stored in history.
 *
 * Redo also is very similar to undo. It has its own stack that is filled with undoing (reversed batches). Operations from
 * batch that is re-done are reversed-back, transformed in proper order and applied to the document.
 *
 *		     History                             Undo stack                                      Redo stack
 *		=================             ==================================             ==================================
 *		[ operation A1  ]                      [  batch A  ]
 *		[ operation B1  ]                      [  batch B  ]
 *		[ operation B2  ]                      [ batch Crr ]
 *		[ operation C1  ]
 *		[ operation C2  ]
 *		[ operation B3  ]
 *		[ operation C3  ]
 *		[ operation C3r ]
 *		[ operation C2' ]
 *		[ operation C1' ]
 *		[ operation C1'r]
 *		[ operation C2'r]
 *		[ operation C3rr]
 *
 * @extends module:core/plugin~Plugin
 */
class Undo extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get requires() {
        return [UndoEditing, UndoUI];
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Undo';
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-undo/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module undo
 */




;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/undo.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/undo
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widgettoolbarrepository.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/widgettoolbarrepository
 */






/**
 * Widget toolbar repository plugin. A central point for registering widget toolbars. This plugin handles the whole
 * toolbar rendering process and exposes a concise API.
 *
 * To add a toolbar for your widget use the {@link ~WidgetToolbarRepository#register `WidgetToolbarRepository#register()`} method.
 *
 * The following example comes from the {@link module:image/imagetoolbar~ImageToolbar} plugin:
 *
 * 		class ImageToolbar extends Plugin {
 *			static get requires() {
 *				return [ WidgetToolbarRepository ];
 *			}
 *
 *			afterInit() {
 *				const editor = this.editor;
 *				const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
 *
 *				widgetToolbarRepository.register( 'image', {
 *					items: editor.config.get( 'image.toolbar' ),
 *					getRelatedElement: getClosestSelectedImageWidget
 *				} );
 *			}
 *		}
 */
class WidgetToolbarRepository extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get requires() {
        return [ContextualBalloon];
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'WidgetToolbarRepository';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        // Disables the default balloon toolbar for all widgets.
        if (editor.plugins.has('BalloonToolbar')) {
            const balloonToolbar = editor.plugins.get('BalloonToolbar');
            this.listenTo(balloonToolbar, 'show', evt => {
                if (isWidgetSelected(editor.editing.view.document.selection)) {
                    evt.stop();
                }
            }, { priority: 'high' });
        }
        /**
         * A map of toolbar definitions.
         *
         * @protected
         * @member {Map.<String,module:widget/widgettoolbarrepository~WidgetRepositoryToolbarDefinition>} #_toolbarDefinitions
         */
        this._toolbarDefinitions = new Map();
        /**
         * @private
         */
        this._balloon = this.editor.plugins.get('ContextualBalloon');
        this.on('change:isEnabled', () => {
            this._updateToolbarsVisibility();
        });
        this.listenTo(editor.ui, 'update', () => {
            this._updateToolbarsVisibility();
        });
        // UI#update is not fired after focus is back in editor, we need to check if balloon panel should be visible.
        this.listenTo(editor.ui.focusTracker, 'change:isFocused', () => {
            this._updateToolbarsVisibility();
        }, { priority: 'low' });
    }
    destroy() {
        super.destroy();
        for (const toolbarConfig of this._toolbarDefinitions.values()) {
            toolbarConfig.view.destroy();
        }
    }
    /**
     * Registers toolbar in the WidgetToolbarRepository. It renders it in the `ContextualBalloon` based on the value of the invoked
     * `getRelatedElement` function. Toolbar items are gathered from `items` array.
     * The balloon's CSS class is by default `ck-toolbar-container` and may be override with the `balloonClassName` option.
     *
     * Note: This method should be called in the {@link module:core/plugin~PluginInterface#afterInit `Plugin#afterInit()`}
     * callback (or later) to make sure that the given toolbar items were already registered by other plugins.
     *
     * @param {String} toolbarId An id for the toolbar. Used to
     * @param {Object} options
     * @param {String} [options.ariaLabel] Label used by assistive technologies to describe this toolbar element.
     * @param {Array.<String>} options.items Array of toolbar items.
     * @param {Function} options.getRelatedElement Callback which returns an element the toolbar should be attached to.
     * @param {String} [options.balloonClassName='ck-toolbar-container'] CSS class for the widget balloon.
     */
    register(toolbarId, { ariaLabel, items, getRelatedElement, balloonClassName = 'ck-toolbar-container' }) {
        // Trying to register a toolbar without any item.
        if (!items.length) {
            /**
             * When {@link #register registering} a new widget toolbar, you need to provide a non-empty array with
             * the items that will be inserted into the toolbar.
             *
             * If you see this error when integrating the editor, you likely forgot to configure one of the widget toolbars.
             *
             * See for instance:
             *
             * * {@link module:table/table~TableConfig#contentToolbar `config.table.contentToolbar`}
             * * {@link module:image/image~ImageConfig#toolbar `config.image.toolbar`}
             *
             * @error widget-toolbar-no-items
             * @param {String} toolbarId The id of the toolbar that has not been configured correctly.
             */
            logWarning('widget-toolbar-no-items', { toolbarId });
            return;
        }
        const editor = this.editor;
        const t = editor.t;
        const toolbarView = new toolbarview_ToolbarView(editor.locale);
        toolbarView.ariaLabel = ariaLabel || t('Widget toolbar');
        if (this._toolbarDefinitions.has(toolbarId)) {
            /**
             * Toolbar with the given id was already added.
             *
             * @error widget-toolbar-duplicated
             * @param toolbarId Toolbar id.
             */
            throw new CKEditorError('widget-toolbar-duplicated', this, { toolbarId });
        }
        toolbarView.fillFromConfig(items, editor.ui.componentFactory);
        const toolbarDefinition = {
            view: toolbarView,
            getRelatedElement,
            balloonClassName
        };
        // Register the toolbar so it becomes available for Alt+F10 and Esc navigation.
        editor.ui.addToolbar(toolbarView, {
            isContextual: true,
            beforeFocus: () => {
                const relatedElement = getRelatedElement(editor.editing.view.document.selection);
                if (relatedElement) {
                    this._showToolbar(toolbarDefinition, relatedElement);
                }
            },
            afterBlur: () => {
                this._hideToolbar(toolbarDefinition);
            }
        });
        this._toolbarDefinitions.set(toolbarId, toolbarDefinition);
    }
    /**
     * Iterates over stored toolbars and makes them visible or hidden.
     *
     * @private
     */
    _updateToolbarsVisibility() {
        let maxRelatedElementDepth = 0;
        let deepestRelatedElement = null;
        let deepestToolbarDefinition = null;
        for (const definition of this._toolbarDefinitions.values()) {
            const relatedElement = definition.getRelatedElement(this.editor.editing.view.document.selection);
            if (!this.isEnabled || !relatedElement) {
                if (this._isToolbarInBalloon(definition)) {
                    this._hideToolbar(definition);
                }
            }
            else if (!this.editor.ui.focusTracker.isFocused) {
                if (this._isToolbarVisible(definition)) {
                    this._hideToolbar(definition);
                }
            }
            else {
                const relatedElementDepth = relatedElement.getAncestors().length;
                // Many toolbars can express willingness to be displayed but they do not know about
                // each other. Figure out which toolbar is deepest in the view tree to decide which
                // should be displayed. For instance, if a selected image is inside a table cell, display
                // the ImageToolbar rather than the TableToolbar (#60).
                if (relatedElementDepth > maxRelatedElementDepth) {
                    maxRelatedElementDepth = relatedElementDepth;
                    deepestRelatedElement = relatedElement;
                    deepestToolbarDefinition = definition;
                }
            }
        }
        if (deepestToolbarDefinition) {
            this._showToolbar(deepestToolbarDefinition, deepestRelatedElement);
        }
    }
    /**
     * Hides the given toolbar.
     *
     * @private
     * @param {module:widget/widgettoolbarrepository~WidgetRepositoryToolbarDefinition} toolbarDefinition
     */
    _hideToolbar(toolbarDefinition) {
        this._balloon.remove(toolbarDefinition.view);
        this.stopListening(this._balloon, 'change:visibleView');
    }
    /**
     * Shows up the toolbar if the toolbar is not visible.
     * Otherwise, repositions the toolbar's balloon when toolbar's view is the most top view in balloon stack.
     *
     * It might happen here that the toolbar's view is under another view. Then do nothing as the other toolbar view
     * should be still visible after the {@link module:core/editor/editorui~EditorUI#event:update}.
     *
     * @private
     * @param {module:widget/widgettoolbarrepository~WidgetRepositoryToolbarDefinition} toolbarDefinition
     * @param {module:engine/view/element~Element} relatedElement
     */
    _showToolbar(toolbarDefinition, relatedElement) {
        if (this._isToolbarVisible(toolbarDefinition)) {
            repositionContextualBalloon(this.editor, relatedElement);
        }
        else if (!this._isToolbarInBalloon(toolbarDefinition)) {
            this._balloon.add({
                view: toolbarDefinition.view,
                position: getBalloonPositionData(this.editor, relatedElement),
                balloonClassName: toolbarDefinition.balloonClassName
            });
            // Update toolbar position each time stack with toolbar view is switched to visible.
            // This is in a case target element has changed when toolbar was in invisible stack
            // e.g. target image was wrapped by a block quote.
            // See https://github.com/ckeditor/ckeditor5-widget/issues/92.
            this.listenTo(this._balloon, 'change:visibleView', () => {
                for (const definition of this._toolbarDefinitions.values()) {
                    if (this._isToolbarVisible(definition)) {
                        const relatedElement = definition.getRelatedElement(this.editor.editing.view.document.selection);
                        repositionContextualBalloon(this.editor, relatedElement);
                    }
                }
            });
        }
    }
    /**
     * @private
     * @param {Object} toolbar
     * @returns {Boolean}
     */
    _isToolbarVisible(toolbar) {
        return this._balloon.visibleView === toolbar.view;
    }
    /**
     * @private
     * @param {Object} toolbar
     * @returns {Boolean}
     */
    _isToolbarInBalloon(toolbar) {
        return this._balloon.hasView(toolbar.view);
    }
}
function repositionContextualBalloon(editor, relatedElement) {
    const balloon = editor.plugins.get('ContextualBalloon');
    const position = getBalloonPositionData(editor, relatedElement);
    balloon.updatePosition(position);
}
function getBalloonPositionData(editor, relatedElement) {
    const editingView = editor.editing.view;
    const defaultPositions = balloonpanelview_BalloonPanelView.defaultPositions;
    return {
        target: editingView.domConverter.mapViewToDom(relatedElement),
        positions: [
            defaultPositions.northArrowSouth,
            defaultPositions.northArrowSouthWest,
            defaultPositions.northArrowSouthEast,
            defaultPositions.southArrowNorth,
            defaultPositions.southArrowNorthWest,
            defaultPositions.southArrowNorthEast,
            defaultPositions.viewportStickyNorth
        ]
    };
}
function isWidgetSelected(selection) {
    const viewElement = selection.getSelectedElement();
    return !!(viewElement && isWidget(viewElement));
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widgetresize/resizerstate.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/widgetresize/resizerstate
 */


/**
 * Stores the internal state of a single resizable object.
 *
 */
class ResizeState extends Observable {
    /**
     * @param {module:widget/widgetresize~ResizerOptions} options Resizer options.
     */
    constructor(options) {
        super();
        /**
         * The original width (pixels) of the resized object when the resize process was started.
         *
         * @readonly
         * @member {Number} #originalWidth
         */
        /**
         * The original height (pixels) of the resized object when the resize process was started.
         *
         * @readonly
         * @member {Number} #originalHeight
         */
        /**
         * The original width (percents) of the resized object when the resize process was started.
         *
         * @readonly
         * @member {Number} #originalWidthPercents
         */
        /**
         * The position of the handle that initiated the resizing. E.g. `"top-left"`, `"bottom-right"` etc. or `null`
         * if unknown.
         *
         * @readonly
         * @observable
         * @member {String|null} #activeHandlePosition
         */
        this.set('activeHandlePosition', null);
        /**
         * The width (percents) proposed, but not committed yet, in the current resize process.
         *
         * @readonly
         * @observable
         * @member {Number|null} #proposedWidthPercents
         */
        this.set('proposedWidthPercents', null);
        /**
         * The width (pixels) proposed, but not committed yet, in the current resize process.
         *
         * @readonly
         * @observable
         * @member {Number|null} #proposedWidthPixels
         */
        this.set('proposedWidth', null);
        /**
         * The height (pixels) proposed, but not committed yet, in the current resize process.
         *
         * @readonly
         * @observable
         * @member {Number|null} #proposedHeightPixels
         */
        this.set('proposedHeight', null);
        this.set('proposedHandleHostWidth', null);
        this.set('proposedHandleHostHeight', null);
        /**
         * A width to height ratio of the resized image.
         *
         * @readonly
         * @member {Number} #aspectRatio
         */
        /**
         * @private
         * @type {module:widget/widgetresize~ResizerOptions}
         */
        this._options = options;
        /**
         * The reference point of the resizer where the dragging started. It is used to measure the distance the user cursor
         * traveled, so how much the image should be enlarged.
         * This information is only known after the DOM was rendered, so it will be updated later.
         *
         * @private
         * @type {Object}
         */
        this._referenceCoordinates = null;
    }
    get originalWidth() {
        return this._originalWidth;
    }
    get originalHeight() {
        return this._originalHeight;
    }
    get originalWidthPercents() {
        return this._originalWidthPercents;
    }
    get aspectRatio() {
        return this._aspectRatio;
    }
    /**
     *
     * @param {HTMLElement} domResizeHandle The handle used to calculate the reference point.
     * @param {HTMLElement} domHandleHost
     * @param {HTMLElement} domResizeHost
     */
    begin(domResizeHandle, domHandleHost, domResizeHost) {
        const clientRect = new rect_Rect(domHandleHost);
        this.activeHandlePosition = getHandlePosition(domResizeHandle);
        this._referenceCoordinates = getAbsoluteBoundaryPoint(domHandleHost, getOppositePosition(this.activeHandlePosition));
        this._originalWidth = clientRect.width;
        this._originalHeight = clientRect.height;
        this._aspectRatio = clientRect.width / clientRect.height;
        const widthStyle = domResizeHost.style.width;
        if (widthStyle && widthStyle.match(/^\d+(\.\d*)?%$/)) {
            this._originalWidthPercents = parseFloat(widthStyle);
        }
        else {
            this._originalWidthPercents = calculateHostPercentageWidth(domResizeHost, clientRect);
        }
    }
    update(newSize) {
        this.proposedWidth = newSize.width;
        this.proposedHeight = newSize.height;
        this.proposedWidthPercents = newSize.widthPercents;
        this.proposedHandleHostWidth = newSize.handleHostWidth;
        this.proposedHandleHostHeight = newSize.handleHostHeight;
    }
}
// Calculates a relative width of a `domResizeHost` compared to it's parent in percents.
//
// @private
// @param {HTMLElement} domResizeHost
// @param {module:utils/dom/rect~Rect} resizeHostRect
// @returns {Number}
function calculateHostPercentageWidth(domResizeHost, resizeHostRect) {
    const domResizeHostParent = domResizeHost.parentElement;
    // Need to use computed style as it properly excludes parent's paddings from the returned value.
    const parentWidth = parseFloat(domResizeHostParent.ownerDocument.defaultView.getComputedStyle(domResizeHostParent).width);
    return resizeHostRect.width / parentWidth * 100;
}
// Returns coordinates of the top-left corner of an element, relative to the document's top-left corner.
//
// @private
// @param {HTMLElement} element
// @param {String} resizerPosition The position of the resize handle, e.g. `"top-left"`, `"bottom-right"`.
// @returns {Object} return
// @returns {Number} return.x
// @returns {Number} return.y
function getAbsoluteBoundaryPoint(element, resizerPosition) {
    const elementRect = new rect_Rect(element);
    const positionParts = resizerPosition.split('-');
    const ret = {
        x: positionParts[1] == 'right' ? elementRect.right : elementRect.left,
        y: positionParts[0] == 'bottom' ? elementRect.bottom : elementRect.top
    };
    ret.x += element.ownerDocument.defaultView.scrollX;
    ret.y += element.ownerDocument.defaultView.scrollY;
    return ret;
}
// @private
// @param {String} resizerPosition The expected resizer position, like `"top-left"`, `"bottom-right"`.
// @returns {String} A prefixed HTML class name for the resizer element.
function getResizerHandleClass(resizerPosition) {
    return `ck-widget__resizer__handle-${resizerPosition}`;
}
// Determines the position of a given resize handle.
//
// @private
// @param {HTMLElement} domHandle Handle used to calculate the reference point.
// @returns {String|undefined} Returns a string like `"top-left"` or `undefined` if not matched.
function getHandlePosition(domHandle) {
    const resizerPositions = ['top-left', 'top-right', 'bottom-right', 'bottom-left'];
    for (const position of resizerPositions) {
        if (domHandle.classList.contains(getResizerHandleClass(position))) {
            return position;
        }
    }
}
// @private
// @param {String} position Like `"top-left"`.
// @returns {String} Inverted `position`, e.g. it returns `"bottom-right"` if `"top-left"` was given as `position`.
function getOppositePosition(position) {
    const parts = position.split('-');
    const replacements = {
        top: 'bottom',
        bottom: 'top',
        left: 'right',
        right: 'left'
    };
    return `${replacements[parts[0]]}-${replacements[parts[1]]}`;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widgetresize/sizeview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/widgetresize/sizeview
 */

/**
 * A view displaying the proposed new element size during the resizing.
 *
 * @protected
 * @extends {module:ui/view~View}
 */
class SizeView extends src_view_View {
    constructor() {
        super();
        /**
         * The visibility of the view defined based on the existence of the host proposed dimensions.
         *
         * @private
         * @observable
         * @readonly
         * @member {Boolean} #_isVisible
         */
        /**
         * The text that will be displayed in the `SizeView` child.
         * It can be formatted as the pixel values (e.g. 10x20) or the percentage value (e.g. 10%).
         *
         * @private
         * @observable
         * @readonly
         * @member {Boolean} #_label
         */
        /**
         * The position of the view defined based on the host size and active handle position.
         *
         * @private
         * @observable
         * @readonly
         * @member {String} #_viewPosition
         */
        const bind = this.bindTemplate;
        this.setTemplate({
            tag: 'div',
            attributes: {
                class: [
                    'ck',
                    'ck-size-view',
                    bind.to('_viewPosition', value => value ? `ck-orientation-${value}` : '')
                ],
                style: {
                    display: bind.if('_isVisible', 'none', visible => !visible)
                }
            },
            children: [{
                    text: bind.to('_label')
                }]
        });
    }
    /**
     * A method used for binding the `SizeView` instance properties to the `ResizeState` instance observable properties.
     *
     * @protected
     * @internal
     * @param {module:widget/widgetresize~ResizerOptions} options
     * An object defining the resizer options, used for setting the proper size label.
     * @param {module:widget/widgetresize/resizerstate~ResizeState} resizeState
     * The `ResizeState` class instance, used for keeping the `SizeView` state up to date.
     */
    _bindToState(options, resizeState) {
        this.bind('_isVisible').to(resizeState, 'proposedWidth', resizeState, 'proposedHeight', (width, height) => width !== null && height !== null);
        this.bind('_label').to(resizeState, 'proposedHandleHostWidth', resizeState, 'proposedHandleHostHeight', resizeState, 'proposedWidthPercents', (width, height, widthPercents) => {
            if (options.unit === 'px') {
                return `${width}×${height}`;
            }
            else {
                return `${widthPercents}%`;
            }
        });
        this.bind('_viewPosition').to(resizeState, 'activeHandlePosition', resizeState, 'proposedHandleHostWidth', resizeState, 'proposedHandleHostHeight', 
        // If the widget is too small to contain the size label, display the label above.
        (position, width, height) => width < 50 || height < 50 ? 'above-center' : position);
    }
    /**
     * A method used for cleaning up. It removes the bindings and hides the view.
     *
     * @protected
     * @internal
     */
    _dismiss() {
        this.unbind();
        this._isVisible = false;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widgetresize/resizer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/widgetresize/resizer
 */






/**
 * Represents a resizer for a single resizable object.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class Resizer extends Observable {
    /**
     * @param {module:widget/widgetresize~ResizerOptions} options Resizer options.
     */
    constructor(options) {
        super();
        /**
         * Stores the state of the resizable host geometry, such as the original width, the currently proposed height, etc.
         *
         * Note that a new state is created for each resize transaction.
         *
         * @readonly
         * @member {module:widget/widgetresize/resizerstate~ResizerState} #state
         */
        /**
         * A view displaying the proposed new element size during the resizing.
         *
         * @protected
         * @readonly
         * @member {module:widget/widgetresize/sizeview~SizeView} #_sizeView
         */
        /**
         * Options passed to the {@link #constructor}.
         *
         * @private
         * @type {module:widget/widgetresize~ResizerOptions}
         */
        this._options = options;
        /**
         * A wrapper that is controlled by the resizer. This is usually a widget element.
         *
         * @private
         * @type {module:engine/view/element~Element|null}
         */
        this._viewResizerWrapper = null;
        /**
         * The width of the resized {@link module:widget/widgetresize~ResizerOptions#viewElement viewElement} before the resizing started.
         *
         * @private
         * @member {Number|String|undefined} #_initialViewWidth
         */
        /**
         * Flag that indicates whether resizer can be used.
         *
         * @observable
         */
        this.set('isEnabled', true);
        /**
         * Flag that indicates that resizer is currently focused.
         *
         * @observable
         */
        this.set('isSelected', false);
        /**
         * Flag that indicates whether resizer is rendered (visible on the screen).
         *
         * @readonly
         * @observable
         */
        this.bind('isVisible').to(this, 'isEnabled', this, 'isSelected', (isEnabled, isSelected) => isEnabled && isSelected);
        this.decorate('begin');
        this.decorate('cancel');
        this.decorate('commit');
        this.decorate('updateSize');
        this.on('commit', event => {
            // State might not be initialized yet. In this case, prevent further handling and make sure that the resizer is
            // cleaned up (#5195).
            if (!this.state.proposedWidth && !this.state.proposedWidthPercents) {
                this._cleanup();
                event.stop();
            }
        }, { priority: 'high' });
    }
    get state() {
        return this._state;
    }
    /**
     * Makes resizer visible in the UI.
     */
    show() {
        const editingView = this._options.editor.editing.view;
        editingView.change(writer => {
            writer.removeClass('ck-hidden', this._viewResizerWrapper);
        });
    }
    /**
     * Hides resizer in the UI.
     */
    hide() {
        const editingView = this._options.editor.editing.view;
        editingView.change(writer => {
            writer.addClass('ck-hidden', this._viewResizerWrapper);
        });
    }
    /**
     * Attaches the resizer to the DOM.
     */
    attach() {
        // eslint-disable-next-line @typescript-eslint/no-this-alias
        const that = this;
        const widgetElement = this._options.viewElement;
        const editingView = this._options.editor.editing.view;
        editingView.change(writer => {
            const viewResizerWrapper = writer.createUIElement('div', {
                class: 'ck ck-reset_all ck-widget__resizer'
            }, function (domDocument) {
                const domElement = this.toDomElement(domDocument);
                that._appendHandles(domElement);
                that._appendSizeUI(domElement);
                return domElement;
            });
            // Append the resizer wrapper to the widget's wrapper.
            writer.insert(writer.createPositionAt(widgetElement, 'end'), viewResizerWrapper);
            writer.addClass('ck-widget_with-resizer', widgetElement);
            this._viewResizerWrapper = viewResizerWrapper;
            if (!this.isVisible) {
                this.hide();
            }
        });
        this.on('change:isVisible', () => {
            if (this.isVisible) {
                this.show();
                this.redraw();
            }
            else {
                this.hide();
            }
        });
    }
    /**
     * Starts the resizing process.
     *
     * Creates a new {@link #state} for the current process.
     *
     * @fires begin
     * @param {HTMLElement} domResizeHandle Clicked handle.
     */
    begin(domResizeHandle) {
        this._state = new ResizeState(this._options);
        this._sizeView._bindToState(this._options, this.state);
        this._initialViewWidth = this._options.viewElement.getStyle('width');
        this.state.begin(domResizeHandle, this._getHandleHost(), this._getResizeHost());
    }
    /**
     * Updates the proposed size based on `domEventData`.
     *
     * @fires updateSize
     * @param {Event} domEventData
     */
    updateSize(domEventData) {
        const newSize = this._proposeNewSize(domEventData);
        const editingView = this._options.editor.editing.view;
        editingView.change(writer => {
            const unit = this._options.unit || '%';
            const newWidth = (unit === '%' ? newSize.widthPercents : newSize.width) + unit;
            writer.setStyle('width', newWidth, this._options.viewElement);
        });
        // Get an actual image width, and:
        // * reflect this size to the resize wrapper
        // * apply this **real** size to the state
        const domHandleHost = this._getHandleHost();
        const domHandleHostRect = new rect_Rect(domHandleHost);
        const handleHostWidth = Math.round(domHandleHostRect.width);
        const handleHostHeight = Math.round(domHandleHostRect.height);
        // Handle max-width limitation.
        const domResizeHostRect = new rect_Rect(domHandleHost);
        newSize.width = Math.round(domResizeHostRect.width);
        newSize.height = Math.round(domResizeHostRect.height);
        this.redraw(domHandleHostRect);
        this.state.update({
            ...newSize,
            handleHostWidth,
            handleHostHeight
        });
    }
    /**
     * Applies the geometry proposed with the resizer.
     *
     * @fires commit
     */
    commit() {
        const unit = this._options.unit || '%';
        const newValue = (unit === '%' ? this.state.proposedWidthPercents : this.state.proposedWidth) + unit;
        // Both cleanup and onCommit callback are very likely to make view changes. Ensure that it is made in a single step.
        this._options.editor.editing.view.change(() => {
            this._cleanup();
            this._options.onCommit(newValue);
        });
    }
    /**
     * Cancels and rejects the proposed resize dimensions, hiding the UI.
     *
     * @fires cancel
     */
    cancel() {
        this._cleanup();
    }
    /**
     * Destroys the resizer.
     */
    destroy() {
        this.cancel();
    }
    /**
     * Redraws the resizer.
     *
     * @param {module:utils/dom/rect~Rect} [handleHostRect] Handle host rectangle might be given to improve performance.
     */
    redraw(handleHostRect) {
        const domWrapper = this._domResizerWrapper;
        // Refresh only if resizer exists in the DOM.
        if (!existsInDom(domWrapper)) {
            return;
        }
        const widgetWrapper = domWrapper.parentElement;
        const handleHost = this._getHandleHost();
        const resizerWrapper = this._viewResizerWrapper;
        const currentDimensions = [
            resizerWrapper.getStyle('width'),
            resizerWrapper.getStyle('height'),
            resizerWrapper.getStyle('left'),
            resizerWrapper.getStyle('top')
        ];
        let newDimensions;
        if (widgetWrapper.isSameNode(handleHost)) {
            const clientRect = handleHostRect || new rect_Rect(handleHost);
            newDimensions = [
                clientRect.width + 'px',
                clientRect.height + 'px',
                undefined,
                undefined
            ];
        }
        // In case a resizing host is not a widget wrapper, we need to compensate
        // for any additional offsets the resize host might have. E.g. wrapper padding
        // or simply another editable. By doing that the border and resizers are shown
        // only around the resize host.
        else {
            newDimensions = [
                handleHost.offsetWidth + 'px',
                handleHost.offsetHeight + 'px',
                handleHost.offsetLeft + 'px',
                handleHost.offsetTop + 'px'
            ];
        }
        // Make changes to the view only if the resizer should actually get new dimensions.
        // Otherwise, if View#change() was always called, this would cause EditorUI#update
        // loops because the WidgetResize plugin listens to EditorUI#update and updates
        // the resizer.
        // https://github.com/ckeditor/ckeditor5/issues/7633
        if (compareArrays(currentDimensions, newDimensions) !== 'same') {
            this._options.editor.editing.view.change(writer => {
                writer.setStyle({
                    width: newDimensions[0],
                    height: newDimensions[1],
                    left: newDimensions[2],
                    top: newDimensions[3]
                }, resizerWrapper);
            });
        }
    }
    containsHandle(domElement) {
        return this._domResizerWrapper.contains(domElement);
    }
    static isResizeHandle(domElement) {
        return domElement.classList.contains('ck-widget__resizer__handle');
    }
    /**
     * Cleans up the context state.
     *
     * @protected
     */
    _cleanup() {
        this._sizeView._dismiss();
        const editingView = this._options.editor.editing.view;
        editingView.change(writer => {
            writer.setStyle('width', this._initialViewWidth, this._options.viewElement);
        });
    }
    /**
     * Calculates the proposed size as the resize handles are dragged.
     *
     * @private
     * @param {Event} domEventData Event data that caused the size update request. It should be used to calculate the proposed size.
     * @returns {Object} return
     * @returns {Number} return.width Proposed width.
     * @returns {Number} return.height Proposed height.
     */
    _proposeNewSize(domEventData) {
        const state = this.state;
        const currentCoordinates = extractCoordinates(domEventData);
        const isCentered = this._options.isCentered ? this._options.isCentered(this) : true;
        // Enlargement defines how much the resize host has changed in a given axis. Naturally it could be a negative number
        // meaning that it has been shrunk.
        //
        // +----------------+--+
        // |                |  |
        // |       img      |  |
        // |  /handle host  |  |
        // +----------------+  | ^
        // |                   | | - enlarge y
        // +-------------------+ v
        // 					<-->
        // 					 enlarge x
        const enlargement = {
            x: state._referenceCoordinates.x - (currentCoordinates.x + state.originalWidth),
            y: (currentCoordinates.y - state.originalHeight) - state._referenceCoordinates.y
        };
        if (isCentered && state.activeHandlePosition.endsWith('-right')) {
            enlargement.x = currentCoordinates.x - (state._referenceCoordinates.x + state.originalWidth);
        }
        // Objects needs to be resized twice as much in horizontal axis if centered, since enlargement is counted from
        // one resized corner to your cursor. It needs to be duplicated to compensate for the other side too.
        if (isCentered) {
            enlargement.x *= 2;
        }
        // const resizeHost = this._getResizeHost();
        // The size proposed by the user. It does not consider the aspect ratio.
        let width = Math.abs(state.originalWidth + enlargement.x);
        let height = Math.abs(state.originalHeight + enlargement.y);
        // Dominant determination must take the ratio into account.
        const dominant = width / state.aspectRatio > height ? 'width' : 'height';
        if (dominant == 'width') {
            height = width / state.aspectRatio;
        }
        else {
            width = height * state.aspectRatio;
        }
        return {
            width: Math.round(width),
            height: Math.round(height),
            widthPercents: Math.min(Math.round(state.originalWidthPercents / state.originalWidth * width * 100) / 100, 100)
        };
    }
    /**
     * Obtains the resize host.
     *
     * Resize host is an object that receives dimensions which are the result of resizing.
     *
     * @protected
     * @returns {HTMLElement}
     */
    _getResizeHost() {
        const widgetWrapper = this._domResizerWrapper.parentElement;
        return this._options.getResizeHost(widgetWrapper);
    }
    /**
     * Obtains the handle host.
     *
     * Handle host is an object that the handles are aligned to.
     *
     * Handle host will not always be an entire widget itself. Take an image as an example. The image widget
     * contains an image and a caption. Only the image should be surrounded with handles.
     *
     * @protected
     * @returns {HTMLElement}
     */
    _getHandleHost() {
        const widgetWrapper = this._domResizerWrapper.parentElement;
        return this._options.getHandleHost(widgetWrapper);
    }
    /**
     * DOM container of the entire resize UI.
     *
     * Note that this property will have a value only after the element bound with the resizer is rendered
     * (otherwise `null`).
     *
     * @private
     * @member {HTMLElement|null}
     */
    get _domResizerWrapper() {
        return this._options.editor.editing.view.domConverter.mapViewToDom(this._viewResizerWrapper);
    }
    /**
     * Renders the resize handles in the DOM.
     *
     * @private
     * @param {HTMLElement} domElement The resizer wrapper.
     */
    _appendHandles(domElement) {
        const resizerPositions = ['top-left', 'top-right', 'bottom-right', 'bottom-left'];
        for (const currentPosition of resizerPositions) {
            domElement.appendChild((new Template({
                tag: 'div',
                attributes: {
                    class: `ck-widget__resizer__handle ${getResizerClass(currentPosition)}`
                }
            }).render()));
        }
    }
    /**
     * Sets up the {@link #_sizeView} property and adds it to the passed `domElement`.
     *
     * @private
     * @param {HTMLElement} domElement
     */
    _appendSizeUI(domElement) {
        this._sizeView = new SizeView();
        // Make sure icon#element is rendered before passing to appendChild().
        this._sizeView.render();
        domElement.appendChild(this._sizeView.element);
    }
}
// @private
// @param {String} resizerPosition Expected resizer position like `"top-left"`, `"bottom-right"`.
// @returns {String} A prefixed HTML class name for the resizer element
function getResizerClass(resizerPosition) {
    return `ck-widget__resizer__handle-${resizerPosition}`;
}
function extractCoordinates(event) {
    return {
        x: event.pageX,
        y: event.pageY
    };
}
function existsInDom(element) {
    return element && element.ownerDocument && element.ownerDocument.contains(element);
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-widget/theme/widgetresize.css
var widgetresize = __webpack_require__(2263);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/theme/widgetresize.css

            

var widgetresize_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

widgetresize_options.insert = "head";
widgetresize_options.singleton = true;

var widgetresize_update = injectStylesIntoStyleTag_default()(widgetresize/* default */.Z, widgetresize_options);



/* harmony default export */ const theme_widgetresize = (widgetresize/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/widgetresize.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module widget/widgetresize
 */







/**
 * The widget resize feature plugin.
 *
 * Use the {@link module:widget/widgetresize~WidgetResize#attachTo} method to create a resizer for the specified widget.
 *
 * @extends module:core/plugin~Plugin
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class WidgetResize extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'WidgetResize';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editing = this.editor.editing;
        const domDocument = dom_global.window.document;
        /**
         * The currently selected resizer.
         *
         * @observable
         * @member {module:widget/widgetresize/resizer~Resizer|null} #selectedResizer
         */
        this.set('selectedResizer', null);
        /**
         * References an active resizer.
         *
         * Active resizer means a resizer which handle is actively used by the end user.
         *
         * @protected
         * @observable
         * @member {module:widget/widgetresize/resizer~Resizer|null} #_activeResizer
         */
        this.set('_activeResizer', null);
        /**
         * A map of resizers created using this plugin instance.
         *
         * @protected
         * @type {Map.<module:engine/view/containerelement~ContainerElement, module:widget/widgetresize/resizer~Resizer>}
         */
        this._resizers = new Map();
        editing.view.addObserver(MouseObserver);
        this._observer = new emittermixin_Emitter();
        this.listenTo(editing.view.document, 'mousedown', this._mouseDownListener.bind(this), { priority: 'high' });
        this._observer.listenTo(domDocument, 'mousemove', this._mouseMoveListener.bind(this));
        this._observer.listenTo(domDocument, 'mouseup', this._mouseUpListener.bind(this));
        this._redrawSelectedResizerThrottled = lodash_es_throttle(() => this.redrawSelectedResizer(), 200);
        // Redrawing on any change of the UI of the editor (including content changes).
        this.editor.ui.on('update', this._redrawSelectedResizerThrottled);
        // Remove view widget-resizer mappings for widgets that have been removed from the document.
        // https://github.com/ckeditor/ckeditor5/issues/10156
        // https://github.com/ckeditor/ckeditor5/issues/10266
        this.editor.model.document.on('change', () => {
            for (const [viewElement, resizer] of this._resizers) {
                if (!viewElement.isAttached()) {
                    this._resizers.delete(viewElement);
                    resizer.destroy();
                }
            }
        }, { priority: 'lowest' });
        // Resizers need to be redrawn upon window resize, because new window might shrink resize host.
        this._observer.listenTo(dom_global.window, 'resize', this._redrawSelectedResizerThrottled);
        const viewSelection = this.editor.editing.view.document.selection;
        viewSelection.on('change', () => {
            const selectedElement = viewSelection.getSelectedElement();
            const resizer = this.getResizerByViewElement(selectedElement) || null;
            if (resizer) {
                this.select(resizer);
            }
            else {
                this.deselect();
            }
        });
    }
    /**
     * Redraws the selected resizer if there is any selected resizer and if it is visible.
     */
    redrawSelectedResizer() {
        if (this.selectedResizer && this.selectedResizer.isVisible) {
            this.selectedResizer.redraw();
        }
    }
    /**
     * @inheritDoc
     */
    destroy() {
        super.destroy();
        this._observer.stopListening();
        for (const resizer of this._resizers.values()) {
            resizer.destroy();
        }
        this._redrawSelectedResizerThrottled.cancel();
    }
    /**
     * Marks resizer as selected.
     *
     * @param {module:widget/widgetresize/resizer~Resizer} resizer
     */
    select(resizer) {
        this.deselect();
        this.selectedResizer = resizer;
        this.selectedResizer.isSelected = true;
    }
    /**
     * Deselects currently set resizer.
     */
    deselect() {
        if (this.selectedResizer) {
            this.selectedResizer.isSelected = false;
        }
        this.selectedResizer = null;
    }
    /**
     * @param {module:widget/widgetresize~ResizerOptions} [options] Resizer options.
     * @returns {module:widget/widgetresize/resizer~Resizer}
     */
    attachTo(options) {
        const resizer = new Resizer(options);
        const plugins = this.editor.plugins;
        resizer.attach();
        if (plugins.has('WidgetToolbarRepository')) {
            // Hiding widget toolbar to improve the performance
            // (https://github.com/ckeditor/ckeditor5-widget/pull/112#issuecomment-564528765).
            const widgetToolbarRepository = plugins.get('WidgetToolbarRepository');
            resizer.on('begin', () => {
                widgetToolbarRepository.forceDisabled('resize');
            }, { priority: 'lowest' });
            resizer.on('cancel', () => {
                widgetToolbarRepository.clearForceDisabled('resize');
            }, { priority: 'highest' });
            resizer.on('commit', () => {
                widgetToolbarRepository.clearForceDisabled('resize');
            }, { priority: 'highest' });
        }
        this._resizers.set(options.viewElement, resizer);
        const viewSelection = this.editor.editing.view.document.selection;
        const selectedElement = viewSelection.getSelectedElement();
        // If the element the resizer is created for is currently focused, it should become visible.
        if (this.getResizerByViewElement(selectedElement) == resizer) {
            this.select(resizer);
        }
        return resizer;
    }
    /**
     * Returns a resizer created for a given view element (widget element).
     *
     * @param {module:engine/view/containerelement~ContainerElement} viewElement View element associated with the resizer.
     * @returns {module:widget/widgetresize/resizer~Resizer|undefined}
     */
    getResizerByViewElement(viewElement) {
        return this._resizers.get(viewElement);
    }
    /**
     * Returns a resizer that contains a given resize handle.
     *
     * @protected
     * @param {HTMLElement} domResizeHandle
     * @returns {module:widget/widgetresize/resizer~Resizer}
     */
    _getResizerByHandle(domResizeHandle) {
        for (const resizer of this._resizers.values()) {
            if (resizer.containsHandle(domResizeHandle)) {
                return resizer;
            }
        }
    }
    /**
     * @protected
     * @param {module:utils/eventinfo~EventInfo} event
     * @param {Event} domEventData Native DOM event.
     */
    _mouseDownListener(event, domEventData) {
        const resizeHandle = domEventData.domTarget;
        if (!Resizer.isResizeHandle(resizeHandle)) {
            return;
        }
        this._activeResizer = this._getResizerByHandle(resizeHandle) || null;
        if (this._activeResizer) {
            this._activeResizer.begin(resizeHandle);
            // Do not call other events when resizing. See: #6755.
            event.stop();
            domEventData.preventDefault();
        }
    }
    /**
     * @protected
     * @param {module:utils/eventinfo~EventInfo} event
     * @param {Event} domEventData Native DOM event.
     */
    _mouseMoveListener(event, domEventData) {
        if (this._activeResizer) {
            this._activeResizer.updateSize(domEventData);
        }
    }
    /**
     * @protected
     */
    _mouseUpListener() {
        if (this._activeResizer) {
            this._activeResizer.commit();
            this._activeResizer = null;
        }
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-widget/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */






;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/widget.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/widget
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/utils
 */



/**
 * Creates a view element representing the inline image.
 *
 *		<span class="image-inline"><img></img></span>
 *
 * Note that `alt` and `src` attributes are converted separately, so they are not included.
 *
 * @protected
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @returns {module:engine/view/containerelement~ContainerElement}
 */
function createInlineImageViewElement( writer ) {
	return writer.createContainerElement( 'span', { class: 'image-inline' },
		writer.createEmptyElement( 'img' )
	);
}

/**
 * Creates a view element representing the block image.
 *
 *		<figure class="image"><img></img></figure>
 *
 * Note that `alt` and `src` attributes are converted separately, so they are not included.
 *
 * @protected
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @returns {module:engine/view/containerelement~ContainerElement}
 */
function createBlockImageViewElement( writer ) {
	return writer.createContainerElement( 'figure', { class: 'image' }, [
		writer.createEmptyElement( 'img' ),
		writer.createSlot()
	] );
}

/**
 * A function returning a `MatcherPattern` for a particular type of View images.
 *
 * @protected
 * @param {module:core/editor/editor~Editor} editor
 * @param {'imageBlock'|'imageInline'} matchImageType The type of created image.
 * @returns {module:engine/view/matcher~MatcherPattern}
 */
function getImgViewElementMatcher( editor, matchImageType ) {
	const imageUtils = editor.plugins.get( 'ImageUtils' );
	const areBothImagePluginsLoaded = editor.plugins.has( 'ImageInlineEditing' ) && editor.plugins.has( 'ImageBlockEditing' );

	return element => {
		// Check if the matched view element is an <img>.
		if ( !imageUtils.isInlineImageView( element ) ) {
			return null;
		}

		// If just one of the plugins is loaded (block or inline), it will match all kinds of images.
		if ( !areBothImagePluginsLoaded ) {
			return getPositiveMatchPattern( element );
		}

		// The <img> can be standalone, wrapped in <figure>...</figure> (ImageBlock plugin) or
		// wrapped in <figure><a>...</a></figure> (LinkImage plugin).
		const imageType = element.findAncestor( imageUtils.isBlockImageView ) ? 'imageBlock' : 'imageInline';

		if ( imageType !== matchImageType ) {
			return null;
		}

		return getPositiveMatchPattern( element );
	};

	function getPositiveMatchPattern( element ) {
		const pattern = {
			name: true
		};

		// This will trigger src consumption (See https://github.com/ckeditor/ckeditor5/issues/11530).
		if ( element.hasAttribute( 'src' ) ) {
			pattern.attributes = [ 'src' ];
		}

		return pattern;
	}
}

/**
 * Considering the current model selection, it returns the name of the model image element
 * (`'imageBlock'` or `'imageInline'`) that will make most sense from the UX perspective if a new
 * image was inserted (also: uploaded, dropped, pasted) at that selection.
 *
 * The assumption is that inserting images into empty blocks or on other block widgets should
 * produce block images. Inline images should be inserted in other cases, e.g. in paragraphs
 * that already contain some text.
 *
 * @protected
 * @param {module:engine/model/schema~Schema} schema
 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
 * @returns {'imageBlock'|'imageInline'}
 */
function determineImageTypeForInsertionAtSelection( schema, selection ) {
	const firstBlock = first_first( selection.getSelectedBlocks() );

	// Insert a block image if the selection is not in/on block elements or it's on a block widget.
	if ( !firstBlock || schema.isObject( firstBlock ) ) {
		return 'imageBlock';
	}

	// A block image should also be inserted into an empty block element
	// (that is not an empty list item so the list won't get split).
	if ( firstBlock.isEmpty && firstBlock.name != 'listItem' ) {
		return 'imageBlock';
	}

	// Otherwise insert an inline image.
	return 'imageInline';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageutils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageutils
 */





/**
 * A set of helpers related to images.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageUtils extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageUtils';
	}

	/**
	 * Checks if the provided model element is an `image` or `imageInline`.
	 *
	 * @param {module:engine/model/element~Element} modelElement
	 * @returns {Boolean}
	 */
	isImage( modelElement ) {
		return this.isInlineImage( modelElement ) || this.isBlockImage( modelElement );
	}

	/**
	 * Checks if the provided view element represents an inline image.
	 *
	 * Also, see {@link module:image/imageutils~ImageUtils#isImageWidget}.
	 *
	 * @param {module:engine/view/element~Element} element
	 * @returns {Boolean}
	 */
	isInlineImageView( element ) {
		return !!element && element.is( 'element', 'img' );
	}

	/**
	 * Checks if the provided view element represents a block image.
	 *
	 * Also, see {@link module:image/imageutils~ImageUtils#isImageWidget}.
	 *
	 * @param {module:engine/view/element~Element} element
	 * @returns {Boolean}
	 */
	isBlockImageView( element ) {
		return !!element && element.is( 'element', 'figure' ) && element.hasClass( 'image' );
	}

	/**
	 * Handles inserting single file. This method unifies image insertion using {@link module:widget/utils~findOptimalInsertionRange}
	 * method.
	 *
	 *		const imageUtils = editor.plugins.get( 'ImageUtils' );
	 *
	 *		imageUtils.insertImage( { src: 'path/to/image.jpg' } );
	 *
	 * @param {Object} [attributes={}] Attributes of the inserted image.
	 * This method filters out the attributes which are disallowed by the {@link module:engine/model/schema~Schema}.
	 * @param {module:engine/model/selection~Selectable} [selectable] Place to insert the image. If not specified,
	 * the {@link module:widget/utils~findOptimalInsertionRange} logic will be applied for the block images
	 * and `model.document.selection` for the inline images.
	 *
	 * **Note**: If `selectable` is passed, this helper will not be able to set selection attributes (such as `linkHref`)
	 * and apply them to the new image. In this case, make sure all selection attributes are passed in `attributes`.
	 *
	 * @param {'imageBlock'|'imageInline'} [imageType] Image type of inserted image. If not specified,
	 * it will be determined automatically depending of editor config or place of the insertion.
	 * @return {module:engine/view/element~Element|null} The inserted model image element.
	 */
	insertImage( attributes = {}, selectable = null, imageType = null ) {
		const editor = this.editor;
		const model = editor.model;
		const selection = model.document.selection;

		imageType = determineImageTypeForInsertion( editor, selectable || selection, imageType );

		// Mix declarative attributes with selection attributes because the new image should "inherit"
		// the latter for best UX. For instance, inline images inserted into existing links
		// should not split them. To do that, they need to have "linkHref" inherited from the selection.
		attributes = {
			...Object.fromEntries( selection.getAttributes() ),
			...attributes
		};

		for ( const attributeName in attributes ) {
			if ( !model.schema.checkAttribute( imageType, attributeName ) ) {
				delete attributes[ attributeName ];
			}
		}

		return model.change( writer => {
			const imageElement = writer.createElement( imageType, attributes );

			model.insertObject( imageElement, selectable, null, {
				setSelection: 'on',
				// If we want to insert a block image (for whatever reason) then we don't want to split text blocks.
				// This applies only when we don't have the selectable specified (i.e., we insert multiple block images at once).
				findOptimalPosition: !selectable && imageType != 'imageInline'
			} );

			// Inserting an image might've failed due to schema regulations.
			if ( imageElement.parent ) {
				return imageElement;
			}

			return null;
		} );
	}

	/**
	 * Returns an image widget editing view element if one is selected or is among the selection's ancestors.
	 *
	 * @protected
	 * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
	 * @returns {module:engine/view/element~Element|null}
	 */
	getClosestSelectedImageWidget( selection ) {
		const selectionPosition = selection.getFirstPosition();

		if ( !selectionPosition ) {
			return null;
		}

		const viewElement = selection.getSelectedElement();

		if ( viewElement && this.isImageWidget( viewElement ) ) {
			return viewElement;
		}

		let parent = selectionPosition.parent;

		while ( parent ) {
			if ( parent.is( 'element' ) && this.isImageWidget( parent ) ) {
				return parent;
			}

			parent = parent.parent;
		}

		return null;
	}

	/**
	 * Returns a image model element if one is selected or is among the selection's ancestors.
	 *
	 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
	 * @returns {module:engine/model/element~Element|null}
	 */
	getClosestSelectedImageElement( selection ) {
		const selectedElement = selection.getSelectedElement();

		return this.isImage( selectedElement ) ? selectedElement : selection.getFirstPosition().findAncestor( 'imageBlock' );
	}

	/**
	 * Checks if image can be inserted at current model selection.
	 *
	 * @protected
	 * @returns {Boolean}
	 */
	isImageAllowed() {
		const model = this.editor.model;
		const selection = model.document.selection;

		return isImageAllowedInParent( this.editor, selection ) && isNotInsideImage( selection );
	}

	/**
	 * Converts a given {@link module:engine/view/element~Element} to an image widget:
	 * * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to recognize the image widget
	 * element.
	 * * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
	 *
	 * @protected
	 * @param {module:engine/view/element~Element} viewElement
	 * @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
	 * @param {String} label The element's label. It will be concatenated with the image `alt` attribute if one is present.
	 * @returns {module:engine/view/element~Element}
	 */
	toImageWidget( viewElement, writer, label ) {
		writer.setCustomProperty( 'image', true, viewElement );

		const labelCreator = () => {
			const imgElement = this.findViewImgElement( viewElement );
			const altText = imgElement.getAttribute( 'alt' );

			return altText ? `${ altText } ${ label }` : label;
		};

		return toWidget( viewElement, writer, { label: labelCreator } );
	}

	/**
	 * Checks if a given view element is an image widget.
	 *
	 * @protected
	 * @param {module:engine/view/element~Element} viewElement
	 * @returns {Boolean}
	 */
	isImageWidget( viewElement ) {
		return !!viewElement.getCustomProperty( 'image' ) && isWidget( viewElement );
	}

	/**
	 * Checks if the provided model element is an `image`.
	 *
	 * @param {module:engine/model/element~Element} modelElement
	 * @returns {Boolean}
	 */
	isBlockImage( modelElement ) {
		return !!modelElement && modelElement.is( 'element', 'imageBlock' );
	}

	/**
	 * Checks if the provided model element is an `imageInline`.
	 *
	 * @param {module:engine/model/element~Element} modelElement
	 * @returns {Boolean}
	 */
	isInlineImage( modelElement ) {
		return !!modelElement && modelElement.is( 'element', 'imageInline' );
	}

	/**
	 * Get the view `<img>` from another view element, e.g. a widget (`<figure class="image">`), a link (`<a>`).
	 *
	 * The `<img>` can be located deep in other elements, so this helper performs a deep tree search.
	 *
	 * @param {module:engine/view/element~Element} figureView
	 * @returns {module:engine/view/element~Element}
	 */
	findViewImgElement( figureView ) {
		if ( this.isInlineImageView( figureView ) ) {
			return figureView;
		}

		const editingView = this.editor.editing.view;

		for ( const { item } of editingView.createRangeIn( figureView ) ) {
			if ( this.isInlineImageView( item ) ) {
				return item;
			}
		}
	}
}

// Checks if image is allowed by schema in optimal insertion parent.
//
// @private
// @param {module:core/editor/editor~Editor} editor
// @param {module:engine/model/selection~Selection} selection
// @returns {Boolean}
function isImageAllowedInParent( editor, selection ) {
	const imageType = determineImageTypeForInsertion( editor, selection );

	if ( imageType == 'imageBlock' ) {
		const parent = getInsertImageParent( selection, editor.model );

		if ( editor.model.schema.checkChild( parent, 'imageBlock' ) ) {
			return true;
		}
	} else if ( editor.model.schema.checkChild( selection.focus, 'imageInline' ) ) {
		return true;
	}

	return false;
}

// Checks if selection is not placed inside an image (e.g. its caption).
//
// @private
// @param {module:engine/model/selection~Selectable} selection
// @returns {Boolean}
function isNotInsideImage( selection ) {
	return [ ...selection.focus.getAncestors() ].every( ancestor => !ancestor.is( 'element', 'imageBlock' ) );
}

// Returns a node that will be used to insert image with `model.insertContent`.
//
// @private
// @param {module:engine/model/selection~Selection} selection
// @param {module:engine/model/model~Model} model
// @returns {module:engine/model/element~Element}
function getInsertImageParent( selection, model ) {
	const insertionRange = utils_findOptimalInsertionRange( selection, model );
	const parent = insertionRange.start.parent;

	if ( parent.isEmpty && !parent.is( 'element', '$root' ) ) {
		return parent.parent;
	}

	return parent;
}

// Determine image element type name depending on editor config or place of insertion.
//
// @private
// @param {module:core/editor/editor~Editor} editor
// @param {module:engine/model/selection~Selectable} selectable
// @param {'imageBlock'|'imageInline'} [imageType] Image element type name. Used to force return of provided element name,
// but only if there is proper plugin enabled.
// @returns {'imageBlock'|'imageInline'} imageType
function determineImageTypeForInsertion( editor, selectable, imageType ) {
	const schema = editor.model.schema;
	const configImageInsertType = editor.config.get( 'image.insert.type' );

	if ( !editor.plugins.has( 'ImageBlockEditing' ) ) {
		return 'imageInline';
	}

	if ( !editor.plugins.has( 'ImageInlineEditing' ) ) {
		return 'imageBlock';
	}

	if ( imageType ) {
		return imageType;
	}

	if ( configImageInsertType === 'inline' ) {
		return 'imageInline';
	}

	if ( configImageInsertType === 'block' ) {
		return 'imageBlock';
	}

	// Try to replace the selected widget (e.g. another image).
	if ( selectable.is( 'selection' ) ) {
		return determineImageTypeForInsertionAtSelection( schema, selectable );
	}

	return schema.checkChild( selectable, 'imageInline' ) ? 'imageInline' : 'imageBlock';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/autoimage.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/autoimage
 */










// Implements the pattern: http(s)://(www.)example.com/path/to/resource.ext?query=params&maybe=too.
const IMAGE_URL_REGEXP = new RegExp( String( /^(http(s)?:\/\/)?[\w-]+\.[\w.~:/[\]@!$&'()*+,;=%-]+/.source +
	/\.(jpg|jpeg|png|gif|ico|webp|JPG|JPEG|PNG|GIF|ICO|WEBP)/.source +
	/(\?[\w.~:/[\]@!$&'()*+,;=%-]*)?/.source +
	/(#[\w.~:/[\]@!$&'()*+,;=%-]*)?$/.source ) );

/**
 * The auto-image plugin. It recognizes image links in the pasted content and embeds
 * them shortly after they are injected into the document.
 *
 * @extends module:core/plugin~Plugin
 */
class AutoImage extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ Clipboard, ImageUtils, Undo, delete_Delete ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'AutoImage';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * The paste–to–embed `setTimeout` ID. Stored as a property to allow
		 * cleaning of the timeout.
		 *
		 * @private
		 * @member {Number} #_timeoutId
		 */
		this._timeoutId = null;

		/**
		 * The position where the `<imageBlock>` element will be inserted after the timeout,
		 * determined each time a new content is pasted into the document.
		 *
		 * @private
		 * @member {module:engine/model/liveposition~LivePosition} #_positionToInsert
		 */
		this._positionToInsert = null;
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const modelDocument = editor.model.document;

		// We need to listen on `Clipboard#inputTransformation` because we need to save positions of selection.
		// After pasting, the content between those positions will be checked for a URL that could be transformed
		// into an image.
		this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', () => {
			const firstRange = modelDocument.selection.getFirstRange();

			const leftLivePosition = LivePosition.fromPosition( firstRange.start );
			leftLivePosition.stickiness = 'toPrevious';

			const rightLivePosition = LivePosition.fromPosition( firstRange.end );
			rightLivePosition.stickiness = 'toNext';

			modelDocument.once( 'change:data', () => {
				this._embedImageBetweenPositions( leftLivePosition, rightLivePosition );

				leftLivePosition.detach();
				rightLivePosition.detach();
			}, { priority: 'high' } );
		} );

		editor.commands.get( 'undo' ).on( 'execute', () => {
			if ( this._timeoutId ) {
				dom_global.window.clearTimeout( this._timeoutId );
				this._positionToInsert.detach();

				this._timeoutId = null;
				this._positionToInsert = null;
			}
		}, { priority: 'high' } );
	}

	/**
	 * Analyzes the part of the document between provided positions in search for a URL representing an image.
	 * When the URL is found, it is automatically converted into an image.
	 *
	 * @protected
	 * @param {module:engine/model/liveposition~LivePosition} leftPosition Left position of the selection.
	 * @param {module:engine/model/liveposition~LivePosition} rightPosition Right position of the selection.
	 */
	_embedImageBetweenPositions( leftPosition, rightPosition ) {
		const editor = this.editor;
		// TODO: Use a marker instead of LiveRange & LivePositions.
		const urlRange = new LiveRange( leftPosition, rightPosition );
		const walker = urlRange.getWalker( { ignoreElementEnd: true } );
		const selectionAttributes = Object.fromEntries( editor.model.document.selection.getAttributes() );
		const imageUtils = this.editor.plugins.get( 'ImageUtils' );

		let src = '';

		for ( const node of walker ) {
			if ( node.item.is( '$textProxy' ) ) {
				src += node.item.data;
			}
		}

		src = src.trim();

		// If the URL does not match the image URL regexp, let's skip that.
		if ( !src.match( IMAGE_URL_REGEXP ) ) {
			urlRange.detach();

			return;
		}

		// Position will not be available in the `setTimeout` function so let's clone it.
		this._positionToInsert = LivePosition.fromPosition( leftPosition );

		// This action mustn't be executed if undo was called between pasting and auto-embedding.
		this._timeoutId = dom_global.window.setTimeout( () => {
			// Do nothing if image element cannot be inserted at the current position.
			// See https://github.com/ckeditor/ckeditor5/issues/2763.
			// Condition must be checked after timeout - pasting may take place on an element, replacing it. The final position matters.
			const imageCommand = editor.commands.get( 'insertImage' );

			if ( !imageCommand.isEnabled ) {
				urlRange.detach();

				return;
			}

			editor.model.change( writer => {
				this._timeoutId = null;

				writer.remove( urlRange );
				urlRange.detach();

				let insertionPosition;

				// Check if the position where the element should be inserted is still valid.
				// Otherwise leave it as undefined to use the logic of insertImage().
				if ( this._positionToInsert.root.rootName !== '$graveyard' ) {
					insertionPosition = this._positionToInsert.toPosition();
				}

				imageUtils.insertImage( { ...selectionAttributes, src }, insertionPosition );

				this._positionToInsert.detach();
				this._positionToInsert = null;
			} );

			editor.plugins.get( 'Delete' ).requestUndoOnBackspace();
		}, 100 );
	}
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/_castSlice.js


/**
 * Casts `array` to a slice if it's needed.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {number} start The start position.
 * @param {number} [end=array.length] The end position.
 * @returns {Array} Returns the cast slice.
 */
function castSlice(array, start, end) {
  var length = array.length;
  end = end === undefined ? length : end;
  return (!start && end >= length) ? array : _baseSlice(array, start, end);
}

/* harmony default export */ const _castSlice = (castSlice);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_hasUnicode.js
/** Used to compose unicode character classes. */
var rsAstralRange = '\\ud800-\\udfff',
    rsComboMarksRange = '\\u0300-\\u036f',
    reComboHalfMarksRange = '\\ufe20-\\ufe2f',
    rsComboSymbolsRange = '\\u20d0-\\u20ff',
    rsComboRange = rsComboMarksRange + reComboHalfMarksRange + rsComboSymbolsRange,
    rsVarRange = '\\ufe0e\\ufe0f';

/** Used to compose unicode capture groups. */
var rsZWJ = '\\u200d';

/** Used to detect strings with [zero-width joiners or code points from the astral planes](http://eev.ee/blog/2015/09/12/dark-corners-of-unicode/). */
var reHasUnicode = RegExp('[' + rsZWJ + rsAstralRange  + rsComboRange + rsVarRange + ']');

/**
 * Checks if `string` contains Unicode symbols.
 *
 * @private
 * @param {string} string The string to inspect.
 * @returns {boolean} Returns `true` if a symbol is found, else `false`.
 */
function hasUnicode(string) {
  return reHasUnicode.test(string);
}

/* harmony default export */ const _hasUnicode = (hasUnicode);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_asciiToArray.js
/**
 * Converts an ASCII `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function asciiToArray(string) {
  return string.split('');
}

/* harmony default export */ const _asciiToArray = (asciiToArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_unicodeToArray.js
/** Used to compose unicode character classes. */
var _unicodeToArray_rsAstralRange = '\\ud800-\\udfff',
    _unicodeToArray_rsComboMarksRange = '\\u0300-\\u036f',
    _unicodeToArray_reComboHalfMarksRange = '\\ufe20-\\ufe2f',
    _unicodeToArray_rsComboSymbolsRange = '\\u20d0-\\u20ff',
    _unicodeToArray_rsComboRange = _unicodeToArray_rsComboMarksRange + _unicodeToArray_reComboHalfMarksRange + _unicodeToArray_rsComboSymbolsRange,
    _unicodeToArray_rsVarRange = '\\ufe0e\\ufe0f';

/** Used to compose unicode capture groups. */
var rsAstral = '[' + _unicodeToArray_rsAstralRange + ']',
    rsCombo = '[' + _unicodeToArray_rsComboRange + ']',
    rsFitz = '\\ud83c[\\udffb-\\udfff]',
    rsModifier = '(?:' + rsCombo + '|' + rsFitz + ')',
    rsNonAstral = '[^' + _unicodeToArray_rsAstralRange + ']',
    rsRegional = '(?:\\ud83c[\\udde6-\\uddff]){2}',
    rsSurrPair = '[\\ud800-\\udbff][\\udc00-\\udfff]',
    _unicodeToArray_rsZWJ = '\\u200d';

/** Used to compose unicode regexes. */
var reOptMod = rsModifier + '?',
    rsOptVar = '[' + _unicodeToArray_rsVarRange + ']?',
    rsOptJoin = '(?:' + _unicodeToArray_rsZWJ + '(?:' + [rsNonAstral, rsRegional, rsSurrPair].join('|') + ')' + rsOptVar + reOptMod + ')*',
    rsSeq = rsOptVar + reOptMod + rsOptJoin,
    rsSymbol = '(?:' + [rsNonAstral + rsCombo + '?', rsCombo, rsRegional, rsSurrPair, rsAstral].join('|') + ')';

/** Used to match [string symbols](https://mathiasbynens.be/notes/javascript-unicode). */
var reUnicode = RegExp(rsFitz + '(?=' + rsFitz + ')|' + rsSymbol + rsSeq, 'g');

/**
 * Converts a Unicode `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function unicodeToArray(string) {
  return string.match(reUnicode) || [];
}

/* harmony default export */ const _unicodeToArray = (unicodeToArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_stringToArray.js




/**
 * Converts `string` to an array.
 *
 * @private
 * @param {string} string The string to convert.
 * @returns {Array} Returns the converted array.
 */
function stringToArray(string) {
  return _hasUnicode(string)
    ? _unicodeToArray(string)
    : _asciiToArray(string);
}

/* harmony default export */ const _stringToArray = (stringToArray);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_createCaseFirst.js





/**
 * Creates a function like `_.lowerFirst`.
 *
 * @private
 * @param {string} methodName The name of the `String` case method to use.
 * @returns {Function} Returns the new case function.
 */
function createCaseFirst(methodName) {
  return function(string) {
    string = lodash_es_toString(string);

    var strSymbols = _hasUnicode(string)
      ? _stringToArray(string)
      : undefined;

    var chr = strSymbols
      ? strSymbols[0]
      : string.charAt(0);

    var trailing = strSymbols
      ? _castSlice(strSymbols, 1).join('')
      : string.slice(1);

    return chr[methodName]() + trailing;
  };
}

/* harmony default export */ const _createCaseFirst = (createCaseFirst);

;// CONCATENATED MODULE: ./node_modules/lodash-es/upperFirst.js


/**
 * Converts the first character of `string` to upper case.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category String
 * @param {string} [string=''] The string to convert.
 * @returns {string} Returns the converted string.
 * @example
 *
 * _.upperFirst('fred');
 * // => 'Fred'
 *
 * _.upperFirst('FRED');
 * // => 'FRED'
 */
var upperFirst = _createCaseFirst('toUpperCase');

/* harmony default export */ const lodash_es_upperFirst = (upperFirst);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/utils
 */

/* global window */



const ATTRIBUTE_WHITESPACES = /[\u0000-\u0020\u00A0\u1680\u180E\u2000-\u2029\u205f\u3000]/g; // eslint-disable-line no-control-regex
const SAFE_URL = /^(?:(?:https?|ftps?|mailto):|[^a-z]|[a-z+.-]+(?:[^a-z+.:-]|$))/i;

// Simplified email test - should be run over previously found URL.
const EMAIL_REG_EXP = /^[\S]+@((?![-_])(?:[-\w\u00a1-\uffff]{0,63}[^-_]\.))+(?:[a-z\u00a1-\uffff]{2,})$/i;

// The regex checks for the protocol syntax ('xxxx://' or 'xxxx:')
// or non-word characters at the beginning of the link ('/', '#' etc.).
const PROTOCOL_REG_EXP = /^((\w+:(\/{2,})?)|(\W))/i;

/**
 * A keystroke used by the {@link module:link/linkui~LinkUI link UI feature}.
 */
const LINK_KEYSTROKE = 'Ctrl+K';

/**
 * Returns `true` if a given view node is the link element.
 *
 * @param {module:engine/view/node~Node} node
 * @returns {Boolean}
 */
function isLinkElement( node ) {
	return node.is( 'attributeElement' ) && !!node.getCustomProperty( 'link' );
}

/**
 * Creates a link {@link module:engine/view/attributeelement~AttributeElement} with the provided `href` attribute.
 *
 * @param {String} href
 * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
 * @returns {module:engine/view/attributeelement~AttributeElement}
 */
function createLinkElement( href, { writer } ) {
	// Priority 5 - https://github.com/ckeditor/ckeditor5-link/issues/121.
	const linkElement = writer.createAttributeElement( 'a', { href }, { priority: 5 } );
	writer.setCustomProperty( 'link', true, linkElement );

	return linkElement;
}

/**
 * Returns a safe URL based on a given value.
 *
 * A URL is considered safe if it is safe for the user (does not contain any malicious code).
 *
 * If a URL is considered unsafe, a simple `"#"` is returned.
 *
 * @protected
 * @param {*} url
 * @returns {String} Safe URL.
 */
function ensureSafeUrl( url ) {
	url = String( url );

	return isSafeUrl( url ) ? url : '#';
}

// Checks whether the given URL is safe for the user (does not contain any malicious code).
//
// @param {String} url URL to check.
function isSafeUrl( url ) {
	const normalizedUrl = url.replace( ATTRIBUTE_WHITESPACES, '' );

	return normalizedUrl.match( SAFE_URL );
}

/**
 * Returns the {@link module:link/link~LinkConfig#decorators `config.link.decorators`} configuration processed
 * to respect the locale of the editor, i.e. to display the {@link module:link/link~LinkDecoratorManualDefinition label}
 * in the correct language.
 *
 * **Note**: Only the few most commonly used labels are translated automatically. Other labels should be manually
 * translated in the {@link module:link/link~LinkConfig#decorators `config.link.decorators`} configuration.
 *
 * @param {module:utils/locale~Locale#t} t shorthand for {@link module:utils/locale~Locale#t Locale#t}
 * @param {Array.<module:link/link~LinkDecoratorDefinition>} The decorator reference
 * where the label values should be localized.
 * @returns {Array.<module:link/link~LinkDecoratorDefinition>}
 */
function getLocalizedDecorators( t, decorators ) {
	const localizedDecoratorsLabels = {
		'Open in a new tab': t( 'Open in a new tab' ),
		'Downloadable': t( 'Downloadable' )
	};

	decorators.forEach( decorator => {
		if ( decorator.label && localizedDecoratorsLabels[ decorator.label ] ) {
			decorator.label = localizedDecoratorsLabels[ decorator.label ];
		}
		return decorator;
	} );

	return decorators;
}

/**
 * Converts an object with defined decorators to a normalized array of decorators. The `id` key is added for each decorator and
 * is used as the attribute's name in the model.
 *
 * @param {Object.<String, module:link/link~LinkDecoratorDefinition>} decorators
 * @returns {Array.<module:link/link~LinkDecoratorDefinition>}
 */
function normalizeDecorators( decorators ) {
	const retArray = [];

	if ( decorators ) {
		for ( const [ key, value ] of Object.entries( decorators ) ) {
			const decorator = Object.assign(
				{},
				value,
				{ id: `link${ lodash_es_upperFirst( key ) }` }
			);
			retArray.push( decorator );
		}
	}

	return retArray;
}

/**
 * Returns `true` if the specified `element` can be linked (the element allows the `linkHref` attribute).
 *
 * @params {module:engine/model/element~Element|null} element
 * @params {module:engine/model/schema~Schema} schema
 * @returns {Boolean}
 */
function isLinkableElement( element, schema ) {
	if ( !element ) {
		return false;
	}

	return schema.checkAttribute( element.name, 'linkHref' );
}

/**
 * Returns `true` if the specified `value` is an email.
 *
 * @params {String} value
 * @returns {Boolean}
 */
function isEmail( value ) {
	return EMAIL_REG_EXP.test( value );
}

/**
 * Adds the protocol prefix to the specified `link` when:
 *
 * * it does not contain it already, and there is a {@link module:link/link~LinkConfig#defaultProtocol `defaultProtocol` }
 * configuration value provided,
 * * or the link is an email address.
 *
 *
 * @params {String} link
 * @params {String} defaultProtocol
 * @returns {String}
 */
function addLinkProtocolIfApplicable( link, defaultProtocol ) {
	const protocol = isEmail( link ) ? 'mailto:' : defaultProtocol;
	const isProtocolNeeded = !!protocol && !linkHasProtocol( link );

	return link && isProtocolNeeded ? protocol + link : link;
}

/**
 * Checks if protocol is already included in the link.
 *
 * @param {String} link
 * @returns {Boolean}
 */
function linkHasProtocol( link ) {
	return PROTOCOL_REG_EXP.test( link );
}

/**
 * Opens the link in a new browser tab.
 *
 * @param {String} link
 */
function openLink( link ) {
	window.open( link, '_blank', 'noopener' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/autolink.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/autolink
 */






const MIN_LINK_LENGTH_WITH_SPACE_AT_END = 4; // Ie: "t.co " (length 5).

// This was a tweak from https://gist.github.com/dperini/729294.
const URL_REG_EXP = new RegExp(
	// Group 1: Line start or after a space.
	'(^|\\s)' +
	// Group 2: Detected URL (or e-mail).
	'(' +
		// Protocol identifier or short syntax "//"
		// a. Full form http://user@foo.bar.baz:8080/foo/bar.html#baz?foo=bar
		'(' +
			'(?:(?:(?:https?|ftp):)?\\/\\/)' +
			// BasicAuth using user:pass (optional)
			'(?:\\S+(?::\\S*)?@)?' +
			'(?:' +
				// IP address dotted notation octets
				// excludes loopback network 0.0.0.0
				// excludes reserved space >= 224.0.0.0
				// excludes network & broadcast addresses
				// (first & last IP address of each class)
				'(?:[1-9]\\d?|1\\d\\d|2[01]\\d|22[0-3])' +
				'(?:\\.(?:1?\\d{1,2}|2[0-4]\\d|25[0-5])){2}' +
				'(?:\\.(?:[1-9]\\d?|1\\d\\d|2[0-4]\\d|25[0-4]))' +
				'|' +
				'(' +
					// Do not allow `www.foo` - see https://github.com/ckeditor/ckeditor5/issues/8050.
					'((?!www\\.)|(www\\.))' +
					// Host & domain names.
					'(?![-_])(?:[-_a-z0-9\\u00a1-\\uffff]{1,63}\\.)+' +
					// TLD identifier name.
					'(?:[a-z\\u00a1-\\uffff]{2,63})' +
				')' +
			')' +
			// port number (optional)
			'(?::\\d{2,5})?' +
			// resource path (optional)
			'(?:[/?#]\\S*)?' +
		')' +
		'|' +
		// b. Short form (either www.example.com or example@example.com)
		'(' +
			'(www.|(\\S+@))' +
			// Host & domain names.
			'((?![-_])(?:[-_a-z0-9\\u00a1-\\uffff]{1,63}\\.))+' +
			// TLD identifier name.
			'(?:[a-z\\u00a1-\\uffff]{2,63})' +
		')' +
	')$', 'i' );

const URL_GROUP_IN_MATCH = 2;

/**
 * The autolink plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class AutoLink extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ delete_Delete ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'AutoLink';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const selection = editor.model.document.selection;

		selection.on( 'change:range', () => {
			// Disable plugin when selection is inside a code block.
			this.isEnabled = !selection.anchor.parent.is( 'element', 'codeBlock' );
		} );

		this._enableTypingHandling();
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		this._enableEnterHandling();
		this._enableShiftEnterHandling();
	}

	/**
	 * Enables autolinking on typing.
	 *
	 * @private
	 */
	_enableTypingHandling() {
		const editor = this.editor;

		const watcher = new TextWatcher( editor.model, text => {
			// 1. Detect <kbd>Space</kbd> after a text with a potential link.
			if ( !isSingleSpaceAtTheEnd( text ) ) {
				return;
			}

			// 2. Check text before last typed <kbd>Space</kbd>.
			const url = getUrlAtTextEnd( text.substr( 0, text.length - 1 ) );

			if ( url ) {
				return { url };
			}
		} );

		watcher.on( 'matched:data', ( evt, data ) => {
			const { batch, range, url } = data;

			if ( !batch.isTyping ) {
				return;
			}

			const linkEnd = range.end.getShiftedBy( -1 ); // Executed after a space character.
			const linkStart = linkEnd.getShiftedBy( -url.length );

			const linkRange = editor.model.createRange( linkStart, linkEnd );

			this._applyAutoLink( url, linkRange );
		} );

		watcher.bind( 'isEnabled' ).to( this );
	}

	/**
	 * Enables autolinking on the <kbd>Enter</kbd> key.
	 *
	 * @private
	 */
	_enableEnterHandling() {
		const editor = this.editor;
		const model = editor.model;
		const enterCommand = editor.commands.get( 'enter' );

		if ( !enterCommand ) {
			return;
		}

		enterCommand.on( 'execute', () => {
			const position = model.document.selection.getFirstPosition();

			if ( !position.parent.previousSibling ) {
				return;
			}

			const rangeToCheck = model.createRangeIn( position.parent.previousSibling );

			this._checkAndApplyAutoLinkOnRange( rangeToCheck );
		} );
	}

	/**
	 * Enables autolinking on the <kbd>Shift</kbd>+<kbd>Enter</kbd> keyboard shortcut.
	 *
	 * @private
	 */
	_enableShiftEnterHandling() {
		const editor = this.editor;
		const model = editor.model;

		const shiftEnterCommand = editor.commands.get( 'shiftEnter' );

		if ( !shiftEnterCommand ) {
			return;
		}

		shiftEnterCommand.on( 'execute', () => {
			const position = model.document.selection.getFirstPosition();

			const rangeToCheck = model.createRange(
				model.createPositionAt( position.parent, 0 ),
				position.getShiftedBy( -1 )
			);

			this._checkAndApplyAutoLinkOnRange( rangeToCheck );
		} );
	}

	/**
	 * Checks if the passed range contains a linkable text.
	 *
	 * @param {module:engine/model/range~Range} rangeToCheck
	 * @private
	 */
	_checkAndApplyAutoLinkOnRange( rangeToCheck ) {
		const model = this.editor.model;
		const { text, range } = getLastTextLine( rangeToCheck, model );

		const url = getUrlAtTextEnd( text );

		if ( url ) {
			const linkRange = model.createRange(
				range.end.getShiftedBy( -url.length ),
				range.end
			);

			this._applyAutoLink( url, linkRange );
		}
	}

	/**
	 * Applies a link on a given range if the link should be applied.
	 *
	 * @param {String} url The URL to link.
	 * @param {module:engine/model/range~Range} range The text range to apply the link attribute to.
	 * @private
	 */
	_applyAutoLink( url, range ) {
		const model = this.editor.model;

		const defaultProtocol = this.editor.config.get( 'link.defaultProtocol' );
		const fullUrl = addLinkProtocolIfApplicable( url, defaultProtocol );

		if ( !this.isEnabled || !isLinkAllowedOnRange( range, model ) || !linkHasProtocol( fullUrl ) || linkIsAlreadySet( range ) ) {
			return;
		}

		this._persistAutoLink( fullUrl, range );
	}

	/**
	 * Enqueues autolink changes in the model.
	 *
	 * @param {String} url The URL to link.
	 * @param {module:engine/model/range~Range} range The text range to apply the link attribute to.
	 * @protected
	 */
	_persistAutoLink( url, range ) {
		const model = this.editor.model;
		const deletePlugin = this.editor.plugins.get( 'Delete' );

		// Enqueue change to make undo step.
		model.enqueueChange( writer => {
			writer.setAttribute( 'linkHref', url, range );

			model.enqueueChange( () => {
				deletePlugin.requestUndoOnBackspace();
			} );
		} );
	}
}

// Check if text should be evaluated by the plugin in order to reduce number of RegExp checks on whole text.
function isSingleSpaceAtTheEnd( text ) {
	return text.length > MIN_LINK_LENGTH_WITH_SPACE_AT_END && text[ text.length - 1 ] === ' ' && text[ text.length - 2 ] !== ' ';
}

function getUrlAtTextEnd( text ) {
	const match = URL_REG_EXP.exec( text );

	return match ? match[ URL_GROUP_IN_MATCH ] : null;
}

function isLinkAllowedOnRange( range, model ) {
	return model.schema.checkAttributeInSelection( model.createSelection( range ), 'linkHref' );
}

function linkIsAlreadySet( range ) {
	const item = range.start.nodeAfter;
	return item && item.hasAttribute( 'linkHref' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-autosave/src/autosave.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module autosave/autosave
 */





/* globals window */

/**
 * The {@link module:autosave/autosave~Autosave} plugin allows you to automatically save the data (e.g. send it to the server)
 * when needed (when the user changed the content).
 *
 * It listens to the {@link module:engine/model/document~Document#event:change:data `editor.model.document#change:data`}
 * and `window#beforeunload` events and calls the
 * {@link module:autosave/autosave~AutosaveAdapter#save `config.autosave.save()`} function.
 *
 *		ClassicEditor
 *			.create( document.querySelector( '#editor' ), {
 *				plugins: [ ArticlePluginSet, Autosave ],
 *				toolbar: [ 'heading', '|', 'bold', 'italic', 'link', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo' ],
 *				image: {
 *					toolbar: [ 'imageStyle:block', 'imageStyle:side', '|', 'toggleImageCaption', 'imageTextAlternative' ],
 *				},
 *				autosave: {
 *					save( editor ) {
 *						// The saveData() function must return a promise
 *						// which should be resolved when the data is successfully saved.
 *						return saveData( editor.getData() );
 *					}
 *				}
 *			} );
 *
 * Read more about this feature in the {@glink installation/advanced/saving-data#autosave-feature Autosave feature}
 * section of the {@glink installation/advanced/saving-data Saving and getting data}.
 *
 * @extends module:core/plugin~Plugin
 */
class Autosave extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Autosave';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ PendingActions ];
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		const config = editor.config.get( 'autosave' ) || {};

		// A minimum amount of time that needs to pass after the last action.
		// After that time the provided save callbacks are being called.
		const waitingTime = config.waitingTime || 1000;

		/**
		 * The adapter is an object with a `save()` method. That method will be called whenever
		 * the data changes. It might be called some time after the change,
		 * since the event is throttled for performance reasons.
		 *
		 * @member {module:autosave/autosave~AutosaveAdapter} #adapter
		 */

		/**
		 * The state of this plugin.
		 *
		 * The plugin can be in the following states:
		 *
		 * * synchronized &ndash; When all changes are saved.
		 * * waiting &ndash; When the plugin is waiting for other changes before calling `adapter#save()` and `config.autosave.save()`.
		 * * saving &ndash; When the provided save method is called and the plugin waits for the response.
		 * * error &ndash When the provided save method will throw an error. This state immediately changes to the `saving` state and
		 * the save method will be called again in the short period of time.
		 *
		 * @readonly
		 * @member {'synchronized'|'waiting'|'saving'} #state
		 */
		this.set( 'state', 'synchronized' );

		/**
		 * Debounced save method. The `save()` method is called the specified `waitingTime` after `debouncedSave()` is called,
		 * unless a new action happens in the meantime.
		 *
		 * @private
		 * @type {Function}
		 */
		this._debouncedSave = lodash_es_debounce( this._save.bind( this ), waitingTime );

		/**
		 * The last saved document version.
		 *
		 * @private
		 * @type {Number}
		 */
		this._lastDocumentVersion = editor.model.document.version;

		/**
		 * Promise used for asynchronous save calls.
		 *
		 * Created to handle the autosave call to an external data source. It resolves when that call is finished. It is re-used if
		 * save is called before the promise has been resolved. It is set to `null` if there is no call in progress.
		 *
		 * @type {Promise|null}
		 * @private
		 */
		this._savePromise = null;

		/**
		 * DOM emitter.
		 *
		 * @private
		 * @type {DomEmitterMixin}
		 */
		this._domEmitter = Object.create( DomEmitterMixin );

		/**
		 * The configuration of this plugins.
		 *
		 * @private
		 * @type {Object}
		 */
		this._config = config;

		/**
		 * Editor's pending actions manager.
		 *
		 * @private
		 * @member {module:core/pendingactions~PendingActions} #_pendingActions
		 */
		this._pendingActions = editor.plugins.get( PendingActions );

		/**
		 * Informs whether there should be another autosave callback performed, immediately after current autosave callback finishes.
		 *
		 * This is set to `true` when there is a save request while autosave callback is already being processed
		 * and the model has changed since the last save.
		 *
		 * @private
		 * @type {Boolean}
		 */
		this._makeImmediateSave = false;

		/**
		 * An action that will be added to the pending action manager for actions happening in that plugin.
		 *
		 * @private
		 * @member {Object} #_action
		 */
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const doc = editor.model.document;

		// Add the listener only after the editor is initialized to prevent firing save callback on data init.
		this.listenTo( editor, 'ready', () => {
			this.listenTo( doc, 'change:data', ( evt, batch ) => {
				if ( !this._saveCallbacks.length ) {
					return;
				}

				if ( !batch.isLocal ) {
					return;
				}

				if ( this.state === 'synchronized' ) {
					this.state = 'waiting';
					// Set pending action already when we are waiting for the autosave callback.
					this._setPendingAction();
				}

				if ( this.state === 'waiting' ) {
					this._debouncedSave();
				}

				// If the plugin is in `saving` state, it will change its state later basing on the `document.version`.
				// If the `document.version` will be higher than stored `#_lastDocumentVersion`, then it means, that some `change:data`
				// event has fired in the meantime.
			} );
		} );

		// Flush on the editor's destroy listener with the highest priority to ensure that
		// `editor.getData()` will be called before plugins are destroyed.
		this.listenTo( editor, 'destroy', () => this._flush(), { priority: 'highest' } );

		// It's not possible to easy test it because karma uses `beforeunload` event
		// to warn before full page reload and this event cannot be dispatched manually.
		/* istanbul ignore next */
		this._domEmitter.listenTo( window, 'beforeunload', ( evtInfo, domEvt ) => {
			if ( this._pendingActions.hasAny ) {
				domEvt.returnValue = this._pendingActions.first.message;
			}
		} );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		// There's no need for canceling or flushing the throttled save, as
		// it's done on the editor's destroy event with the highest priority.

		this._domEmitter.stopListening();
		super.destroy();
	}

	/**
	 * Immediately calls autosave callback. All previously queued (debounced) callbacks are cleared. If there is already an autosave
	 * callback in progress, then the requested save will be performed immediately after the current callback finishes.
	 *
	 * @returns {Promise} A promise that will be resolved when the autosave callback is finished.
	 */
	save() {
		this._debouncedSave.cancel();

		return this._save();
	}

	/**
	 * Invokes the remaining `_save()` method call.
	 *
	 * @protected
	 */
	_flush() {
		this._debouncedSave.flush();
	}

	/**
	 * If the adapter is set and a new document version exists,
	 * the `_save()` method creates a pending action and calls the `adapter.save()` method.
	 * It waits for the result and then removes the created pending action.
	 *
	 * @private
	 * @returns {Promise} A promise that will be resolved when the autosave callback is finished.
	 */
	_save() {
		if ( this._savePromise ) {
			this._makeImmediateSave = this.editor.model.document.version > this._lastDocumentVersion;

			return this._savePromise;
		}

		// Make sure there is a pending action (in case if `_save()` was called through manual `save()` call).
		this._setPendingAction();

		this.state = 'saving';
		this._lastDocumentVersion = this.editor.model.document.version;

		// Wait one promise cycle to be sure that save callbacks are not called inside a conversion or when the editor's state changes.
		this._savePromise = Promise.resolve()
			// Make autosave callback.
			.then( () => Promise.all(
				this._saveCallbacks.map( cb => cb( this.editor ) )
			) )
			// When the autosave callback is finished, always clear `this._savePromise`, no matter if it was successful or not.
			.finally( () => {
				this._savePromise = null;
			} )
			// If the save was successful, we have three scenarios:
			//
			// 1. If a save was requested when an autosave callback was already processed, we need to immediately call
			// another autosave callback. In this case, `this._savePromise` will not be resolved until the next callback is done.
			// 2. Otherwise, if changes happened to the model, make a delayed autosave callback (like the change just happened).
			// 3. If no changes happened to the model, return to the `synchronized` state.
			.then( () => {
				if ( this._makeImmediateSave ) {
					this._makeImmediateSave = false;

					// Start another autosave callback. Return a promise that will be resolved after the new autosave callback.
					// This way promises returned by `_save()` will not be resolved until all changes are saved.
					//
					// If `save()` was called when another (most often automatic) autosave callback was already processed,
					// the promise returned by `save()` call will be resolved only after new changes have been saved.
					//
					// Note that it would not work correctly if `this._savePromise` is not cleared.
					return this._save();
				} else {
					if ( this.editor.model.document.version > this._lastDocumentVersion ) {
						this.state = 'waiting';
						this._debouncedSave();
					} else {
						this.state = 'synchronized';
						this._pendingActions.remove( this._action );
						this._action = null;
					}
				}
			} )
			// In case of an error, retry the autosave callback after a delay (and also throw the original error).
			.catch( err => {
				// Change state to `error` so that listeners handling autosave error can be called.
				this.state = 'error';
				// Then, immediately change to the `saving` state as described above.
				// Being in the `saving` state ensures that the autosave callback won't be delayed further by the `change:data` listener.
				this.state = 'saving';

				this._debouncedSave();

				throw err;
			} );

		return this._savePromise;
	}

	/**
	 * Creates a pending action if it is not set already.
	 *
	 * @private
	 */
	_setPendingAction() {
		const t = this.editor.t;

		if ( !this._action ) {
			this._action = this._pendingActions.add( t( 'Saving changes' ) );
		}
	}

	/**
	 * Saves callbacks.
	 *
	 * @private
	 * @type {Array.<Function>}
	 */
	get _saveCallbacks() {
		const saveCallbacks = [];

		if ( this.adapter && this.adapter.save ) {
			saveCallbacks.push( this.adapter.save );
		}

		if ( this._config.save ) {
			saveCallbacks.push( this._config.save );
		}

		return saveCallbacks;
	}
}

mix( Autosave, ObservableMixin );

/**
 * An interface that requires the `save()` method.
 *
 * Used by {@link module:autosave/autosave~Autosave#adapter}.
 *
 * @interface module:autosave/autosave~AutosaveAdapter
 */

/**
 * The method that will be called when the data changes. It should return a promise (e.g. in case of saving content to the database),
 * so the autosave plugin will wait for that action before removing it from pending actions.
 *
 * @method #save
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @returns {Promise.<*>}
 */

/**
 * The configuration of the {@link module:autosave/autosave~Autosave autosave feature}.
 *
 * Read more in {@link module:autosave/autosave~AutosaveConfig}.
 *
 * @member {module:autosave/autosave~AutosaveConfig} module:core/editor/editorconfig~EditorConfig#autosave
 */

/**
 * The configuration of the {@link module:autosave/autosave~Autosave autosave feature}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				autosave: {
 *					save( editor ) {
 *						// The saveData() function must return a promise
 *						// which should be resolved when the data is successfully saved.
 *						return saveData( editor.getData() );
 *					}
 *				}
 *			} );
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor configuration options}.
 *
 * See also the demo of the {@glink installation/advanced/saving-data#autosave-feature autosave feature}.
 *
 * @interface AutosaveConfig
 */

/**
 * The callback to be executed when the data needs to be saved.
 *
 * This function must return a promise which should be resolved when the data is successfully saved.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				autosave: {
 *					save( editor ) {
 *						return saveData( editor.getData() );
 *					}
 *				}
 *			} );
 *			.then( ... )
 *			.catch( ... );
 *
 * @method module:autosave/autosave~AutosaveConfig#save
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @returns {Promise.<*>}
 */

/**
 * The minimum amount of time that needs to pass after the last action to call the provided callback.
 * By default it is 1000 ms.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				autosave: {
 *					save( editor ) {
 *						return saveData( editor.getData() );
 *					},
 *					waitingTime: 2000
 *				}
 *			} );
 *			.then( ... )
 *			.catch( ... );
 *
 * @member {Number} module:autosave/autosave~AutosaveConfig#waitingTime
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-upload/src/filereader.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module upload/filereader
 */
/* globals window */

/**
 * Wrapper over the native `FileReader`.
 */
class FileReader extends Observable {
    /**
     * Creates an instance of the FileReader.
     */
    constructor() {
        super();
        const reader = new window.FileReader();
        /**
         * Instance of native FileReader.
         *
         * @private
         * @member {FileReader} #_reader
         */
        this._reader = reader;
        this._data = undefined;
        /**
         * Number of bytes loaded.
         *
         * @readonly
         * @observable
         * @member {Number} #loaded
         */
        this.set('loaded', 0);
        reader.onprogress = evt => {
            this.loaded = evt.loaded;
        };
    }
    /**
     * Returns error that occurred during file reading.
     *
     * @returns {Error}
     */
    get error() {
        return this._reader.error;
    }
    /**
     * Holds the data of an already loaded file. The file must be first loaded
     * by using {@link module:upload/filereader~FileReader#read `read()`}.
     *
     * @type {File|undefined}
     */
    get data() {
        return this._data;
    }
    /**
     * Reads the provided file.
     *
     * @param {File} file Native File object.
     * @returns {Promise.<String>} Returns a promise that will be resolved with file's content.
     * The promise will be rejected in case of an error or when the reading process is aborted.
     */
    read(file) {
        const reader = this._reader;
        this.total = file.size;
        return new Promise((resolve, reject) => {
            reader.onload = () => {
                const result = reader.result;
                this._data = result;
                resolve(result);
            };
            reader.onerror = () => {
                reject('error');
            };
            reader.onabort = () => {
                reject('aborted');
            };
            this._reader.readAsDataURL(file);
        });
    }
    /**
     * Aborts file reader.
     */
    abort() {
        this._reader.abort();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-upload/src/filerepository.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module upload/filerepository
 */







/**
 * File repository plugin. A central point for managing file upload.
 *
 * To use it, first you need an upload adapter. Upload adapter's job is to handle communication with the server
 * (sending the file and handling server's response). You can use one of the existing plugins introducing upload adapters
 * (e.g. {@link module:easy-image/cloudservicesuploadadapter~CloudServicesUploadAdapter} or
 * {@link module:adapter-ckfinder/uploadadapter~CKFinderUploadAdapter}) or write your own one – see
 * the {@glink framework/guides/deep-dive/upload-adapter Custom image upload adapter deep dive guide}.
 *
 * Then, you can use {@link module:upload/filerepository~FileRepository#createLoader `createLoader()`} and the returned
 * {@link module:upload/filerepository~FileLoader} instance to load and upload files.
 *
 * @extends module:core/plugin~Plugin
 */
class filerepository_FileRepository extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'FileRepository';
    }
    /**
     * @inheritDoc
     */
    static get requires() {
        return [PendingActions];
    }
    /**
     * @inheritDoc
     */
    init() {
        /**
         * Collection of loaders associated with this repository.
         *
         * @member {module:utils/collection~Collection} #loaders
         */
        this.loaders = new Collection();
        // Keeps upload in a sync with pending actions.
        this.loaders.on('change', () => this._updatePendingAction());
        /**
         * Loaders mappings used to retrieve loaders references.
         *
         * @private
         * @member {Map<File|Promise, FileLoader>} #_loadersMap
         */
        this._loadersMap = new Map();
        /**
         * Reference to a pending action registered in a {@link module:core/pendingactions~PendingActions} plugin
         * while upload is in progress. When there is no upload then value is `null`.
         *
         * @private
         * @member {Object} #_pendingAction
         */
        this._pendingAction = null;
        /**
         * A factory function which should be defined before using `FileRepository`.
         *
         * It should return a new instance of {@link module:upload/filerepository~UploadAdapter} that will be used to upload files.
         * {@link module:upload/filerepository~FileLoader} instance associated with the adapter
         * will be passed to that function.
         *
         * For more information and example see {@link module:upload/filerepository~UploadAdapter}.
         *
         * @member {Function} #createUploadAdapter
         */
        /**
         * Number of bytes uploaded.
         *
         * @readonly
         * @observable
         * @member {Number} #uploaded
         */
        this.set('uploaded', 0);
        /**
         * Number of total bytes to upload.
         *
         * It might be different than the file size because of headers and additional data.
         * It contains `null` if value is not available yet, so it's better to use {@link #uploadedPercent} to monitor
         * the progress.
         *
         * @readonly
         * @observable
         * @member {Number|null} #uploadTotal
         */
        this.set('uploadTotal', null);
        /**
         * Upload progress in percents.
         *
         * @readonly
         * @observable
         * @member {Number} #uploadedPercent
         */
        this.bind('uploadedPercent').to(this, 'uploaded', this, 'uploadTotal', (uploaded, total) => {
            return total ? (uploaded / total * 100) : 0;
        });
    }
    /**
     * Returns the loader associated with specified file or promise.
     *
     * To get loader by id use `fileRepository.loaders.get( id )`.
     *
     * @param {File|Promise.<File>} fileOrPromise Native file or promise handle.
     * @returns {module:upload/filerepository~FileLoader|null}
     */
    getLoader(fileOrPromise) {
        return this._loadersMap.get(fileOrPromise) || null;
    }
    /**
     * Creates a loader instance for the given file.
     *
     * Requires {@link #createUploadAdapter} factory to be defined.
     *
     * @param {File|Promise.<File>} fileOrPromise Native File object or native Promise object which resolves to a File.
     * @returns {module:upload/filerepository~FileLoader|null}
     */
    createLoader(fileOrPromise) {
        if (!this.createUploadAdapter) {
            /**
             * You need to enable an upload adapter in order to be able to upload files.
             *
             * This warning shows up when {@link module:upload/filerepository~FileRepository} is being used
             * without {@link #createUploadAdapter defining an upload adapter}.
             *
             * **If you see this warning when using one of the {@glink installation/getting-started/predefined-builds
             * CKEditor 5 Builds}**
             * it means that you did not configure any of the upload adapters available by default in those builds.
             *
             * See the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn which upload
             * adapters are available in the builds and how to configure them.
             *
             * **If you see this warning when using a custom build** there is a chance that you enabled
             * a feature like {@link module:image/imageupload~ImageUpload},
             * or {@link module:image/imageupload/imageuploadui~ImageUploadUI} but you did not enable any upload adapter.
             * You can choose one of the existing upload adapters listed in the
             * {@glink features/images/image-upload/image-upload "Image upload overview"}.
             *
             * You can also implement your {@glink framework/guides/deep-dive/upload-adapter own image upload adapter}.
             *
             * @error filerepository-no-upload-adapter
             */
            logWarning('filerepository-no-upload-adapter');
            return null;
        }
        const loader = new FileLoader(Promise.resolve(fileOrPromise), this.createUploadAdapter);
        this.loaders.add(loader);
        this._loadersMap.set(fileOrPromise, loader);
        // Store also file => loader mapping so loader can be retrieved by file instance returned upon Promise resolution.
        if (fileOrPromise instanceof Promise) {
            loader.file
                .then(file => {
                this._loadersMap.set(file, loader);
            })
                // Every then() must have a catch().
                // File loader state (and rejections) are handled in read() and upload().
                // Also, see the "does not swallow the file promise rejection" test.
                .catch(() => { });
        }
        loader.on('change:uploaded', () => {
            let aggregatedUploaded = 0;
            for (const loader of this.loaders) {
                aggregatedUploaded += loader.uploaded;
            }
            this.uploaded = aggregatedUploaded;
        });
        loader.on('change:uploadTotal', () => {
            let aggregatedTotal = 0;
            for (const loader of this.loaders) {
                if (loader.uploadTotal) {
                    aggregatedTotal += loader.uploadTotal;
                }
            }
            this.uploadTotal = aggregatedTotal;
        });
        return loader;
    }
    /**
     * Destroys the given loader.
     *
     * @param {File|Promise|module:upload/filerepository~FileLoader} fileOrPromiseOrLoader File or Promise associated
     * with that loader or loader itself.
     */
    destroyLoader(fileOrPromiseOrLoader) {
        const loader = fileOrPromiseOrLoader instanceof FileLoader ? fileOrPromiseOrLoader : this.getLoader(fileOrPromiseOrLoader);
        loader._destroy();
        this.loaders.remove(loader);
        this._loadersMap.forEach((value, key) => {
            if (value === loader) {
                this._loadersMap.delete(key);
            }
        });
    }
    /**
     * Registers or deregisters pending action bound with upload progress.
     *
     * @private
     */
    _updatePendingAction() {
        const pendingActions = this.editor.plugins.get(PendingActions);
        if (this.loaders.length) {
            if (!this._pendingAction) {
                const t = this.editor.t;
                const getMessage = (value) => `${t('Upload in progress')} ${parseInt(value)}%.`;
                this._pendingAction = pendingActions.add(getMessage(this.uploadedPercent));
                this._pendingAction.bind('message').to(this, 'uploadedPercent', getMessage);
            }
        }
        else {
            pendingActions.remove(this._pendingAction);
            this._pendingAction = null;
        }
    }
}
/**
 * File loader class.
 *
 * It is used to control the process of reading the file and uploading it using the specified upload adapter.
 */
class FileLoader extends Observable {
    /**
     * Creates a new instance of `FileLoader`.
     *
     * @param {Promise.<File>} filePromise A promise which resolves to a file instance.
     * @param {Function} uploadAdapterCreator The function which returns {@link module:upload/filerepository~UploadAdapter} instance.
     */
    constructor(filePromise, uploadAdapterCreator) {
        super();
        /**
         * Unique id of FileLoader instance.
         *
         * @readonly
         * @member {Number}
         */
        this.id = uid();
        /**
         * Additional wrapper over the initial file promise passed to this loader.
         *
         * @protected
         * @member {module:upload/filerepository~FilePromiseWrapper}
         */
        this._filePromiseWrapper = this._createFilePromiseWrapper(filePromise);
        /**
         * Adapter instance associated with this file loader.
         *
         * @private
         * @member {module:upload/filerepository~UploadAdapter}
         */
        this._adapter = uploadAdapterCreator(this);
        /**
         * FileReader used by FileLoader.
         *
         * @protected
         * @member {module:upload/filereader~FileReader}
         */
        this._reader = new FileReader();
        /**
         * Current status of FileLoader. It can be one of the following:
         *
         * * 'idle',
         * * 'reading',
         * * 'uploading',
         * * 'aborted',
         * * 'error'.
         *
         * When reading status can change in a following way:
         *
         * `idle` -> `reading` -> `idle`
         * `idle` -> `reading -> `aborted`
         * `idle` -> `reading -> `error`
         *
         * When uploading status can change in a following way:
         *
         * `idle` -> `uploading` -> `idle`
         * `idle` -> `uploading` -> `aborted`
         * `idle` -> `uploading` -> `error`
         *
         * @readonly
         * @observable
         * @member {String} #status
         */
        this.set('status', 'idle');
        /**
         * Number of bytes uploaded.
         *
         * @readonly
         * @observable
         * @member {Number} #uploaded
         */
        this.set('uploaded', 0);
        /**
         * Number of total bytes to upload.
         *
         * @readonly
         * @observable
         * @member {Number|null} #uploadTotal
         */
        this.set('uploadTotal', null);
        /**
         * Upload progress in percents.
         *
         * @readonly
         * @observable
         * @member {Number} #uploadedPercent
         */
        this.bind('uploadedPercent').to(this, 'uploaded', this, 'uploadTotal', (uploaded, total) => {
            return total ? (uploaded / total * 100) : 0;
        });
        /**
         * Response of the upload.
         *
         * @readonly
         * @observable
         * @member {Object|null} #uploadResponse
         */
        this.set('uploadResponse', null);
    }
    /**
     * A `Promise` which resolves to a `File` instance associated with this file loader.
     *
     * @type {Promise.<File|null>}
     */
    get file() {
        if (!this._filePromiseWrapper) {
            // Loader was destroyed, return promise which resolves to null.
            return Promise.resolve(null);
        }
        else {
            // The `this._filePromiseWrapper.promise` is chained and not simply returned to handle a case when:
            //
            //		* The `loader.file.then( ... )` is called by external code (returned promise is pending).
            //		* Then `loader._destroy()` is called (call is synchronous) which destroys the `loader`.
            //		* Promise returned by the first `loader.file.then( ... )` call is resolved.
            //
            // Returning `this._filePromiseWrapper.promise` will still resolve to a `File` instance so there
            // is an additional check needed in the chain to see if `loader` was destroyed in the meantime.
            return this._filePromiseWrapper.promise.then(file => this._filePromiseWrapper ? file : null);
        }
    }
    /**
     * Returns the file data. To read its data, you need for first load the file
     * by using the {@link module:upload/filerepository~FileLoader#read `read()`} method.
     *
     * @type {File|undefined}
     */
    get data() {
        return this._reader.data;
    }
    /**
     * Reads file using {@link module:upload/filereader~FileReader}.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `filerepository-read-wrong-status` when status
     * is different than `idle`.
     *
     * Example usage:
     *
     *	fileLoader.read()
     *		.then( data => { ... } )
     *		.catch( err => {
     *			if ( err === 'aborted' ) {
     *				console.log( 'Reading aborted.' );
     *			} else {
     *				console.log( 'Reading error.', err );
     *			}
     *		} );
     *
     * @returns {Promise.<String>} Returns promise that will be resolved with read data. Promise will be rejected if error
     * occurs or if read process is aborted.
     */
    read() {
        if (this.status != 'idle') {
            /**
             * You cannot call read if the status is different than idle.
             *
             * @error filerepository-read-wrong-status
             */
            throw new CKEditorError('filerepository-read-wrong-status', this);
        }
        this.status = 'reading';
        return this.file
            .then(file => this._reader.read(file))
            .then(data => {
            // Edge case: reader was aborted after file was read - double check for proper status.
            // It can happen when image was deleted during its upload.
            if (this.status !== 'reading') {
                throw this.status;
            }
            this.status = 'idle';
            return data;
        })
            .catch(err => {
            if (err === 'aborted') {
                this.status = 'aborted';
                throw 'aborted';
            }
            this.status = 'error';
            throw this._reader.error ? this._reader.error : err;
        });
    }
    /**
     * Reads file using the provided {@link module:upload/filerepository~UploadAdapter}.
     *
     * Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `filerepository-upload-wrong-status` when status
     * is different than `idle`.
     * Example usage:
     *
     *	fileLoader.upload()
     *		.then( data => { ... } )
     *		.catch( e => {
     *			if ( e === 'aborted' ) {
     *				console.log( 'Uploading aborted.' );
     *			} else {
     *				console.log( 'Uploading error.', e );
     *			}
     *		} );
     *
     * @returns {Promise.<Object>} Returns promise that will be resolved with response data. Promise will be rejected if error
     * occurs or if read process is aborted.
     */
    upload() {
        if (this.status != 'idle') {
            /**
             * You cannot call upload if the status is different than idle.
             *
             * @error filerepository-upload-wrong-status
             */
            throw new CKEditorError('filerepository-upload-wrong-status', this);
        }
        this.status = 'uploading';
        return this.file
            .then(() => this._adapter.upload())
            .then(data => {
            this.uploadResponse = data;
            this.status = 'idle';
            return data;
        })
            .catch(err => {
            if (this.status === 'aborted') {
                throw 'aborted';
            }
            this.status = 'error';
            throw err;
        });
    }
    /**
     * Aborts loading process.
     */
    abort() {
        const status = this.status;
        this.status = 'aborted';
        if (!this._filePromiseWrapper.isFulfilled) {
            // Edge case: file loader is aborted before read() is called
            // so it might happen that no one handled the rejection of this promise.
            // See https://github.com/ckeditor/ckeditor5-upload/pull/100
            this._filePromiseWrapper.promise.catch(() => { });
            this._filePromiseWrapper.rejecter('aborted');
        }
        else if (status == 'reading') {
            this._reader.abort();
        }
        else if (status == 'uploading' && this._adapter.abort) {
            this._adapter.abort();
        }
        this._destroy();
    }
    /**
     * Performs cleanup.
     *
     * @internal
     */
    _destroy() {
        this._filePromiseWrapper = undefined;
        this._reader = undefined;
        this._adapter = undefined;
        this.uploadResponse = undefined;
    }
    /**
     * Wraps a given file promise into another promise giving additional
     * control (resolving, rejecting, checking if fulfilled) over it.
     *
     * @private
     * @param filePromise The initial file promise to be wrapped.
     * @returns {module:upload/filerepository~FilePromiseWrapper}
     */
    _createFilePromiseWrapper(filePromise) {
        const wrapper = {};
        wrapper.promise = new Promise((resolve, reject) => {
            wrapper.rejecter = reject;
            wrapper.isFulfilled = false;
            filePromise
                .then(file => {
                wrapper.isFulfilled = true;
                resolve(file);
            })
                .catch(err => {
                wrapper.isFulfilled = true;
                reject(err);
            });
        });
        return wrapper;
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-upload/src/adapters/simpleuploadadapter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module upload/adapters/simpleuploadadapter
 */
/* globals XMLHttpRequest, FormData */



/**
 * The Simple upload adapter allows uploading images to an application running on your server using
 * the [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest) API with a
 * minimal {@link module:upload/adapters/simpleuploadadapter~SimpleUploadConfig editor configuration}.
 *
 *		ClassicEditor
 *			.create( document.querySelector( '#editor' ), {
 *				simpleUpload: {
 *					uploadUrl: 'http://example.com',
 *					headers: {
 *						...
 *					}
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See the {@glink features/images/image-upload/simple-upload-adapter "Simple upload adapter"} guide to learn how to
 * learn more about the feature (configuration, server–side requirements, etc.).
 *
 * Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn about
 * other ways to upload images into CKEditor 5.
 *
 * @extends module:core/plugin~Plugin
 */
class SimpleUploadAdapter extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get requires() {
        return [filerepository_FileRepository];
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'SimpleUploadAdapter';
    }
    /**
     * @inheritDoc
     */
    init() {
        const options = this.editor.config.get('simpleUpload');
        if (!options) {
            return;
        }
        if (!options.uploadUrl) {
            /**
             * The {@link module:upload/adapters/simpleuploadadapter~SimpleUploadConfig#uploadUrl `config.simpleUpload.uploadUrl`}
             * configuration required by the {@link module:upload/adapters/simpleuploadadapter~SimpleUploadAdapter `SimpleUploadAdapter`}
             * is missing. Make sure the correct URL is specified for the image upload to work properly.
             *
             * @error simple-upload-adapter-missing-uploadurl
             */
            logWarning('simple-upload-adapter-missing-uploadurl');
            return;
        }
        this.editor.plugins.get(filerepository_FileRepository).createUploadAdapter = loader => {
            return new Adapter(loader, options);
        };
    }
}
/**
 * Upload adapter.
 *
 * @private
 * @implements module:upload/filerepository~UploadAdapter
 */
class Adapter {
    /**
     * Creates a new adapter instance.
     *
     * @param {module:upload/filerepository~FileLoader} loader
     * @param {module:upload/adapters/simpleuploadadapter~SimpleUploadConfig} options
     */
    constructor(loader, options) {
        /**
         * FileLoader instance to use during the upload.
         *
         * @member {module:upload/filerepository~FileLoader} #loader
         */
        this.loader = loader;
        /**
         * The configuration of the adapter.
         *
         * @member {module:upload/adapters/simpleuploadadapter~SimpleUploadConfig} #options
         */
        this.options = options;
    }
    /**
     * Starts the upload process.
     *
     * @see module:upload/filerepository~UploadAdapter#upload
     * @returns {Promise}
     */
    upload() {
        return this.loader.file
            .then(file => new Promise((resolve, reject) => {
            this._initRequest();
            this._initListeners(resolve, reject, file);
            this._sendRequest(file);
        }));
    }
    /**
     * Aborts the upload process.
     *
     * @see module:upload/filerepository~UploadAdapter#abort
     * @returns {Promise}
     */
    abort() {
        if (this.xhr) {
            this.xhr.abort();
        }
    }
    /**
     * Initializes the `XMLHttpRequest` object using the URL specified as
     * {@link module:upload/adapters/simpleuploadadapter~SimpleUploadConfig#uploadUrl `simpleUpload.uploadUrl`} in the editor's
     * configuration.
     *
     * @private
     */
    _initRequest() {
        const xhr = this.xhr = new XMLHttpRequest();
        xhr.open('POST', this.options.uploadUrl, true);
        xhr.responseType = 'json';
    }
    /**
     * Initializes XMLHttpRequest listeners
     *
     * @private
     * @param {Function} resolve Callback function to be called when the request is successful.
     * @param {Function} reject Callback function to be called when the request cannot be completed.
     * @param {File} file Native File object.
     */
    _initListeners(resolve, reject, file) {
        const xhr = this.xhr;
        const loader = this.loader;
        const genericErrorText = `Couldn't upload file: ${file.name}.`;
        xhr.addEventListener('error', () => reject(genericErrorText));
        xhr.addEventListener('abort', () => reject());
        xhr.addEventListener('load', () => {
            const response = xhr.response;
            if (!response || response.error) {
                return reject(response && response.error && response.error.message ? response.error.message : genericErrorText);
            }
            const urls = response.url ? { default: response.url } : response.urls;
            // Resolve with the normalized `urls` property and pass the rest of the response
            // to allow customizing the behavior of features relying on the upload adapters.
            resolve({
                ...response,
                urls
            });
        });
        // Upload progress when it is supported.
        /* istanbul ignore else */
        if (xhr.upload) {
            xhr.upload.addEventListener('progress', evt => {
                if (evt.lengthComputable) {
                    loader.uploadTotal = evt.total;
                    loader.uploaded = evt.loaded;
                }
            });
        }
    }
    /**
     * Prepares the data and sends the request.
     *
     * @private
     * @param {File} file File instance to be uploaded.
     */
    _sendRequest(file) {
        // Set headers if specified.
        const headers = this.options.headers || {};
        // Use the withCredentials flag if specified.
        const withCredentials = this.options.withCredentials || false;
        for (const headerName of Object.keys(headers)) {
            this.xhr.setRequestHeader(headerName, headers[headerName]);
        }
        this.xhr.withCredentials = withCredentials;
        // Prepare the form data.
        const data = new FormData();
        data.append('upload', file);
        // Send the request.
        this.xhr.send(data);
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-enter/src/shiftentercommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module enter/shiftentercommand
 */


/**
 * ShiftEnter command. It is used by the {@link module:enter/shiftenter~ShiftEnter ShiftEnter feature} to handle
 * the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke.
 *
 * @extends module:core/command~Command
 */
class ShiftEnterCommand extends command_Command {
    /**
     * @inheritDoc
     */
    execute() {
        const model = this.editor.model;
        const doc = model.document;
        model.change(writer => {
            softBreakAction(model, writer, doc.selection);
            this.fire('afterExecute', { writer });
        });
    }
    refresh() {
        const model = this.editor.model;
        const doc = model.document;
        this.isEnabled = isEnabled(model.schema, doc.selection);
    }
}
// Checks whether the ShiftEnter command should be enabled in the specified selection.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
function isEnabled(schema, selection) {
    // At this moment it is okay to support single range selections only.
    // But in the future we may need to change that.
    if (selection.rangeCount > 1) {
        return false;
    }
    const anchorPos = selection.anchor;
    // Check whether the break element can be inserted in the current selection anchor.
    if (!anchorPos || !schema.checkChild(anchorPos, 'softBreak')) {
        return false;
    }
    const range = selection.getFirstRange();
    const startElement = range.start.parent;
    const endElement = range.end.parent;
    // Do not modify the content if selection is cross-limit elements.
    if ((isInsideLimitElement(startElement, schema) || isInsideLimitElement(endElement, schema)) && startElement !== endElement) {
        return false;
    }
    return true;
}
// Creates a break in the way that the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke is expected to work.
//
// @param {module:engine/model~Model} model
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// Selection on which the action should be performed.
function softBreakAction(model, writer, selection) {
    const isSelectionEmpty = selection.isCollapsed;
    const range = selection.getFirstRange();
    const startElement = range.start.parent;
    const endElement = range.end.parent;
    const isContainedWithinOneElement = (startElement == endElement);
    if (isSelectionEmpty) {
        const attributesToCopy = getCopyOnEnterAttributes(model.schema, selection.getAttributes());
        insertBreak(model, writer, range.end);
        writer.removeSelectionAttribute(selection.getAttributeKeys());
        writer.setSelectionAttribute(attributesToCopy);
    }
    else {
        const leaveUnmerged = !(range.start.isAtStart && range.end.isAtEnd);
        model.deleteContent(selection, { leaveUnmerged });
        // Selection within one element:
        //
        // <h>x[xx]x</h>		-> <h>x^x</h>			-> <h>x<br>^x</h>
        if (isContainedWithinOneElement) {
            insertBreak(model, writer, selection.focus);
        }
        // Selection over multiple elements.
        //
        // <h>x[x</h><p>y]y<p>	-> <h>x^</h><p>y</p>	-> <h>x</h><p>^y</p>
        //
        // We chose not to insert a line break in this case because:
        //
        // * it's not a very common scenario,
        // * it actually surprised me when I saw the "expected behavior" in real life.
        //
        // It's ok if the user will need to be more specific where they want the <br> to be inserted.
        else {
            // Move the selection to the 2nd element (last step of the example above).
            if (leaveUnmerged) {
                writer.setSelection(endElement, 0);
            }
        }
    }
}
function insertBreak(model, writer, position) {
    const breakLineElement = writer.createElement('softBreak');
    model.insertContent(breakLineElement, position);
    writer.setSelection(breakLineElement, 'after');
}
// Checks whether the specified `element` is a child of the limit element.
//
// Checking whether the `<p>` element is inside a limit element:
//   - <$root><p>Text.</p></$root> => false
//   - <$root><limitElement><p>Text</p></limitElement></$root> => true
//
// @param {module:engine/model/element~Element} element
// @param {module:engine/schema~Schema} schema
// @returns {Boolean}
function isInsideLimitElement(element, schema) {
    // `$root` is a limit element but in this case is an invalid element.
    if (element.is('rootElement')) {
        return false;
    }
    return schema.isLimit(element) || isInsideLimitElement(element.parent, schema);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-enter/src/shiftenter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module enter/shiftenter
 */



/**
 * This plugin handles the <kbd>Shift</kbd>+<kbd>Enter</kbd> keystroke (soft line break) in the editor.
 *
 * See also the {@link module:enter/enter~Enter} plugin.
 *
 * For more information about this feature see the {@glink api/enter package page}.
 *
 * @extends module:core/plugin~Plugin
 */
class ShiftEnter extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'ShiftEnter';
    }
    init() {
        const editor = this.editor;
        const schema = editor.model.schema;
        const conversion = editor.conversion;
        const view = editor.editing.view;
        const viewDocument = view.document;
        // Configure the schema.
        schema.register('softBreak', {
            allowWhere: '$text',
            isInline: true
        });
        // Configure converters.
        conversion.for('upcast')
            .elementToElement({
            model: 'softBreak',
            view: 'br'
        });
        conversion.for('downcast')
            .elementToElement({
            model: 'softBreak',
            view: (modelElement, { writer }) => writer.createEmptyElement('br')
        });
        view.addObserver(EnterObserver);
        editor.commands.add('shiftEnter', new ShiftEnterCommand(editor));
        this.listenTo(viewDocument, 'enter', (evt, data) => {
            // When not in composition, we handle the action, so prevent the default one.
            // When in composition, it's the browser who modify the DOM (renderer is disabled).
            if (!viewDocument.isComposing) {
                data.preventDefault();
            }
            // The hard enter key is handled by the Enter plugin.
            if (!data.isSoft) {
                return;
            }
            editor.execute('shiftEnter');
            view.scrollToTheSelection();
        }, { priority: 'low' });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-enter/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module enter
 */



;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/enter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/enter
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-block-quote/src/blockquotecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module block-quote/blockquotecommand
 */




/**
 * The block quote command plugin.
 *
 * @extends module:core/command~Command
 */
class BlockQuoteCommand extends command_Command {
	/**
	 * Whether the selection starts in a block quote.
	 *
	 * @observable
	 * @readonly
	 * @member {Boolean} #value
	 */

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.value = this._getValue();
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Executes the command. When the command {@link #value is on}, all top-most block quotes within
	 * the selection will be removed. If it is off, all selected blocks will be wrapped with
	 * a block quote.
	 *
	 * @fires execute
	 * @param {Object} [options] Command options.
	 * @param {Boolean} [options.forceValue] If set, it will force the command behavior. If `true`, the command will apply a block quote,
	 * otherwise the command will remove the block quote. If not set, the command will act basing on its current value.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const schema = model.schema;
		const selection = model.document.selection;

		const blocks = Array.from( selection.getSelectedBlocks() );

		const value = ( options.forceValue === undefined ) ? !this.value : options.forceValue;

		model.change( writer => {
			if ( !value ) {
				this._removeQuote( writer, blocks.filter( findQuote ) );
			} else {
				const blocksToQuote = blocks.filter( block => {
					// Already quoted blocks needs to be considered while quoting too
					// in order to reuse their <bQ> elements.
					return findQuote( block ) || checkCanBeQuoted( schema, block );
				} );

				this._applyQuote( writer, blocksToQuote );
			}
		} );
	}

	/**
	 * Checks the command's {@link #value}.
	 *
	 * @private
	 * @returns {Boolean} The current value.
	 */
	_getValue() {
		const selection = this.editor.model.document.selection;

		const firstBlock = first_first( selection.getSelectedBlocks() );

		// In the current implementation, the block quote must be an immediate parent of a block element.
		return !!( firstBlock && findQuote( firstBlock ) );
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		if ( this.value ) {
			return true;
		}

		const selection = this.editor.model.document.selection;
		const schema = this.editor.model.schema;

		const firstBlock = first_first( selection.getSelectedBlocks() );

		if ( !firstBlock ) {
			return false;
		}

		return checkCanBeQuoted( schema, firstBlock );
	}

	/**
	 * Removes the quote from given blocks.
	 *
	 * If blocks which are supposed to be "unquoted" are in the middle of a quote,
	 * start it or end it, then the quote will be split (if needed) and the blocks
	 * will be moved out of it, so other quoted blocks remained quoted.
	 *
	 * @private
	 * @param {module:engine/model/writer~Writer} writer
	 * @param {Array.<module:engine/model/element~Element>} blocks
	 */
	_removeQuote( writer, blocks ) {
		// Unquote all groups of block. Iterate in the reverse order to not break following ranges.
		getRangesOfBlockGroups( writer, blocks ).reverse().forEach( groupRange => {
			if ( groupRange.start.isAtStart && groupRange.end.isAtEnd ) {
				writer.unwrap( groupRange.start.parent );

				return;
			}

			// The group of blocks are at the beginning of an <bQ> so let's move them left (out of the <bQ>).
			if ( groupRange.start.isAtStart ) {
				const positionBefore = writer.createPositionBefore( groupRange.start.parent );

				writer.move( groupRange, positionBefore );

				return;
			}

			// The blocks are in the middle of an <bQ> so we need to split the <bQ> after the last block
			// so we move the items there.
			if ( !groupRange.end.isAtEnd ) {
				writer.split( groupRange.end );
			}

			// Now we are sure that groupRange.end.isAtEnd is true, so let's move the blocks right.

			const positionAfter = writer.createPositionAfter( groupRange.end.parent );

			writer.move( groupRange, positionAfter );
		} );
	}

	/**
	 * Applies the quote to given blocks.
	 *
	 * @private
	 * @param {module:engine/model/writer~Writer} writer
	 * @param {Array.<module:engine/model/element~Element>} blocks
	 */
	_applyQuote( writer, blocks ) {
		const quotesToMerge = [];

		// Quote all groups of block. Iterate in the reverse order to not break following ranges.
		getRangesOfBlockGroups( writer, blocks ).reverse().forEach( groupRange => {
			let quote = findQuote( groupRange.start );

			if ( !quote ) {
				quote = writer.createElement( 'blockQuote' );

				writer.wrap( groupRange, quote );
			}

			quotesToMerge.push( quote );
		} );

		// Merge subsequent <bQ> elements. Reverse the order again because this time we want to go through
		// the <bQ> elements in the source order (due to how merge works – it moves the right element's content
		// to the first element and removes the right one. Since we may need to merge a couple of subsequent `<bQ>` elements
		// we want to keep the reference to the first (furthest left) one.
		quotesToMerge.reverse().reduce( ( currentQuote, nextQuote ) => {
			if ( currentQuote.nextSibling == nextQuote ) {
				writer.merge( writer.createPositionAfter( currentQuote ) );

				return currentQuote;
			}

			return nextQuote;
		} );
	}
}

function findQuote( elementOrPosition ) {
	return elementOrPosition.parent.name == 'blockQuote' ? elementOrPosition.parent : null;
}

// Returns a minimal array of ranges containing groups of subsequent blocks.
//
// content:         abcdefgh
// blocks:          [ a, b, d, f, g, h ]
// output ranges:   [ab]c[d]e[fgh]
//
// @param {Array.<module:engine/model/element~Element>} blocks
// @returns {Array.<module:engine/model/range~Range>}
function getRangesOfBlockGroups( writer, blocks ) {
	let startPosition;
	let i = 0;
	const ranges = [];

	while ( i < blocks.length ) {
		const block = blocks[ i ];
		const nextBlock = blocks[ i + 1 ];

		if ( !startPosition ) {
			startPosition = writer.createPositionBefore( block );
		}

		if ( !nextBlock || block.nextSibling != nextBlock ) {
			ranges.push( writer.createRange( startPosition, writer.createPositionAfter( block ) ) );
			startPosition = null;
		}

		i++;
	}

	return ranges;
}

// Checks whether <bQ> can wrap the block.
function checkCanBeQuoted( schema, block ) {
	// TMP will be replaced with schema.checkWrap().
	const isBQAllowed = schema.checkChild( block.parent, 'blockQuote' );
	const isBlockAllowedInBQ = schema.checkChild( [ '$root', 'blockQuote' ], block );

	return isBQAllowed && isBlockAllowedInBQ;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-block-quote/src/blockquoteediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module block-quote/blockquoteediting
 */







/**
 * The block quote editing.
 *
 * Introduces the `'blockQuote'` command and the `'blockQuote'` model element.
 *
 * @extends module:core/plugin~Plugin
 */
class BlockQuoteEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'BlockQuoteEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ enter_Enter, delete_Delete ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;

		editor.commands.add( 'blockQuote', new BlockQuoteCommand( editor ) );

		schema.register( 'blockQuote', {
			inheritAllFrom: '$container'
		} );

		editor.conversion.elementToElement( { model: 'blockQuote', view: 'blockquote' } );

		// Postfixer which cleans incorrect model states connected with block quotes.
		editor.model.document.registerPostFixer( writer => {
			const changes = editor.model.document.differ.getChanges();

			for ( const entry of changes ) {
				if ( entry.type == 'insert' ) {
					const element = entry.position.nodeAfter;

					if ( !element ) {
						// We are inside a text node.
						continue;
					}

					if ( element.is( 'element', 'blockQuote' ) && element.isEmpty ) {
						// Added an empty blockQuote - remove it.
						writer.remove( element );

						return true;
					} else if ( element.is( 'element', 'blockQuote' ) && !schema.checkChild( entry.position, element ) ) {
						// Added a blockQuote in incorrect place. Unwrap it so the content inside is not lost.
						writer.unwrap( element );

						return true;
					} else if ( element.is( 'element' ) ) {
						// Just added an element. Check that all children meet the scheme rules.
						const range = writer.createRangeIn( element );

						for ( const child of range.getItems() ) {
							if (
								child.is( 'element', 'blockQuote' ) &&
								!schema.checkChild( writer.createPositionBefore( child ), child )
							) {
								writer.unwrap( child );

								return true;
							}
						}
					}
				} else if ( entry.type == 'remove' ) {
					const parent = entry.position.parent;

					if ( parent.is( 'element', 'blockQuote' ) && parent.isEmpty ) {
						// Something got removed and now blockQuote is empty. Remove the blockQuote as well.
						writer.remove( parent );

						return true;
					}
				}
			}

			return false;
		} );

		const viewDocument = this.editor.editing.view.document;
		const selection = editor.model.document.selection;
		const blockQuoteCommand = editor.commands.get( 'blockQuote' );

		// Overwrite default Enter key behavior.
		// If Enter key is pressed with selection collapsed in empty block inside a quote, break the quote.
		this.listenTo( viewDocument, 'enter', ( evt, data ) => {
			if ( !selection.isCollapsed || !blockQuoteCommand.value ) {
				return;
			}

			const positionParent = selection.getLastPosition().parent;

			if ( positionParent.isEmpty ) {
				editor.execute( 'blockQuote' );
				editor.editing.view.scrollToTheSelection();

				data.preventDefault();
				evt.stop();
			}
		}, { context: 'blockquote' } );

		// Overwrite default Backspace key behavior.
		// If Backspace key is pressed with selection collapsed in first empty block inside a quote, break the quote.
		this.listenTo( viewDocument, 'delete', ( evt, data ) => {
			if ( data.direction != 'backward' || !selection.isCollapsed || !blockQuoteCommand.value ) {
				return;
			}

			const positionParent = selection.getLastPosition().parent;

			if ( positionParent.isEmpty && !positionParent.previousSibling ) {
				editor.execute( 'blockQuote' );
				editor.editing.view.scrollToTheSelection();

				data.preventDefault();
				evt.stop();
			}
		}, { context: 'blockquote' } );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-block-quote/theme/blockquote.css
var blockquote = __webpack_require__(636);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-block-quote/theme/blockquote.css

            

var blockquote_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

blockquote_options.insert = "head";
blockquote_options.singleton = true;

var blockquote_update = injectStylesIntoStyleTag_default()(blockquote/* default */.Z, blockquote_options);



/* harmony default export */ const theme_blockquote = (blockquote/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-block-quote/src/blockquoteui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module block-quote/blockquoteui
 */






/**
 * The block quote UI plugin.
 *
 * It introduces the `'blockQuote'` button.
 *
 * @extends module:core/plugin~Plugin
 */
class BlockQuoteUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'BlockQuoteUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.ui.componentFactory.add( 'blockQuote', locale => {
			const command = editor.commands.get( 'blockQuote' );
			const buttonView = new buttonview_ButtonView( locale );

			buttonView.set( {
				label: t( 'Block quote' ),
				icon: icons.quote,
				tooltip: true,
				isToggleable: true
			} );

			// Bind button model to command.
			buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( buttonView, 'execute', () => {
				editor.execute( 'blockQuote' );
				editor.editing.view.focus();
			} );

			return buttonView;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-block-quote/src/blockquote.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module block-quote/blockquote
 */






/**
 * The block quote plugin.
 *
 * For more information about this feature check the {@glink api/block-quote package page}.
 *
 * This is a "glue" plugin which loads the {@link module:block-quote/blockquoteediting~BlockQuoteEditing block quote editing feature}
 * and {@link module:block-quote/blockquoteui~BlockQuoteUI block quote UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class BlockQuote extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ BlockQuoteEditing, BlockQuoteUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'BlockQuote';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/attributecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/attributecommand
 */



/**
 * An extension of the base {@link module:core/command~Command} class, which provides utilities for a command
 * that toggles a single attribute on a text or an element.
 *
 * `AttributeCommand` uses {@link module:engine/model/document~Document#selection}
 * to decide which nodes (if any) should be changed, and applies or removes the attribute from them.
 *
 * The command checks the {@link module:engine/model/model~Model#schema} to decide if it can be enabled
 * for the current selection and to which nodes the attribute can be applied.
 *
 * @extends module:core/command~Command
 */
class AttributeCommand extends command_Command {
	/**
	 * @param {module:core/editor/editor~Editor} editor
	 * @param {String} attributeKey Attribute that will be set by the command.
	 */
	constructor( editor, attributeKey ) {
		super( editor );

		/**
		 * The attribute that will be set by the command.
		 *
		 * @readonly
		 * @member {String}
		 */
		this.attributeKey = attributeKey;

		/**
		 * Flag indicating whether the command is active. The command is active when the
		 * {@link module:engine/model/selection~Selection#hasAttribute selection has the attribute} which means that:
		 *
		 * * If the selection is not empty &ndash; That the attribute is set on the first node in the selection that allows this attribute.
		 * * If the selection is empty &ndash; That the selection has the attribute itself (which means that newly typed
		 * text will have this attribute, too).
		 *
		 * @observable
		 * @readonly
		 * @member {Boolean} #value
		 */
	}

	/**
	 * Updates the command's {@link #value} and {@link #isEnabled} based on the current selection.
	 */
	refresh() {
		const model = this.editor.model;
		const doc = model.document;

		this.value = this._getValueFromFirstAllowedNode();
		this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, this.attributeKey );
	}

	/**
	 * Executes the command &mdash; applies the attribute to the selection or removes it from the selection.
	 *
	 * If the command is active (`value == true`), it will remove attributes. Otherwise, it will set attributes.
	 *
	 * The execution result differs, depending on the {@link module:engine/model/document~Document#selection}:
	 *
	 * * If the selection is on a range, the command applies the attribute to all nodes in that range
	 * (if they are allowed to have this attribute by the {@link module:engine/model/schema~Schema schema}).
	 * * If the selection is collapsed in a non-empty node, the command applies the attribute to the
	 * {@link module:engine/model/document~Document#selection} itself (note that typed characters copy attributes from the selection).
	 * * If the selection is collapsed in an empty node, the command applies the attribute to the parent node of the selection (note
	 * that the selection inherits all attributes from a node if it is in an empty node).
	 *
	 * @fires execute
	 * @param {Object} [options] Command options.
	 * @param {Boolean} [options.forceValue] If set, it will force the command behavior. If `true`, the command will apply the attribute,
	 * otherwise the command will remove the attribute.
	 * If not set, the command will look for its current value to decide what it should do.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const doc = model.document;
		const selection = doc.selection;
		const value = ( options.forceValue === undefined ) ? !this.value : options.forceValue;

		model.change( writer => {
			if ( selection.isCollapsed ) {
				if ( value ) {
					writer.setSelectionAttribute( this.attributeKey, true );
				} else {
					writer.removeSelectionAttribute( this.attributeKey );
				}
			} else {
				const ranges = model.schema.getValidRanges( selection.getRanges(), this.attributeKey );

				for ( const range of ranges ) {
					if ( value ) {
						writer.setAttribute( this.attributeKey, value, range );
					} else {
						writer.removeAttribute( this.attributeKey, range );
					}
				}
			}
		} );
	}

	/**
	 * Checks the attribute value of the first node in the selection that allows the attribute.
	 * For the collapsed selection returns the selection attribute.
	 *
	 * @private
	 * @returns {Boolean} The attribute value.
	 */
	_getValueFromFirstAllowedNode() {
		const model = this.editor.model;
		const schema = model.schema;
		const selection = model.document.selection;

		if ( selection.isCollapsed ) {
			return selection.hasAttribute( this.attributeKey );
		}

		for ( const range of selection.getRanges() ) {
			for ( const item of range.getItems() ) {
				if ( schema.checkAttribute( item, this.attributeKey ) ) {
					return item.hasAttribute( this.attributeKey );
				}
			}
		}

		return false;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/bold/boldediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/bold/boldediting
 */




const BOLD = 'bold';

/**
 * The bold editing feature.
 *
 * It registers the `'bold'` command and introduces the `bold` attribute in the model which renders to the view
 * as a `<strong>` element.
 *
 * @extends module:core/plugin~Plugin
 */
class BoldEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'BoldEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		// Allow bold attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: BOLD } );
		editor.model.schema.setAttributeProperties( BOLD, {
			isFormatting: true,
			copyOnEnter: true
		} );

		// Build converter from model to view for data and editing pipelines.
		editor.conversion.attributeToElement( {
			model: BOLD,
			view: 'strong',
			upcastAlso: [
				'b',
				viewElement => {
					const fontWeight = viewElement.getStyle( 'font-weight' );

					if ( !fontWeight ) {
						return null;
					}

					// Value of the `font-weight` attribute can be defined as a string or a number.
					if ( fontWeight == 'bold' || Number( fontWeight ) >= 600 ) {
						return {
							name: true,
							styles: [ 'font-weight' ]
						};
					}
				}
			]
		} );

		// Create bold command.
		editor.commands.add( BOLD, new AttributeCommand( editor, BOLD ) );

		// Set the Ctrl+B keystroke.
		editor.keystrokes.set( 'CTRL+B', BOLD );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/bold/boldui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/bold/boldui
 */




const boldui_BOLD = 'bold';

/**
 * The bold UI feature. It introduces the Bold button.
 *
 * @extends module:core/plugin~Plugin
 */
class BoldUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'BoldUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add bold button to feature components.
		editor.ui.componentFactory.add( boldui_BOLD, locale => {
			const command = editor.commands.get( boldui_BOLD );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Bold' ),
				icon: icons.bold,
				keystroke: 'CTRL+B',
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( boldui_BOLD );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/bold.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/bold
 */





/**
 * The bold feature.
 *
 * For a detailed overview check the {@glink features/basic-styles Basic styles feature documentation}
 * and the {@glink api/basic-styles package page}.
 *
 * This is a "glue" plugin which loads the {@link module:basic-styles/bold/boldediting~BoldEditing bold editing feature}
 * and {@link module:basic-styles/bold/boldui~BoldUI bold UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class Bold extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ BoldEditing, BoldUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Bold';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/code/codeediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/code/codeediting
 */






const CODE = 'code';
const HIGHLIGHT_CLASS = 'ck-code_selected';

/**
 * The code editing feature.
 *
 * It registers the `'code'` command and introduces the `code` attribute in the model which renders to the view
 * as a `<code>` element.
 *
 * @extends module:core/plugin~Plugin
 */
class CodeEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'CodeEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TwoStepCaretMovement ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow code attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: CODE } );
		editor.model.schema.setAttributeProperties( CODE, {
			isFormatting: true,
			copyOnEnter: false
		} );

		editor.conversion.attributeToElement( {
			model: CODE,
			view: 'code',
			upcastAlso: {
				styles: {
					'word-wrap': 'break-word'
				}
			}
		} );

		// Create code command.
		editor.commands.add( CODE, new AttributeCommand( editor, CODE ) );

		// Enable two-step caret movement for `code` attribute.
		editor.plugins.get( TwoStepCaretMovement ).registerAttribute( CODE );

		// Setup highlight over selected element.
		inlineHighlight( editor, CODE, 'code', HIGHLIGHT_CLASS );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/code.svg
/* harmony default export */ const code = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m12.5 5.7 5.2 3.9v1.3l-5.6 4c-.1.2-.3.2-.5.2-.3-.1-.6-.7-.6-1l.3-.4 4.7-3.5L11.5 7l-.2-.2c-.1-.3-.1-.6 0-.8.2-.2.5-.4.8-.4a.8.8 0 0 1 .4.1zm-5.2 0L2 9.6v1.3l5.6 4c.1.2.3.2.5.2.3-.1.7-.7.6-1 0-.1 0-.3-.2-.4l-5-3.5L8.2 7l.2-.2c.1-.3.1-.6 0-.8-.2-.2-.5-.4-.8-.4a.8.8 0 0 0-.3.1z\"/></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-basic-styles/theme/code.css
var theme_code = __webpack_require__(8180);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/theme/code.css

            

var code_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

code_options.insert = "head";
code_options.singleton = true;

var code_update = injectStylesIntoStyleTag_default()(theme_code/* default */.Z, code_options);



/* harmony default export */ const ckeditor5_basic_styles_theme_code = (theme_code/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/code/codeui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/code/codeui
 */








const codeui_CODE = 'code';

/**
 * The code UI feature. It introduces the Code button.
 *
 * @extends module:core/plugin~Plugin
 */
class CodeUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'CodeUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add code button to feature components.
		editor.ui.componentFactory.add( codeui_CODE, locale => {
			const command = editor.commands.get( codeui_CODE );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Code' ),
				icon: code,
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( codeui_CODE );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/code.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/code
 */







/**
 * The code feature.
 *
 * For a detailed overview check the {@glink features/basic-styles Basic styles feature documentation}
 * and the {@glink api/basic-styles package page}.
 *
 * This is a "glue" plugin which loads the {@link module:basic-styles/code/codeediting~CodeEditing code editing feature}
 * and {@link module:basic-styles/code/codeui~CodeUI code UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class Code extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ CodeEditing, CodeUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Code';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/schemadefinitions.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/schemadefinitions
 */

// Skipped elements due to HTML deprecation:
// * noframes (not sure if we should provide support for this element. CKE4 is not supporting frameset and frame,
//   but it will unpack <frameset><noframes>foobar</noframes></frameset> to <noframes>foobar</noframes>, so there
//   may be some content loss. Although using noframes as a standalone element seems invalid)
// * keygen (this one is also empty)
// * applet (support is limited mostly to old IE)
// * basefont (this one is also empty)
// * isindex (basically no support for modern browsers at all)
//
// Skipped elements due to lack empty element support:
// * hr
// * area
// * br
// * command
// * map
// * wbr
// * colgroup -> col
//
// Skipped elements due to complexity:
// * datalist with option elements used as a data source for input[list] element
//
// Skipped elements as they are handled as an object content:
// * track
// * source
// * option
// * param
// * optgroup
//
// Skipped full page HTML elements:
// * body
// * html
// * title
// * head
// * meta
// * link
// * etc...
//
// Skipped hidden elements:
// noscript

/* harmony default export */ const schemadefinitions = ({
	block: [
		// Existing features
		{
			model: 'codeBlock',
			view: 'pre'
		},
		{
			model: 'paragraph',
			view: 'p'
		},
		{
			model: 'blockQuote',
			view: 'blockquote'
		},
		{
			model: 'listItem',
			view: 'li'
		},
		{
			model: 'pageBreak',
			view: 'div'
		},
		{
			model: 'rawHtml',
			view: 'div'
		},
		{
			model: 'table',
			view: 'table'
		},
		{
			model: 'tableRow',
			view: 'tr'
		},
		{
			model: 'tableCell',
			view: 'td'
		},
		{
			model: 'tableCell',
			view: 'th'
		},
		{
			model: 'caption',
			view: 'caption'
		},
		{
			model: 'caption',
			view: 'figcaption'
		},
		{
			model: 'imageBlock',
			view: 'img'
		},
		{
			model: 'imageInline',
			view: 'img'
		},

		// Compatibility features
		{
			model: 'htmlP',
			view: 'p',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: 'htmlBlockquote',
			view: 'blockquote',
			modelSchema: {
				inheritAllFrom: '$container'
			}
		},
		{
			model: 'htmlTable',
			view: 'table',
			modelSchema: {
				allowWhere: '$block',
				isBlock: true
			}
		},
		{
			model: 'htmlTbody',
			view: 'tbody',
			modelSchema: {
				allowIn: 'htmlTable',
				isBlock: false
			}
		},
		{
			model: 'htmlThead',
			view: 'thead',
			modelSchema: {
				allowIn: 'htmlTable',
				isBlock: false
			}
		},
		{
			model: 'htmlTfoot',
			view: 'tfoot',
			modelSchema: {
				allowIn: 'htmlTable',
				isBlock: false
			}
		},
		{
			model: 'htmlCaption',
			view: 'caption',
			modelSchema: {
				allowIn: 'htmlTable',
				allowChildren: '$text',
				isBlock: false
			}
		},
		{
			model: 'htmlColgroup',
			view: 'colgroup',
			modelSchema: {
				allowIn: 'htmlTable',
				allowChildren: 'col',
				isBlock: false
			}
		},
		{
			model: 'htmlCol',
			view: 'col',
			modelSchema: {
				allowIn: 'htmlColgroup',
				isBlock: false
			}
		},
		{
			model: 'htmlTr',
			view: 'tr',
			modelSchema: {
				allowIn: [ 'htmlTable', 'htmlThead', 'htmlTbody' ],
				isLimit: true
			}
		},
		// TODO can also include text.
		{
			model: 'htmlTd',
			view: 'td',
			modelSchema: {
				allowIn: 'htmlTr',
				allowContentOf: '$container',
				isLimit: true,
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlTh',
			view: 'th',
			modelSchema: {
				allowIn: 'htmlTr',
				allowContentOf: '$container',
				isLimit: true,
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlFigure',
			view: 'figure',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include other block elements.
		{
			model: 'htmlFigcaption',
			view: 'figcaption',
			modelSchema: {
				allowIn: 'htmlFigure',
				allowChildren: '$text',
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlAddress',
			view: 'address',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlAside',
			view: 'aside',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlMain',
			view: 'main',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlDetails',
			view: 'details',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		{
			model: 'htmlSummary',
			view: 'summary',
			modelSchema: {
				allowChildren: '$text',
				allowIn: 'htmlDetails',
				isBlock: false
			}
		},
		{
			model: 'htmlDiv',
			view: 'div',
			paragraphLikeModel: 'htmlDivParagraph',
			modelSchema: {
				inheritAllFrom: '$container'
			}
		},
		// TODO can also include text.
		{
			model: 'htmlFieldset',
			view: 'fieldset',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include h1-h6.
		{
			model: 'htmlLegend',
			view: 'legend',
			modelSchema: {
				allowIn: 'htmlFieldset',
				allowChildren: '$text'
			}
		},
		// TODO can also include text.
		{
			model: 'htmlHeader',
			view: 'header',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlFooter',
			view: 'footer',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlForm',
			view: 'form',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: true
			}
		},
		{
			model: 'htmlHgroup',
			view: 'hgroup',
			modelSchema: {
				allowChildren: [
					'htmlH1',
					'htmlH2',
					'htmlH3',
					'htmlH4',
					'htmlH5',
					'htmlH6'
				],
				isBlock: false
			}
		},
		{
			model: 'htmlH1',
			view: 'h1',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: 'htmlH2',
			view: 'h2',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: 'htmlH3',
			view: 'h3',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: 'htmlH4',
			view: 'h4',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: 'htmlH5',
			view: 'h5',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: 'htmlH6',
			view: 'h6',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: '$htmlList',
			modelSchema: {
				allowWhere: '$container',
				allowChildren: [ '$htmlList', 'htmlLi' ],
				isBlock: false
			}
		},
		{
			model: 'htmlDir',
			view: 'dir',
			modelSchema: {
				inheritAllFrom: '$htmlList'
			}
		},
		{
			model: 'htmlMenu',
			view: 'menu',
			modelSchema: {
				inheritAllFrom: '$htmlList'
			}
		},
		{
			model: 'htmlUl',
			view: 'ul',
			modelSchema: {
				inheritAllFrom: '$htmlList'
			}
		},
		{
			model: 'htmlOl',
			view: 'ol',
			modelSchema: {
				inheritAllFrom: '$htmlList'
			}
		},
		// TODO can also include other block elements.
		{
			model: 'htmlLi',
			view: 'li',
			modelSchema: {
				allowIn: '$htmlList',
				allowChildren: '$text',
				isBlock: false
			}
		},
		{
			model: 'htmlPre',
			view: 'pre',
			modelSchema: {
				inheritAllFrom: '$block'
			}
		},
		{
			model: 'htmlArticle',
			view: 'article',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		{
			model: 'htmlSection',
			view: 'section',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		// TODO can also include text.
		{
			model: 'htmlNav',
			view: 'nav',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		},
		{
			model: 'htmlDl',
			view: 'dl',
			modelSchema: {
				allowWhere: '$container',
				allowChildren: [ 'htmlDt', 'htmlDd' ],
				isBlock: false
			}
		},
		{
			model: 'htmlDt',
			view: 'dt',
			modelSchema: {
				allowChildren: '$block',
				isBlock: false
			}
		},
		{
			model: 'htmlDd',
			view: 'dd',
			modelSchema: {
				allowChildren: '$block',
				isBlock: false
			}
		},
		{
			model: 'htmlCenter',
			view: 'center',
			modelSchema: {
				inheritAllFrom: '$container',
				isBlock: false
			}
		}
	],
	inline: [
		{
			model: 'htmlAcronym',
			view: 'acronym',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlTt',
			view: 'tt',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlFont',
			view: 'font',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlTime',
			view: 'time',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlVar',
			view: 'var',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlBig',
			view: 'big',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlSmall',
			view: 'small',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlSamp',
			view: 'samp',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlQ',
			view: 'q',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlOutput',
			view: 'output',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlKbd',
			view: 'kbd',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlBdi',
			view: 'bdi',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlBdo',
			view: 'bdo',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlAbbr',
			view: 'abbr',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlA',
			view: 'a',
			priority: 5,
			coupledAttribute: 'linkHref',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlStrong',
			view: 'strong',
			coupledAttribute: 'bold',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlB',
			view: 'b',
			coupledAttribute: 'bold',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlI',
			view: 'i',
			coupledAttribute: 'italic',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlEm',
			view: 'em',
			coupledAttribute: 'italic',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlS',
			view: 's',
			coupledAttribute: 'strikethrough',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		// TODO According to HTML-spec can behave as div-like element, although CKE4 only handles it as an inline element.
		{
			model: 'htmlDel',
			view: 'del',
			coupledAttribute: 'strikethrough',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		// TODO According to HTML-spec can behave as div-like element, although CKE4 only handles it as an inline element.
		{
			model: 'htmlIns',
			view: 'ins',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlU',
			view: 'u',
			coupledAttribute: 'underline',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlSub',
			view: 'sub',
			coupledAttribute: 'subscript',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlSup',
			view: 'sup',
			coupledAttribute: 'superscript',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlCode',
			view: 'code',
			coupledAttribute: 'code',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlMark',
			view: 'mark',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlSpan',
			view: 'span',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlCite',
			view: 'cite',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlLabel',
			view: 'label',
			attributeProperties: {
				copyOnEnter: true
			}
		},
		{
			model: 'htmlDfn',
			view: 'dfn',
			attributeProperties: {
				copyOnEnter: true
			}
		},

		// Objects
		{
			model: 'htmlObject',
			view: 'object',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlIframe',
			view: 'iframe',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlInput',
			view: 'input',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlButton',
			view: 'button',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlTextarea',
			view: 'textarea',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlSelect',
			view: 'select',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlVideo',
			view: 'video',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlEmbed',
			view: 'embed',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlOembed',
			view: 'oembed',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlAudio',
			view: 'audio',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlImg',
			view: 'img',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlCanvas',
			view: 'canvas',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		// TODO it could be probably represented as non-object element, although it has graphical representation,
		// so probably makes more sense to keep it as an object.
		{
			model: 'htmlMeter',
			view: 'meter',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		// TODO it could be probably represented as non-object element, although it has graphical representation,
		// so probably makes more sense to keep it as an object.
		{
			model: 'htmlProgress',
			view: 'progress',
			isObject: true,
			modelSchema: {
				inheritAllFrom: '$inlineObject'
			}
		},
		{
			model: 'htmlScript',
			view: 'script',
			modelSchema: {
				allowWhere: [ '$text', '$block' ],
				isInline: true
			}
		},
		{
			model: 'htmlStyle',
			view: 'style',
			modelSchema: {
				allowWhere: [ '$text', '$block' ],
				isInline: true
			}
		},
		{
			model: 'htmlCustomElement',
			view: '$customElement',
			modelSchema: {
				allowWhere: [ '$text', '$block' ],
				isInline: true
			}
		}
	]
});

;// CONCATENATED MODULE: ./node_modules/lodash-es/mergeWith.js



/**
 * This method is like `_.merge` except that it accepts `customizer` which
 * is invoked to produce the merged values of the destination and source
 * properties. If `customizer` returns `undefined`, merging is handled by the
 * method instead. The `customizer` is invoked with six arguments:
 * (objValue, srcValue, key, object, source, stack).
 *
 * **Note:** This method mutates `object`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Object
 * @param {Object} object The destination object.
 * @param {...Object} sources The source objects.
 * @param {Function} customizer The function to customize assigned values.
 * @returns {Object} Returns `object`.
 * @example
 *
 * function customizer(objValue, srcValue) {
 *   if (_.isArray(objValue)) {
 *     return objValue.concat(srcValue);
 *   }
 * }
 *
 * var object = { 'a': [1], 'b': [2] };
 * var other = { 'a': [3], 'b': [4] };
 *
 * _.mergeWith(object, other, customizer);
 * // => { 'a': [1, 3], 'b': [2, 4] }
 */
var mergeWith = _createAssigner(function(object, source, srcIndex, customizer) {
  _baseMerge(object, source, srcIndex, customizer);
});

/* harmony default export */ const lodash_es_mergeWith = (mergeWith);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/dataschema.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/dataschema
 */






/**
 * Holds representation of the extended HTML document type definitions to be used by the
 * editor in HTML support.
 *
 * Data schema is represented by data schema definitions.
 *
 * To add new definition for block element,
 * use {@link module:html-support/dataschema~DataSchema#registerBlockElement} method:
 *
 *		dataSchema.registerBlockElement( {
 *			view: 'section',
 *			model: 'my-section',
 *			modelSchema: {
 *				inheritAllFrom: '$block'
 *			}
 *		} );
 *
 * To add new definition for inline element,
 * use {@link module:html-support/dataschema~DataSchema#registerInlineElement} method:
 *
 *		dataSchema.registerInlineElement( {
 *			view: 'span',
 *			model: 'my-span',
 *			attributeProperties: {
 *				copyOnEnter: true
 *			}
 *		} );
 *
 * @extends module:core/plugin~Plugin
 */
class DataSchema extends plugin_Plugin {
	constructor( editor ) {
		super( editor );

		/**
		 * A map of registered data schema definitions.
		 *
		 * @readonly
		 * @private
		 * @member {Map.<String, module:html-support/dataschema~DataSchemaDefinition>} #_definitions
		 */
		this._definitions = new Map();
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DataSchema';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		for ( const definition of schemadefinitions.block ) {
			this.registerBlockElement( definition );
		}

		for ( const definition of schemadefinitions.inline ) {
			this.registerInlineElement( definition );
		}
	}

	/**
	 * Add new data schema definition describing block element.
	 *
	 * @param {module:html-support/dataschema~DataSchemaBlockElementDefinition} definition
	 */
	registerBlockElement( definition ) {
		this._definitions.set( definition.model, { ...definition, isBlock: true } );
	}

	/**
	 * Add new data schema definition describing inline element.
	 *
	 * @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition
	 */
	registerInlineElement( definition ) {
		this._definitions.set( definition.model, { ...definition, isInline: true } );
	}

	/**
	 * Updates schema definition describing block element with new properties.
	 *
	 * Creates new scheme if it doesn't exist.
	 * Array properties are concatenated with original values.
	 *
	 * @param {module:html-support/dataschema~DataSchemaBlockElementDefinition} definition Definition update.
	 */
	extendBlockElement( definition ) {
		this._extendDefinition( { ...definition, isBlock: true } );
	}

	/**
	 * Updates schema definition describing inline element with new properties.
	 *
	 * Creates new scheme if it doesn't exist.
	 * Array properties are concatenated with original values.
	 *
	 * @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition Definition update.
	 */
	extendInlineElement( definition ) {
		this._extendDefinition( { ...definition, isInline: true } );
	}

	/**
	 * Returns all definitions matching the given view name.
	 *
	 * @param {String|RegExp} viewName
	 * @param {Boolean} [includeReferences] Indicates if this method should also include definitions of referenced models.
	 * @returns {Set.<module:html-support/dataschema~DataSchemaDefinition>}
	 */
	getDefinitionsForView( viewName, includeReferences ) {
		const definitions = new Set();

		for ( const definition of this._getMatchingViewDefinitions( viewName ) ) {
			if ( includeReferences ) {
				for ( const reference of this._getReferences( definition.model ) ) {
					definitions.add( reference );
				}
			}

			definitions.add( definition );
		}

		return definitions;
	}

	/**
	 * Returns definitions matching the given view name.
	 *
	 * @private
	 * @param {String|RegExp} viewName
	 * @returns {Array.<module:html-support/dataschema~DataSchemaDefinition>}
	 */
	_getMatchingViewDefinitions( viewName ) {
		return Array.from( this._definitions.values() )
			.filter( def => def.view && testViewName( viewName, def.view ) );
	}

	/**
	 * Resolves all definition references registered for the given data schema definition.
	 *
	 * @private
	 * @param {String} modelName Data schema model name.
	 * @returns {Iterable.<module:html-support/dataschema~DataSchemaDefinition>}
	 */
	* _getReferences( modelName ) {
		const { modelSchema } = this._definitions.get( modelName );

		if ( !modelSchema ) {
			return;
		}

		const inheritProperties = [ 'inheritAllFrom', 'inheritTypesFrom', 'allowWhere', 'allowContentOf', 'allowAttributesOf' ];

		for ( const property of inheritProperties ) {
			for ( const referenceName of toArray( modelSchema[ property ] || [] ) ) {
				const definition = this._definitions.get( referenceName );

				if ( referenceName !== modelName && definition ) {
					yield* this._getReferences( definition.model );
					yield definition;
				}
			}
		}
	}

	/**
	 * Updates schema definition with new properties.
	 *
	 * Creates new scheme if it doesn't exist.
	 * Array properties are concatenated with original values.
	 *
	 * @private
	 * @param {module:html-support/dataschema~DataSchemaDefinition} definition Definition update.
	 */
	_extendDefinition( definition ) {
		const currentDefinition = this._definitions.get( definition.model );

		const mergedDefinition = lodash_es_mergeWith( {}, currentDefinition, definition, ( target, source ) => {
			return Array.isArray( target ) ? target.concat( source ) : undefined;
		} );

		this._definitions.set( definition.model, mergedDefinition );
	}
}

// Test view name against the given pattern.
//
// @private
// @param {String|RegExp} pattern
// @param {String} viewName
// @returns {Boolean}
function testViewName( pattern, viewName ) {
	if ( typeof pattern === 'string' ) {
		return pattern === viewName;
	}

	if ( pattern instanceof RegExp ) {
		return pattern.test( viewName );
	}

	return false;
}

/**
 * A base definition of {@link module:html-support/dataschema~DataSchema data schema}.
 *
 * @typedef {Object} module:html-support/dataschema~DataSchemaDefinition
 * @property {String} model Name of the model.
 * @property {String} [view] Name of the view element.
 * @property {Boolean} [isObject] Indicates that the definition describes object element.
 * @property {module:engine/model/schema~SchemaItemDefinition} [modelSchema] The model schema item definition describing registered model.
 */

/**
 * A definition of {@link module:html-support/dataschema~DataSchema data schema} for block elements.
 *
 * @typedef {Object} module:html-support/dataschema~DataSchemaBlockElementDefinition
 * @property {Boolean} isBlock Indicates that the definition describes block element.
 * Set by {@link module:html-support/dataschema~DataSchema#registerBlockElement} method.
 * @property {String} [paragraphLikeModel] Should be used when an element can behave both as a sectioning element (e.g. article) and
 * element accepting only inline content (e.g. paragraph).
 * If an element contains only inline content, this option will be used as a model
 * name.
 * @extends module:html-support/dataschema~DataSchemaDefinition
 */

/**
 * A definition of {@link module:html-support/dataschema~DataSchema data schema} for inline elements.
 *
 * @typedef {Object} module:html-support/dataschema~DataSchemaInlineElementDefinition
 * @property {module:engine/model/schema~AttributeProperties} [attributeProperties] Additional metadata describing the model attribute.
 * @property {Boolean} isInline Indicates that the definition describes inline element.
 * @property {Number} [priority] Element priority. Decides in what order elements are wrapped by
 * {@link module:engine/view/downcastwriter~DowncastWriter}.
 * Set by {@link module:html-support/dataschema~DataSchema#registerInlineElement} method.
 * @property {String} [coupledAttribute] The name of the model attribute that generates the same view element. GHS inline attribute
 * will be removed from the model tree as soon as the coupled attribute is removed. See
 * {@link module:html-support/datafilter~DataFilter#_registerModelPostFixer GHS post-fixer} for more details.
 * @extends module:html-support/dataschema~DataSchemaDefinition
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/conversionutils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/conversionutils
 */



/**
* Helper function for the downcast converter. Updates attributes on the given view element.
*
* @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer.
* @param {Object} oldViewAttributes The previous GHS attribute value.
* @param {Object} newViewAttributes The current GHS attribute value.
* @param {module:engine/view/element~Element} viewElement The view element to update.
*/
function updateViewAttributes( writer, oldViewAttributes, newViewAttributes, viewElement ) {
	if ( oldViewAttributes ) {
		removeViewAttributes( writer, oldViewAttributes, viewElement );
	}

	if ( newViewAttributes ) {
		setViewAttributes( writer, newViewAttributes, viewElement );
	}
}

/**
 * Helper function for the downcast converter. Sets attributes on the given view element.
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer.
 * @param {Object} viewAttributes The GHS attribute value.
 * @param {module:engine/view/element~Element} viewElement The view element to update.
 */
function setViewAttributes( writer, viewAttributes, viewElement ) {
	if ( viewAttributes.attributes ) {
		for ( const [ key, value ] of Object.entries( viewAttributes.attributes ) ) {
			writer.setAttribute( key, value, viewElement );
		}
	}

	if ( viewAttributes.styles ) {
		writer.setStyle( viewAttributes.styles, viewElement );
	}

	if ( viewAttributes.classes ) {
		writer.addClass( viewAttributes.classes, viewElement );
	}
}

/**
 * Helper function for the downcast converter. Removes attributes on the given view element.
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer.
 * @param {Object} viewAttributes The GHS attribute value.
 * @param {module:engine/view/element~Element} viewElement The view element to update.
 */
function removeViewAttributes( writer, viewAttributes, viewElement ) {
	if ( viewAttributes.attributes ) {
		for ( const [ key ] of Object.entries( viewAttributes.attributes ) ) {
			writer.removeAttribute( key, viewElement );
		}
	}

	if ( viewAttributes.styles ) {
		for ( const style of Object.keys( viewAttributes.styles ) ) {
			writer.removeStyle( style, viewElement );
		}
	}

	if ( viewAttributes.classes ) {
		writer.removeClass( viewAttributes.classes, viewElement );
	}
}

/**
* Merges view element attribute objects.
*
* @param {Object} target
* @param {Object} source
* @returns {Object}
*/
function mergeViewElementAttributes( target, source ) {
	const result = lodash_es_cloneDeep( target );

	for ( const key in source ) {
		// Merge classes.
		if ( Array.isArray( source[ key ] ) ) {
			result[ key ] = Array.from( new Set( [ ...( target[ key ] || [] ), ...source[ key ] ] ) );
		}

		// Merge attributes or styles.
		else {
			result[ key ] = { ...target[ key ], ...source[ key ] };
		}
	}

	return result;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/converters
 */




/**
 * View-to-model conversion helper for object elements.
 *
 * Preserves object element content in `htmlContent` attribute.
 *
 * @param {module:html-support/dataschema~DataSchemaDefinition} definition
 * @returns {Function} Returns a conversion callback.
*/
function viewToModelObjectConverter( { model: modelName } ) {
	return ( viewElement, conversionApi ) => {
		// Let's keep element HTML and its attributes, so we can rebuild element in downcast conversions.
		return conversionApi.writer.createElement( modelName, {
			htmlContent: viewElement.getCustomProperty( '$rawContent' )
		} );
	};
}

/**
 * Conversion helper converting an object element to an HTML object widget.
 *
 * @param {module:core/editor/editor~Editor} editor
 * @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition
 * @returns {Function} Returns a conversion callback.
*/
function toObjectWidgetConverter( editor, { view: viewName, isInline } ) {
	const t = editor.t;

	return ( modelElement, { writer } ) => {
		const widgetLabel = t( 'HTML object' );

		const viewElement = createObjectView( viewName, modelElement, writer );
		const viewAttributes = modelElement.getAttribute( 'htmlAttributes' );

		writer.addClass( 'html-object-embed__content', viewElement );

		if ( viewAttributes ) {
			setViewAttributes( writer, viewAttributes, viewElement );
		}

		// Widget cannot be a raw element because the widget system would not be able
		// to add its UI to it. Thus, we need separate view container.
		const viewContainer = writer.createContainerElement( isInline ? 'span' : 'div',
			{
				class: 'html-object-embed',
				'data-html-object-embed-label': widgetLabel
			},
			viewElement
		);

		return toWidget( viewContainer, writer, { widgetLabel } );
	};
}

/**
* Creates object view element from the given model element.
*
* @param {String} viewName
* @param {module:engine/model/element~Element} modelElement
* @param {module:engine/view/downcastwriter~DowncastWriter} writer
* @returns {module:engine/view/element~Element}
*/
function createObjectView( viewName, modelElement, writer ) {
	return writer.createRawElement( viewName, null, ( domElement, domConverter ) => {
		domConverter.setContentOf( domElement, modelElement.getAttribute( 'htmlContent' ) );
	} );
}

/**
 * View-to-attribute conversion helper preserving inline element attributes on `$text`.
 *
 * @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition
 * @param {module:html-support/datafilter~DataFilter} dataFilter
 * @returns {Function} Returns a conversion callback.
*/
function viewToAttributeInlineConverter( { view: viewName, model: attributeKey }, dataFilter ) {
	return dispatcher => {
		dispatcher.on( `element:${ viewName }`, ( evt, data, conversionApi ) => {
			let viewAttributes = dataFilter.processViewAttributes( data.viewItem, conversionApi );

			// Do not apply the attribute if the element itself is already consumed and there are no view attributes to store.
			if ( !viewAttributes && !conversionApi.consumable.test( data.viewItem, { name: true } ) ) {
				return;
			}

			// Otherwise, we might need to convert it to an empty object just to preserve element itself,
			// for example `<cite>` => <$text htmlCite="{}">.
			viewAttributes = viewAttributes || {};

			// Consume the element itself if it wasn't consumed by any other converter.
			conversionApi.consumable.consume( data.viewItem, { name: true } );

			// Since we are converting to attribute we need a range on which we will set the attribute.
			// If the range is not created yet, we will create it.
			if ( !data.modelRange ) {
				data = Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
			}

			// Set attribute on each item in range according to the schema.
			for ( const node of data.modelRange.getItems() ) {
				if ( conversionApi.schema.checkAttribute( node, attributeKey ) ) {
					// Node's children are converted recursively, so node can already include model attribute.
					// We want to extend it, not replace.
					const nodeAttributes = node.getAttribute( attributeKey );
					const attributesToAdd = mergeViewElementAttributes( viewAttributes, nodeAttributes || {} );

					conversionApi.writer.setAttribute( attributeKey, attributesToAdd, node );
				}
			}
		}, { priority: 'low' } );
	};
}

/**
 * Attribute-to-view conversion helper applying attributes to view element preserved on `$text`.
 *
 * @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition
 * @returns {Function} Returns a conversion callback.
*/
function attributeToViewInlineConverter( { priority, view: viewName } ) {
	return ( attributeValue, conversionApi ) => {
		if ( !attributeValue ) {
			return;
		}

		const { writer } = conversionApi;
		const viewElement = writer.createAttributeElement( viewName, null, { priority } );

		setViewAttributes( writer, attributeValue, viewElement );

		return viewElement;
	};
}

/**
 * View-to-model conversion helper preserving allowed attributes on block element.
 *
 * All matched attributes will be preserved on `htmlAttributes` attribute.
 *
 * @param {module:html-support/dataschema~DataSchemaBlockElementDefinition} definition
 * @param {module:html-support/datafilter~DataFilter} dataFilter
 * @returns {Function} Returns a conversion callback.
*/
function viewToModelBlockAttributeConverter( { view: viewName }, dataFilter ) {
	return dispatcher => {
		dispatcher.on( `element:${ viewName }`, ( evt, data, conversionApi ) => {
			// Converting an attribute of an element that has not been converted to anything does not make sense
			// because there will be nowhere to set that attribute on. At this stage, the element should've already
			// been converted. A collapsed range can show up in to-do lists (<input>) or complex widgets (e.g. table).
			// (https://github.com/ckeditor/ckeditor5/issues/11000).
			if ( !data.modelRange || data.modelRange.isCollapsed ) {
				return;
			}

			const viewAttributes = dataFilter.processViewAttributes( data.viewItem, conversionApi );

			if ( viewAttributes ) {
				conversionApi.writer.setAttribute( 'htmlAttributes', viewAttributes, data.modelRange );
			}
		}, { priority: 'low' } );
	};
}

/**
 * Model-to-view conversion helper applying attributes preserved in `htmlAttributes` attribute
 * for block elements.
 *
 * @param {module:html-support/dataschema~DataSchemaBlockElementDefinition} definition
 * @returns {Function} Returns a conversion callback.
*/
function modelToViewBlockAttributeConverter( { model: modelName } ) {
	return dispatcher => {
		dispatcher.on( `attribute:htmlAttributes:${ modelName }`, ( evt, data, conversionApi ) => {
			if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
				return;
			}

			const { attributeOldValue, attributeNewValue } = data;
			const viewWriter = conversionApi.writer;
			const viewElement = conversionApi.mapper.toViewElement( data.item );

			updateViewAttributes( viewWriter, attributeOldValue, attributeNewValue, viewElement );
		} );
	};
}

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseFindIndex.js
/**
 * The base implementation of `_.findIndex` and `_.findLastIndex` without
 * support for iteratee shorthands.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {Function} predicate The function invoked per iteration.
 * @param {number} fromIndex The index to search from.
 * @param {boolean} [fromRight] Specify iterating from right to left.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseFindIndex(array, predicate, fromIndex, fromRight) {
  var length = array.length,
      index = fromIndex + (fromRight ? 1 : -1);

  while ((fromRight ? index-- : ++index < length)) {
    if (predicate(array[index], index, array)) {
      return index;
    }
  }
  return -1;
}

/* harmony default export */ const _baseFindIndex = (baseFindIndex);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIsNaN.js
/**
 * The base implementation of `_.isNaN` without support for number objects.
 *
 * @private
 * @param {*} value The value to check.
 * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
 */
function baseIsNaN(value) {
  return value !== value;
}

/* harmony default export */ const _baseIsNaN = (baseIsNaN);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_strictIndexOf.js
/**
 * A specialized version of `_.indexOf` which performs strict equality
 * comparisons of values, i.e. `===`.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function strictIndexOf(array, value, fromIndex) {
  var index = fromIndex - 1,
      length = array.length;

  while (++index < length) {
    if (array[index] === value) {
      return index;
    }
  }
  return -1;
}

/* harmony default export */ const _strictIndexOf = (strictIndexOf);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIndexOf.js




/**
 * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseIndexOf(array, value, fromIndex) {
  return value === value
    ? _strictIndexOf(array, value, fromIndex)
    : _baseFindIndex(array, _baseIsNaN, fromIndex);
}

/* harmony default export */ const _baseIndexOf = (baseIndexOf);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_baseIndexOfWith.js
/**
 * This function is like `baseIndexOf` except that it accepts a comparator.
 *
 * @private
 * @param {Array} array The array to inspect.
 * @param {*} value The value to search for.
 * @param {number} fromIndex The index to search from.
 * @param {Function} comparator The comparator invoked per element.
 * @returns {number} Returns the index of the matched value, else `-1`.
 */
function baseIndexOfWith(array, value, fromIndex, comparator) {
  var index = fromIndex - 1,
      length = array.length;

  while (++index < length) {
    if (comparator(array[index], value)) {
      return index;
    }
  }
  return -1;
}

/* harmony default export */ const _baseIndexOfWith = (baseIndexOfWith);

;// CONCATENATED MODULE: ./node_modules/lodash-es/_basePullAll.js






/** Used for built-in method references. */
var _basePullAll_arrayProto = Array.prototype;

/** Built-in value references. */
var _basePullAll_splice = _basePullAll_arrayProto.splice;

/**
 * The base implementation of `_.pullAllBy` without support for iteratee
 * shorthands.
 *
 * @private
 * @param {Array} array The array to modify.
 * @param {Array} values The values to remove.
 * @param {Function} [iteratee] The iteratee invoked per element.
 * @param {Function} [comparator] The comparator invoked per element.
 * @returns {Array} Returns `array`.
 */
function basePullAll(array, values, iteratee, comparator) {
  var indexOf = comparator ? _baseIndexOfWith : _baseIndexOf,
      index = -1,
      length = values.length,
      seen = array;

  if (array === values) {
    values = _copyArray(values);
  }
  if (iteratee) {
    seen = _arrayMap(array, _baseUnary(iteratee));
  }
  while (++index < length) {
    var fromIndex = 0,
        value = values[index],
        computed = iteratee ? iteratee(value) : value;

    while ((fromIndex = indexOf(seen, computed, fromIndex, comparator)) > -1) {
      if (seen !== array) {
        _basePullAll_splice.call(seen, fromIndex, 1);
      }
      _basePullAll_splice.call(array, fromIndex, 1);
    }
  }
  return array;
}

/* harmony default export */ const _basePullAll = (basePullAll);

;// CONCATENATED MODULE: ./node_modules/lodash-es/pullAll.js


/**
 * This method is like `_.pull` except that it accepts an array of values to remove.
 *
 * **Note:** Unlike `_.difference`, this method mutates `array`.
 *
 * @static
 * @memberOf _
 * @since 4.0.0
 * @category Array
 * @param {Array} array The array to modify.
 * @param {Array} values The values to remove.
 * @returns {Array} Returns `array`.
 * @example
 *
 * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
 *
 * _.pullAll(array, ['a', 'c']);
 * console.log(array);
 * // => ['b', 'b']
 */
function pullAll(array, values) {
  return (array && array.length && values && values.length)
    ? _basePullAll(array, values)
    : array;
}

/* harmony default export */ const lodash_es_pullAll = (pullAll);

;// CONCATENATED MODULE: ./node_modules/lodash-es/pull.js



/**
 * Removes all given values from `array` using
 * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
 * for equality comparisons.
 *
 * **Note:** Unlike `_.without`, this method mutates `array`. Use `_.remove`
 * to remove elements from an array by predicate.
 *
 * @static
 * @memberOf _
 * @since 2.0.0
 * @category Array
 * @param {Array} array The array to modify.
 * @param {...*} [values] The values to remove.
 * @returns {Array} Returns `array`.
 * @example
 *
 * var array = ['a', 'b', 'c', 'a', 'b', 'c'];
 *
 * _.pull(array, 'a', 'c');
 * console.log(array);
 * // => ['b', 'b']
 */
var pull = _baseRest(lodash_es_pullAll);

/* harmony default export */ const lodash_es_pull = (pull);

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-html-support/theme/datafilter.css
var datafilter = __webpack_require__(8468);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/theme/datafilter.css

            

var datafilter_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

datafilter_options.insert = "head";
datafilter_options.singleton = true;

var datafilter_update = injectStylesIntoStyleTag_default()(datafilter/* default */.Z, datafilter_options);



/* harmony default export */ const theme_datafilter = (datafilter/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/datafilter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/datafilter
 */

/* globals document */












/**
 * Allows to validate elements and element attributes registered by {@link module:html-support/dataschema~DataSchema}.
 *
 * To enable registered element in the editor, use {@link module:html-support/datafilter~DataFilter#allowElement} method:
 *
 *		dataFilter.allowElement( 'section' );
 *
 * You can also allow or disallow specific element attributes:
 *
 *		// Allow `data-foo` attribute on `section` element.
 *		dataFilter.allowAttributes( {
 *			name: 'section',
 *			attributes: {
 *				'data-foo': true
 *			}
 *		} );
 *
 *		// Disallow `color` style attribute on 'section' element.
 *		dataFilter.disallowAttributes( {
 *			name: 'section',
 *			styles: {
 *				color: /[\s\S]+/
 *			}
 *		} );
 *
 * To apply the information about allowed and disallowed attributes in custom integration plugin,
 * use the {@link module:html-support/datafilter~DataFilter#processViewAttributes `processViewAttributes()`} method.
 *
 * @extends module:core/plugin~Plugin
 */
class DataFilter extends plugin_Plugin {
	constructor( editor ) {
		super( editor );

		/**
		 * An instance of the {@link module:html-support/dataschema~DataSchema}.
		 *
		 * @readonly
		 * @private
		 * @member {module:html-support/dataschema~DataSchema} #_dataSchema
		 */
		this._dataSchema = editor.plugins.get( 'DataSchema' );

		/**
		 * {@link module:engine/view/matcher~Matcher Matcher} instance describing rules upon which
		 * content attributes should be allowed.
		 *
		 * @readonly
		 * @private
		 * @member {module:engine/view/matcher~Matcher} #_allowedAttributes
		 */
		this._allowedAttributes = new Matcher();

		/**
		 * {@link module:engine/view/matcher~Matcher Matcher} instance describing rules upon which
		 * content attributes should be disallowed.
		 *
		 * @readonly
		 * @private
		 * @member {module:engine/view/matcher~Matcher} #_disallowedAttributes
		 */
		this._disallowedAttributes = new Matcher();

		/**
		 * Allowed element definitions by {@link module:html-support/datafilter~DataFilter#allowElement} method.
		 *
		 * @readonly
		 * @private
		 * @member {Set.<module:html-support/dataschema~DataSchemaDefinition>} #_allowedElements
		*/
		this._allowedElements = new Set();

		/**
		 * Disallowed element names by {@link module:html-support/datafilter~DataFilter#disallowElement} method.
		 *
		 * @readonly
		 * @private
		 * @member {Set.<String>} #_disallowedElements
		 */
		this._disallowedElements = new Set();

		/**
		 * Indicates if {@link module:engine/controller/datacontroller~DataController editor's data controller}
		 * data has been already initialized.
		 *
		 * @private
		 * @member {Boolean} [#_dataInitialized=false]
		*/
		this._dataInitialized = false;

		/**
		 * Cached map of coupled attributes. Keys are the feature attributes names
		 * and values are arrays with coupled GHS attributes names.
		 *
		 * @private
		 * @member {Map.<String,Array>}
		 */
		this._coupledAttributes = null;

		this._registerElementsAfterInit();
		this._registerElementHandlers();
		this._registerModelPostFixer();
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DataFilter';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataSchema, Widget ];
	}

	/**
	 * Load a configuration of one or many elements, where their attributes should be allowed.
	 *
	 * **Note**: Rules will be applied just before next data pipeline data init or set.
	 *
	 * @param {Array.<module:engine/view/matcher~MatcherPattern>} config Configuration of elements
	 * that should have their attributes accepted in the editor.
	 */
	loadAllowedConfig( config ) {
		for ( const pattern of config ) {
			// MatcherPattern allows omitting `name` to widen the search of elements.
			// Let's keep it consistent and match every element if a `name` has not been provided.
			const elementName = pattern.name || /[\s\S]+/;
			const rules = splitRules( pattern );

			this.allowElement( elementName );

			rules.forEach( pattern => this.allowAttributes( pattern ) );
		}
	}

	/**
	 * Load a configuration of one or many elements, where their attributes should be disallowed.
	 *
	 * **Note**: Rules will be applied just before next data pipeline data init or set.
	 *
	 * @param {Array.<module:engine/view/matcher~MatcherPattern>} config Configuration of elements
	 * that should have their attributes rejected from the editor.
	 */
	loadDisallowedConfig( config ) {
		for ( const pattern of config ) {
			// MatcherPattern allows omitting `name` to widen the search of elements.
			// Let's keep it consistent and match every element if a `name` has not been provided.
			const elementName = pattern.name || /[\s\S]+/;
			const rules = splitRules( pattern );

			// Disallow element itself if there is no other rules.
			if ( rules.length == 0 ) {
				this.disallowElement( elementName );
			} else {
				rules.forEach( pattern => this.disallowAttributes( pattern ) );
			}
		}
	}

	/**
	 * Allow the given element in the editor context.
	 *
	 * This method will only allow elements described by the {@link module:html-support/dataschema~DataSchema} used
	 * to create data filter.
	 *
	 * **Note**: Rules will be applied just before next data pipeline data init or set.
	 *
	 * @param {String|RegExp} viewName String or regular expression matching view name.
	 */
	allowElement( viewName ) {
		for ( const definition of this._dataSchema.getDefinitionsForView( viewName, true ) ) {
			if ( this._allowedElements.has( definition ) ) {
				continue;
			}

			this._allowedElements.add( definition );

			// We need to wait for all features to be initialized before we can register
			// element, so we can access existing features model schemas.
			// If the data has not been initialized yet, _registerElementsAfterInit() method will take care of
			// registering elements.
			if ( this._dataInitialized ) {
				// Defer registration to the next data pipeline data set so any disallow rules could be applied
				// even if added after allow rule (disallowElement).
				this.editor.data.once( 'set', () => {
					this._fireRegisterEvent( definition );
				}, {
					// With the highest priority listener we are able to register elements right before
					// running data conversion.
					priority: src_priorities.get( 'highest' ) + 1
				} );
			}

			// Reset cached map to recalculate it on the next usage.
			this._coupledAttributes = null;
		}
	}

	/**
	 * Disallow the given element in the editor context.
	 *
	 * This method will only disallow elements described by the {@link module:html-support/dataschema~DataSchema} used
	 * to create data filter.
	 *
	 * @param {String|RegExp} viewName String or regular expression matching view name.
	 */
	disallowElement( viewName ) {
		for ( const definition of this._dataSchema.getDefinitionsForView( viewName, false ) ) {
			this._disallowedElements.add( definition.view );
		}
	}

	/**
	 * Allow the given attributes for view element allowed by {@link #allowElement} method.
	 *
	 * @param {module:engine/view/matcher~MatcherPattern} config Pattern matching all attributes which should be allowed.
	 */
	allowAttributes( config ) {
		this._allowedAttributes.add( config );
	}

	/**
	 * Disallow the given attributes for view element allowed by {@link #allowElement} method.
	 *
	 * @param {module:engine/view/matcher~MatcherPattern} config Pattern matching all attributes which should be disallowed.
	 */
	disallowAttributes( config ) {
		this._disallowedAttributes.add( config );
	}

	/**
	 * Processes all allowed and disallowed attributes on the view element by consuming them and returning the allowed ones.
	 *
	 * This method applies the configuration set up by {@link #allowAttributes `allowAttributes()`}
	 * and {@link #disallowAttributes `disallowAttributes()`} over the given view element by consuming relevant attributes.
	 * It returns the allowed attributes that were found on the given view element for further processing by integration code.
	 *
	 *		dispatcher.on( 'element:myElement', ( evt, data, conversionApi ) => {
	 *			// Get rid of disallowed and extract all allowed attributes from a viewElement.
	 *			const viewAttributes = dataFilter.processViewAttributes( data.viewItem, conversionApi );
	 *			// Do something with them, i.e. store inside a model as a dictionary.
	 *			if ( viewAttributes ) {
	 *				conversionApi.writer.setAttribute( 'htmlAttributesOfMyElement', viewAttributes, data.modelRange );
	 *			}
	 *		} );
	 *
	 * @see module:engine/conversion/viewconsumable~ViewConsumable#consume
	 * @param {module:engine/view/element~Element} viewElement
	 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi
	 * @returns {Object} [result]
	 * @returns {Object} result.attributes Set with matched attribute names.
	 * @returns {Object} result.styles Set with matched style names.
	 * @returns {Array.<String>} result.classes Set with matched class names.
	 */
	processViewAttributes( viewElement, conversionApi ) {
		// Make sure that the disabled attributes are handled before the allowed attributes are called.
		// For example, for block images the <figure> converter triggers conversion for <img> first and then for other elements, i.e. <a>.
		consumeAttributes( viewElement, conversionApi, this._disallowedAttributes );

		return consumeAttributes( viewElement, conversionApi, this._allowedAttributes );
	}

	/**
	 * Registers elements allowed by {@link module:html-support/datafilter~DataFilter#allowElement} method
	 * once {@link module:engine/controller/datacontroller~DataController editor's data controller} is initialized.
	 *
	 * @private
	*/
	_registerElementsAfterInit() {
		this.editor.data.on( 'init', () => {
			this._dataInitialized = true;

			for ( const definition of this._allowedElements ) {
				this._fireRegisterEvent( definition );
			}
		}, {
			// With highest priority listener we are able to register elements right before
			// running data conversion. Also:
			// * Make sure that priority is higher than the one used by `RealTimeCollaborationClient`,
			// as RTC is stopping event propagation.
			// * Make sure no other features hook into this event before GHS because otherwise the
			// downcast conversion (for these features) could run before GHS registered its converters
			// (https://github.com/ckeditor/ckeditor5/issues/11356).
			priority: src_priorities.get( 'highest' ) + 1
		} );
	}

	/**
	 * Registers default element handlers.
	 *
	 * @private
	 */
	_registerElementHandlers() {
		this.on( 'register', ( evt, definition ) => {
			const schema = this.editor.model.schema;

			// Object element should be only registered for new features.
			// If the model schema is already registered, it should be handled by
			// #_registerBlockElement() or #_registerObjectElement() attribute handlers.
			if ( definition.isObject && !schema.isRegistered( definition.model ) ) {
				this._registerObjectElement( definition );
			} else if ( definition.isBlock ) {
				this._registerBlockElement( definition );
			} else if ( definition.isInline ) {
				this._registerInlineElement( definition );
			} else {
				/**
				 * The definition cannot be handled by the data filter.
				 *
				 * Make sure that the registered definition is correct.
				 *
				 * @error data-filter-invalid-definition
				 */
				throw new CKEditorError(
					'data-filter-invalid-definition',
					null,
					definition
				);
			}

			evt.stop();
		}, { priority: 'lowest' } );
	}

	/**
	 * Registers a model post-fixer that is removing coupled GHS attributes of inline elements. Those attributes
	 * are removed if a coupled feature attribute is removed.
	 *
	 * For example, consider following HTML:
	 *
	 *		<a href="foo.html" id="myId">bar</a>
	 *
	 * Which would be upcasted to following text node in the model:
	 *
	 *		<$text linkHref="foo.html" htmlA="{ attributes: { id: 'myId' } }">bar</$text>
	 *
	 * When the user removes the link from that text (using UI), only `linkHref` attribute would be removed:
	 *
	 *		<$text htmlA="{ attributes: { id: 'myId' } }">bar</$text>
	 *
	 * The `htmlA` attribute would stay in the model and would cause GHS to generate an `<a>` element.
	 * This is incorrect from UX point of view, as the user wanted to remove the whole link (not only `href`).
	 *
	 * @private
	 */
	_registerModelPostFixer() {
		const model = this.editor.model;

		model.document.registerPostFixer( writer => {
			const changes = model.document.differ.getChanges();
			let changed = false;

			const coupledAttributes = this._getCoupledAttributesMap();

			for ( const change of changes ) {
				// Handle only attribute removals.
				if ( change.type != 'attribute' || change.attributeNewValue !== null ) {
					continue;
				}

				// Find a list of coupled GHS attributes.
				const attributeKeys = coupledAttributes.get( change.attributeKey );

				if ( !attributeKeys ) {
					continue;
				}

				// Remove the coupled GHS attributes on the same range as the feature attribute was removed.
				for ( const { item } of change.range.getWalker( { shallow: true } ) ) {
					for ( const attributeKey of attributeKeys ) {
						if ( item.hasAttribute( attributeKey ) ) {
							writer.removeAttribute( attributeKey, item );
							changed = true;
						}
					}
				}
			}

			return changed;
		} );
	}

	/**
	 * Collects the map of coupled attributes. The returned map is keyed by the feature attribute name
	 * and coupled GHS attribute names are stored in the value array .
	 *
	 * @private
	 * @returns {Map.<String,Array>}
	 */
	_getCoupledAttributesMap() {
		if ( this._coupledAttributes ) {
			return this._coupledAttributes;
		}

		this._coupledAttributes = new Map();

		for ( const definition of this._allowedElements ) {
			if ( definition.coupledAttribute && definition.model ) {
				const attributeNames = this._coupledAttributes.get( definition.coupledAttribute );

				if ( attributeNames ) {
					attributeNames.push( definition.model );
				} else {
					this._coupledAttributes.set( definition.coupledAttribute, [ definition.model ] );
				}
			}
		}
	}

	/**
	 * Fires `register` event for the given element definition.
	 *
	 * @private
	 * @param {module:html-support/dataschema~DataSchemaDefinition} definition
	 */
	_fireRegisterEvent( definition ) {
		if ( definition.view && this._disallowedElements.has( definition.view ) ) {
			return;
		}

		this.fire( definition.view ? `register:${ definition.view }` : 'register', definition );
	}

	/**
	 * Registers object element and attribute converters for the given data schema definition.
	 *
	 * @private
	 * @param {module:html-support/dataschema~DataSchemaDefinition} definition
	 */
	_registerObjectElement( definition ) {
		const editor = this.editor;
		const schema = editor.model.schema;
		const conversion = editor.conversion;
		const { view: viewName, model: modelName } = definition;

		schema.register( modelName, definition.modelSchema );

		/* istanbul ignore next: paranoid check */
		if ( !viewName ) {
			return;
		}

		schema.extend( definition.model, {
			allowAttributes: [ 'htmlAttributes', 'htmlContent' ]
		} );

		// Store element content in special `$rawContent` custom property to
		// avoid editor's data filtering mechanism.
		editor.data.registerRawContentMatcher( {
			name: viewName
		} );

		conversion.for( 'upcast' ).elementToElement( {
			view: viewName,
			model: viewToModelObjectConverter( definition ),
			// With a `low` priority, `paragraph` plugin auto-paragraphing mechanism is executed. Make sure
			// this listener is called before it. If not, some elements will be transformed into a paragraph.
			converterPriority: src_priorities.get( 'low' ) + 1
		} );
		conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, this ) );

		conversion.for( 'editingDowncast' ).elementToStructure( {
			model: {
				name: modelName,
				attributes: [
					'htmlAttributes'
				]
			},
			view: toObjectWidgetConverter( editor, definition )
		} );

		conversion.for( 'dataDowncast' ).elementToElement( {
			model: modelName,
			view: ( modelElement, { writer } ) => {
				return createObjectView( viewName, modelElement, writer );
			}
		} );
		conversion.for( 'dataDowncast' ).add( modelToViewBlockAttributeConverter( definition ) );
	}

	/**
	 * Registers block element and attribute converters for the given data schema definition.
	 *
	 * @private
	 * @param {module:html-support/dataschema~DataSchemaBlockElementDefinition} definition
	 */
	_registerBlockElement( definition ) {
		const editor = this.editor;
		const schema = editor.model.schema;
		const conversion = editor.conversion;
		const { view: viewName, model: modelName } = definition;

		if ( !schema.isRegistered( definition.model ) ) {
			schema.register( definition.model, definition.modelSchema );

			if ( !viewName ) {
				return;
			}

			conversion.for( 'upcast' ).elementToElement( {
				model: modelName,
				view: viewName,
				// With a `low` priority, `paragraph` plugin auto-paragraphing mechanism is executed. Make sure
				// this listener is called before it. If not, some elements will be transformed into a paragraph.
				converterPriority: src_priorities.get( 'low' ) + 1
			} );

			conversion.for( 'downcast' ).elementToElement( {
				model: modelName,
				view: viewName
			} );
		}

		if ( !viewName ) {
			return;
		}

		schema.extend( definition.model, {
			allowAttributes: 'htmlAttributes'
		} );

		conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, this ) );
		conversion.for( 'downcast' ).add( modelToViewBlockAttributeConverter( definition ) );
	}

	/**
	 * Registers inline element and attribute converters for the given data schema definition.
	 *
	 * Extends `$text` model schema to allow the given definition model attribute and its properties.
	 *
	 * @private
	 * @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition
	 */
	_registerInlineElement( definition ) {
		const editor = this.editor;
		const schema = editor.model.schema;
		const conversion = editor.conversion;
		const attributeKey = definition.model;

		schema.extend( '$text', {
			allowAttributes: attributeKey
		} );

		if ( definition.attributeProperties ) {
			schema.setAttributeProperties( attributeKey, definition.attributeProperties );
		}

		conversion.for( 'upcast' ).add( viewToAttributeInlineConverter( definition, this ) );

		conversion.for( 'downcast' ).attributeToElement( {
			model: attributeKey,
			view: attributeToViewInlineConverter( definition )
		} );
	}

	/**
	 * Fired when {@link module:html-support/datafilter~DataFilter} is registering element and attribute
	 * converters for the {@link module:html-support/dataschema~DataSchemaDefinition element definition}.
	 *
	 * The event also accepts {@link module:html-support/dataschema~DataSchemaDefinition#view} value
	 * as an event namespace, e.g. `register:span`.
	 *
	 * 		dataFilter.on( 'register', ( evt, definition ) => {
	 * 			editor.model.schema.register( definition.model, definition.modelSchema );
	 * 			editor.conversion.elementToElement( { model: definition.model, view: definition.view } );
	 *
	 * 			evt.stop();
	 * 		} );
	 *
	 * 		dataFilter.on( 'register:span', ( evt, definition ) => {
	 * 			editor.model.schema.extend( '$text', { allowAttributes: 'htmlSpan' } );
	 *
	 * 			editor.conversion.for( 'upcast' ).elementToAttribute( { view: 'span', model: 'htmlSpan' } );
	 * 			editor.conversion.for( 'downcast' ).attributeToElement( { view: 'span', model: 'htmlSpan' } );
	 *
	 * 			evt.stop();
	 * 		}, { priority: 'high' } )
	 *
	 * @event register
	 * @param {module:html-support/dataschema~DataSchemaDefinition} definition
	 */
}

// Matches and consumes the given view attributes.
//
// @private
// @param {module:engine/view/element~Element} viewElement
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi
// @param {module:engine/view/matcher~Matcher Matcher} matcher
// @returns {Object} [result]
// @returns {Object} result.attributes
// @returns {Object} result.styles
// @returns {Array.<String>} result.classes
function consumeAttributes( viewElement, conversionApi, matcher ) {
	const matches = consumeAttributeMatches( viewElement, conversionApi, matcher );
	const { attributes, styles, classes } = mergeMatchResults( matches );
	const viewAttributes = {};

	// Remove invalid DOM element attributes.
	if ( attributes.size ) {
		for ( const key of attributes ) {
			if ( !isValidAttributeName( key ) ) {
				attributes.delete( key );
			}
		}
	}

	if ( attributes.size ) {
		viewAttributes.attributes = iterableToObject( attributes, key => viewElement.getAttribute( key ) );
	}

	if ( styles.size ) {
		viewAttributes.styles = iterableToObject( styles, key => viewElement.getStyle( key ) );
	}

	if ( classes.size ) {
		viewAttributes.classes = Array.from( classes );
	}

	if ( !Object.keys( viewAttributes ).length ) {
		return null;
	}

	return viewAttributes;
}

// Consumes matched attributes.
//
// @private
// @param {module:engine/view/element~Element} viewElement
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi
// @param {module:engine/view/matcher~Matcher Matcher} matcher
// @returns {Array.<Object>} Array with match information about found attributes.
function consumeAttributeMatches( viewElement, { consumable }, matcher ) {
	const matches = matcher.matchAll( viewElement ) || [];
	const consumedMatches = [];

	for ( const match of matches ) {
		removeConsumedAttributes( consumable, viewElement, match );

		// We only want to consume attributes, so element can be still processed by other converters.
		delete match.match.name;

		consumable.consume( viewElement, match.match );
		consumedMatches.push( match );
	}

	return consumedMatches;
}

// Removes attributes from the given match that were already consumed by other converters.
//
// @private
// @param {module:engine/view/element~Element} viewElement
// @param {module:engine/conversion/viewconsumable~ViewConsumable} consumable
// @param {Object} match
function removeConsumedAttributes( consumable, viewElement, match ) {
	for ( const key of [ 'attributes', 'classes', 'styles' ] ) {
		const attributes = match.match[ key ];

		if ( !attributes ) {
			continue;
		}

		// Iterating over a copy of an array so removing items doesn't influence iteration.
		for ( const value of Array.from( attributes ) ) {
			if ( !consumable.test( viewElement, ( { [ key ]: [ value ] } ) ) ) {
				lodash_es_pull( attributes, value );
			}
		}
	}
}

// Merges the result of {@link module:engine/view/matcher~Matcher#matchAll} method.
//
// @private
// @param {Array.<Object>} matches
// @returns {Object} result
// @returns {Set.<Object>} result.attributes Set with matched attribute names.
// @returns {Set.<Object>} result.styles Set with matched style names.
// @returns {Set.<String>} result.classes Set with matched class names.
function mergeMatchResults( matches ) {
	const matchResult = {
		attributes: new Set(),
		classes: new Set(),
		styles: new Set()
	};

	for ( const match of matches ) {
		for ( const key in matchResult ) {
			const values = match.match[ key ] || [];

			values.forEach( value => matchResult[ key ].add( value ) );
		}
	}

	return matchResult;
}

// Converts the given iterable object into an object.
//
// @private
// @param {Iterable.<String>} iterable
// @param {Function} getValue Should result with value for the given object key.
// @returns {Object}
function iterableToObject( iterable, getValue ) {
	const attributesObject = {};

	for ( const prop of iterable ) {
		const value = getValue( prop );
		if ( value !== undefined ) {
			attributesObject[ prop ] = getValue( prop );
		}
	}

	return attributesObject;
}

// Matcher by default has to match **all** patterns to count it as an actual match. Splitting the pattern
// into separate patterns means that any matched pattern will be count as a match.
//
// @private
// @param {module:engine/view/matcher~MatcherPattern} pattern Pattern to split.
// @param {String} attributeName Name of the attribute to split (e.g. 'attributes', 'classes', 'styles').
// @returns {Array.<module:engine/view/matcher~MatcherPattern>}
function splitPattern( pattern, attributeName ) {
	const { name } = pattern;

	if ( lodash_es_isPlainObject( pattern[ attributeName ] ) ) {
		return Object.entries( pattern[ attributeName ] ).map(
			( [ key, value ] ) => ( {
				name,
				[ attributeName ]: {
					[ key ]: value
				}
			} ) );
	}

	if ( Array.isArray( pattern[ attributeName ] ) ) {
		return pattern[ attributeName ].map(
			value => ( {
				name,
				[ attributeName ]: [ value ]
			} )
		);
	}

	return [ pattern ];
}

// Rules are matched in conjunction (AND operation), but we want to have a match if *any* of the rules is matched (OR operation).
// By splitting the rules we force the latter effect.
//
// @private
// @param {module:engine/view/matcher~MatcherPattern} rules
// @returns {Array.<module:engine/view/matcher~MatcherPattern>}
function splitRules( rules ) {
	const { name, attributes, classes, styles } = rules;
	const splittedRules = [];

	if ( attributes ) {
		splittedRules.push( ...splitPattern( { name, attributes }, 'attributes' ) );
	}
	if ( classes ) {
		splittedRules.push( ...splitPattern( { name, classes }, 'classes' ) );
	}
	if ( styles ) {
		splittedRules.push( ...splitPattern( { name, styles }, 'styles' ) );
	}

	return splittedRules;
}

// Returns true if name is valid for a DOM attribute name.
function isValidAttributeName( name ) {
	try {
		document.createAttribute( name );
	} catch ( error ) {
		return false;
	}

	return true;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-select-all/src/selectallcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module select-all/selectallcommand
 */

/**
 * The select all command.
 *
 * It is used by the {@link module:select-all/selectallediting~SelectAllEditing select all editing feature} to handle
 * the <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> keystroke.
 *
 * Executing this command changes the {@glink framework/guides/architecture/editing-engine#model model}
 * selection so it contains the entire content of the editable root of the editor the selection is
 * {@link module:engine/model/selection~Selection#anchor anchored} in.
 *
 * If the selection was anchored in a {@glink framework/guides/tutorials/implementing-a-block-widget nested editable}
 * (e.g. a caption of an image), the new selection will contain its entire content. Successive executions of this command
 * will expand the selection to encompass more and more content up to the entire editable root of the editor.
 *
 * @extends module:core/command~Command
 */
class SelectAllCommand extends command_Command {
    /**
     * @inheritDoc
     */
    constructor(editor) {
        super(editor);
        // It does not affect data so should be enabled in read-only mode.
        this.affectsData = false;
    }
    /**
     * @inheritDoc
     */
    execute() {
        const model = this.editor.model;
        const selection = model.document.selection;
        let scopeElement = model.schema.getLimitElement(selection);
        // If an entire scope is selected, or the selection's ancestor is not a scope yet,
        // browse through ancestors to find the enclosing parent scope.
        if (selection.containsEntireContent(scopeElement) || !isSelectAllScope(model.schema, scopeElement)) {
            do {
                scopeElement = scopeElement.parent;
                // Do nothing, if the entire `root` is already selected.
                if (!scopeElement) {
                    return;
                }
            } while (!isSelectAllScope(model.schema, scopeElement));
        }
        model.change(writer => {
            writer.setSelection(scopeElement, 'in');
        });
    }
}
// Checks whether the element is a valid select-all scope.
// Returns true, if the element is a {@link module:engine/model/schema~Schema#isLimit limit},
// and can contain any text or paragraph.
//
// @param {module:engine/model/schema~Schema} schema The schema to check against.
// @param {module:engine/model/element~Element} element
// @return {Boolean}
function isSelectAllScope(schema, element) {
    return schema.isLimit(element) && (schema.checkChild(element, '$text') || schema.checkChild(element, 'paragraph'));
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-select-all/src/selectallediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module select-all/selectallediting
 */



const SELECT_ALL_KEYSTROKE = parseKeystroke('Ctrl+A');
/**
 * The select all editing feature.
 *
 * It registers the `'selectAll'` {@link module:select-all/selectallcommand~SelectAllCommand command}
 * and the <kbd>Ctrl/⌘</kbd>+<kbd>A</kbd> keystroke listener which executes it.
 *
 * @extends module:core/plugin~Plugin
 */
class SelectAllEditing extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'SelectAllEditing';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const view = editor.editing.view;
        const viewDocument = view.document;
        editor.commands.add('selectAll', new SelectAllCommand(editor));
        this.listenTo(viewDocument, 'keydown', (eventInfo, domEventData) => {
            if (getCode(domEventData) === SELECT_ALL_KEYSTROKE) {
                editor.execute('selectAll');
                domEventData.preventDefault();
            }
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-select-all/theme/icons/select-all.svg
/* harmony default export */ const select_all = ("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\"><path d=\"M.75 15.5a.75.75 0 0 1 .75.75V18l.008.09A.5.5 0 0 0 2 18.5h1.75a.75.75 0 1 1 0 1.5H1.5l-.144-.007a1.5 1.5 0 0 1-1.35-1.349L0 18.5v-2.25a.75.75 0 0 1 .75-.75zm18.5 0a.75.75 0 0 1 .75.75v2.25l-.007.144a1.5 1.5 0 0 1-1.349 1.35L18.5 20h-2.25a.75.75 0 1 1 0-1.5H18a.5.5 0 0 0 .492-.41L18.5 18v-1.75a.75.75 0 0 1 .75-.75zm-10.45 3c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2H7.2a.2.2 0 0 1-.2-.2v-1.1c0-.11.09-.2.2-.2h1.6zm4 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2h-1.6a.2.2 0 0 1-.2-.2v-1.1c0-.11.09-.2.2-.2h1.6zm.45-5.5a.75.75 0 1 1 0 1.5h-8.5a.75.75 0 1 1 0-1.5h8.5zM1.3 11c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2H.2a.2.2 0 0 1-.2-.2v-1.6c0-.11.09-.2.2-.2h1.1zm18.5 0c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2h-1.1a.2.2 0 0 1-.2-.2v-1.6c0-.11.09-.2.2-.2h1.1zm-4.55-2a.75.75 0 1 1 0 1.5H4.75a.75.75 0 1 1 0-1.5h10.5zM1.3 7c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2H.2a.2.2 0 0 1-.2-.2V7.2c0-.11.09-.2.2-.2h1.1zm18.5 0c.11 0 .2.09.2.2v1.6a.2.2 0 0 1-.2.2h-1.1a.2.2 0 0 1-.2-.2V7.2c0-.11.09-.2.2-.2h1.1zm-4.55-2a.75.75 0 1 1 0 1.5h-2.5a.75.75 0 1 1 0-1.5h2.5zm-5 0a.75.75 0 1 1 0 1.5h-5.5a.75.75 0 0 1 0-1.5h5.5zm-6.5-5a.75.75 0 0 1 0 1.5H2a.5.5 0 0 0-.492.41L1.5 2v1.75a.75.75 0 0 1-1.5 0V1.5l.007-.144A1.5 1.5 0 0 1 1.356.006L1.5 0h2.25zM18.5 0l.144.007a1.5 1.5 0 0 1 1.35 1.349L20 1.5v2.25a.75.75 0 1 1-1.5 0V2l-.008-.09A.5.5 0 0 0 18 1.5h-1.75a.75.75 0 1 1 0-1.5h2.25zM8.8 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2H7.2a.2.2 0 0 1-.2-.2V.2c0-.11.09-.2.2-.2h1.6zm4 0c.11 0 .2.09.2.2v1.1a.2.2 0 0 1-.2.2h-1.6a.2.2 0 0 1-.2-.2V.2c0-.11.09-.2.2-.2h1.6z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-select-all/src/selectallui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module select-all/selectallui
 */



/**
 * The select all UI feature.
 *
 * It registers the `'selectAll'` UI button in the editor's
 * {@link module:ui/componentfactory~ComponentFactory component factory}. When clicked, the button
 * executes the {@link module:select-all/selectallcommand~SelectAllCommand select all command}.
 *
 * @extends module:core/plugin~Plugin
 */
class SelectAllUI extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'SelectAllUI';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        editor.ui.componentFactory.add('selectAll', locale => {
            const command = editor.commands.get('selectAll');
            const view = new buttonview_ButtonView(locale);
            const t = locale.t;
            view.set({
                label: t('Select all'),
                icon: select_all,
                keystroke: 'Ctrl+A',
                tooltip: true
            });
            view.bind('isEnabled').to(command, 'isEnabled');
            // Execute the command.
            this.listenTo(view, 'execute', () => {
                editor.execute('selectAll');
                editor.editing.view.focus();
            });
            return view;
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-select-all/src/selectall.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module select-all/selectall
 */



/**
 * The select all feature.
 *
 * This is a "glue" plugin which loads the {@link module:select-all/selectallediting~SelectAllEditing select all editing feature}
 * and the {@link module:select-all/selectallui~SelectAllUI select all UI feature}.
 *
 * Please refer to the documentation of individual features to learn more.
 *
 * @extends module:core/plugin~Plugin
 */
class SelectAll extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get requires() {
        return [SelectAllEditing, SelectAllUI];
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'SelectAll';
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-select-all/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module select-all
 */




;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/select-all.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/select-all
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-essentials/src/essentials.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module essentials/essentials
 */









/**
 * A plugin including all essential editing features. It represents a set of features that enables similar functionalities
 * to a `<textarea>` element.
 *
 * It includes:
 *
 * * {@link module:clipboard/clipboard~Clipboard},
 * * {@link module:enter/enter~Enter},
 * * {@link module:select-all/selectall~SelectAll},
 * * {@link module:enter/shiftenter~ShiftEnter},
 * * {@link module:typing/typing~Typing},
 * * {@link module:undo/undo~Undo}.
 *
 * This plugin set does not define any block-level containers (such as {@link module:paragraph/paragraph~Paragraph}).
 * If your editor is supposed to handle block content, make sure to include it.
 *
 * @extends module:core/plugin~Plugin
 */
class Essentials extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ Clipboard, enter_Enter, SelectAll, ShiftEnter, Typing, Undo ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Essentials';
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css
var responsiveform = __webpack_require__(1590);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-ui/theme/components/responsive-form/responsiveform.css

            

var responsiveform_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

responsiveform_options.insert = "head";
responsiveform_options.singleton = true;

var responsiveform_update = injectStylesIntoStyleTag_default()(responsiveform/* default */.Z, responsiveform_options);



/* harmony default export */ const responsive_form_responsiveform = (responsiveform/* default.locals */.Z.locals || {});
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-find-and-replace/theme/findandreplaceform.css
var findandreplaceform = __webpack_require__(9289);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/theme/findandreplaceform.css

            

var findandreplaceform_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

findandreplaceform_options.insert = "head";
findandreplaceform_options.singleton = true;

var findandreplaceform_update = injectStylesIntoStyleTag_default()(findandreplaceform/* default */.Z, findandreplaceform_options);



/* harmony default export */ const theme_findandreplaceform = (findandreplaceform/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/ui/findandreplaceformview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/ui/findandreplaceformview
 */





// See: #8833.
// eslint-disable-next-line ckeditor5-rules/ckeditor-imports



// eslint-disable-next-line ckeditor5-rules/ckeditor-imports



/**
 * The find and replace form view class.
 *
 * See {@link module:find-and-replace/ui/findandreplaceformview~FindAndReplaceFormView}.
 *
 * @extends module:ui/view~View
 */
class FindAndReplaceFormView extends src_view_View {
	/**
	 * Creates a view of find and replace form.
	 *
	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
	 */
	constructor( locale ) {
		super( locale );

		const t = locale.t;

		/**
		 * Stores the number of matched search results.
		 *
		 * @readonly
		 * @observable
		 * @member {Number} #matchCount
		 */
		this.set( 'matchCount', 0 );

		/**
		 * The offset of currently highlighted search result in {@link #matchCount matched results}.
		 *
		 * @readonly
		 * @observable
		 * @member {Number|null} #highlightOffset
		 */
		this.set( 'highlightOffset', 0 );

		/**
		 * `true` when the search params (find text, options) has been changed by the user since
		 * the last time find was executed. `false` otherwise.
		 *
		 * @readonly
		 * @observable
		 * @member {Boolean} #isDirty
		 */
		this.set( 'isDirty', false );

		/**
		 * A live object with the aggregated `isEnabled` states of editor commands related to find and
		 * replace. For instance, it may look as follows:
		 *
		 *		{
		 *			findNext: true,
		 *			findPrevious: true,
		 *			replace: false,
		 *			replaceAll: false
		 *		}
		 *
		 * @protected
		 * @readonly
		 * @observable
		 * @member {Object} #_areCommandsEnabled
		 */
		this.set( '_areCommandsEnabled', {} );

		/**
		 * The content of the counter label displaying the index of the current highlighted match
		 * on top of the find input, for instance "3 of 50".
		 *
		 * @protected
		 * @readonly
		 * @observable
		 * @member {String} #_resultsCounterText
		 */
		this.set( '_resultsCounterText', '' );

		/**
		 * The flag reflecting the state of the "Match case" switch button in the search options
		 * dropdown.
		 *
		 * @protected
		 * @readonly
		 * @observable
		 * @member {Boolean} #_matchCase
		 */
		this.set( '_matchCase', false );

		/**
		 * The flag reflecting the state of the "Whole words only" switch button in the search options
		 * dropdown.
		 *
		 * @protected
		 * @readonly
		 * @observable
		 * @member {Boolean} #_wholeWordsOnly
		 */
		this.set( '_wholeWordsOnly', false );

		/**
		 * This flag is set `true` when some matches were found and the user didn't change the search
		 * params (text to find, options) yet. This is only possible immediately after hitting the "Find" button.
		 * `false` when there were no matches (see {@link #matchCount}) or the user changed the params (see {@link #isDirty}).
		 *
		 * It is used to control the enabled state of the replace UI (input and buttons); replacing text is only possible
		 * if this flag is `true`.
		 *
		 * @protected
		 * @readonly
		 * @observable
		 * @member {Boolean} #_searchResultsFound
		 */
		this.bind( '_searchResultsFound' ).to(
			this, 'matchCount',
			this, 'isDirty',
			( matchCount, isDirty ) => {
				return matchCount > 0 && !isDirty;
			}
		);

		/**
		 * The find in text input view that stores the searched string.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
		 */
		this._findInputView = this._createInputField( t( 'Find in text…' ) );

		/**
		 * The replace input view.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
		 */
		this._replaceInputView = this._createInputField( t( 'Replace with…' ) );

		/**
		 * The find button view that initializes the search process.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this._findButtonView = this._createButton( {
			label: t( 'Find' ),
			class: 'ck-button-find ck-button-action',
			withText: true
		} );

		/**
		 * The find previous button view.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this._findPrevButtonView = this._createButton( {
			label: t( 'Previous result' ),
			class: 'ck-button-prev',
			icon: previous_arrow,
			keystroke: 'Shift+F3',
			tooltip: true
		} );

		/**
		 * The find next button view.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this._findNextButtonView = this._createButton( {
			label: t( 'Next result' ),
			class: 'ck-button-next',
			icon: previous_arrow,
			keystroke: 'F3',
			tooltip: true
		} );

		/**
		 * The find options dropdown.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/dropdown/dropdownview~DropdownView}
		 */
		this._optionsDropdown = this._createOptionsDropdown();

		/**
		 * The replace button view.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this._replaceButtonView = this._createButton( {
			label: t( 'Replace' ),
			class: 'ck-button-replace',
			withText: true
		} );

		/**
		 * The replace all button view.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this._replaceAllButtonView = this._createButton( {
			label: t( 'Replace all' ),
			class: 'ck-button-replaceall',
			withText: true
		} );

		/**
		 * The fieldset aggregating the find UI.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/view/view~View}
		 */
		this._findFieldsetView = this._createFindFieldset();

		/**
		 * The fieldset aggregating the replace UI.
		 *
		 * @protected
		 * @readonly
		 * @member {module:ui/view/view~View}
		 */
		this._replaceFieldsetView = this._createReplaceFieldset();

		/**
		 * Tracks information about the DOM focus in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this._focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @protected
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this._keystrokes = new KeystrokeHandler();

		/**
		 * A collection of views that can be focused in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this._focusTracker,
			keystrokeHandler: this._keystrokes,
			actions: {
				// Navigate form fields backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke.
				focusPrevious: 'shift + tab',

				// Navigate form fields forwards using the <kbd>Tab</kbd> key.
				focusNext: 'tab'
			}
		} );

		this.setTemplate( {
			tag: 'form',
			attributes: {
				class: [
					'ck',
					'ck-find-and-replace-form'
				],

				tabindex: '-1'
			},
			children: [
				new FormHeaderView( locale, {
					label: t( 'Find and replace' )
				} ),
				this._findFieldsetView,
				this._replaceFieldsetView
			]
		} );

		injectCssTransitionDisabler( this );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		submitHandler( { view: this } );

		this._initFocusCycling();
		this._initKeystrokeHandling();
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this._focusTracker.destroy();
		this._keystrokes.destroy();
	}

	/**
	 * Focuses the fist {@link #_focusables} in the form.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}

	/**
	 * Resets the form before re-appearing.
	 *
	 * It clears error messages, hides the match counter and disables the replace feature
	 * until the next hit of the "Find" button.
	 *
	 * **Note**: It does not reset inputs and options, though. This way the form works better in editors with
	 * disappearing toolbar (e.g. BalloonEditor): hiding the toolbar by accident (together with the find and replace UI)
	 * does not require filling the entire form again.
	 */
	reset() {
		this._findInputView.errorText = null;
		this.isDirty = true;
	}

	/**
	 * Returns the value of the find input.
	 *
	 * @protected
	 * @returns {String}
	 */
	get _textToFind() {
		return this._findInputView.fieldView.element.value;
	}

	/**
	 * Returns the value of the replace input.
	 *
	 * @protected
	 * @returns {String}
	 */
	get _textToReplace() {
		return this._replaceInputView.fieldView.element.value;
	}

	/**
	 * Configures and returns the `<fieldset>` aggregating all find controls.
	 *
	 * @private
	 * @returns {module:ui/view~View}
	 */
	_createFindFieldset() {
		const locale = this.locale;
		const fieldsetView = new src_view_View( locale );

		// Typing in the find field invalidates all previous results (the form is "dirty").
		this._findInputView.fieldView.on( 'input', () => {
			this.isDirty = true;
		} );

		this._findButtonView.on( 'execute', this._onFindButtonExecute.bind( this ) );

		// Pressing prev/next buttons fires related event on the form.
		this._findPrevButtonView.delegate( 'execute' ).to( this, 'findPrevious' );
		this._findNextButtonView.delegate( 'execute' ).to( this, 'findNext' );

		// Prev/next buttons will be disabled when related editor command gets disabled.
		this._findPrevButtonView.bind( 'isEnabled' ).to( this, '_areCommandsEnabled', ( { findPrevious } ) => findPrevious );
		this._findNextButtonView.bind( 'isEnabled' ).to( this, '_areCommandsEnabled', ( { findNext } ) => findNext );

		this._injectFindResultsCounter();

		fieldsetView.setTemplate( {
			tag: 'fieldset',
			attributes: {
				class: [ 'ck', 'ck-find-and-replace-form__find' ]
			},
			children: [
				this._findInputView,
				this._findButtonView,
				this._findPrevButtonView,
				this._findNextButtonView
			]
		} );

		return fieldsetView;
	}

	/**
	 * The action performed when the {@link #_findButtonView} is pressed.
	 *
	 * @private
	 */
	_onFindButtonExecute() {
		// When hitting "Find" in an empty input, an error should be displayed.
		// Also, if the form was "dirty", it should remain so.
		if ( !this._textToFind ) {
			const t = this.t;

			this._findInputView.errorText = t( 'Text to find must not be empty.' );

			return;
		}

		// Hitting "Find" automatically clears the dirty state.
		this.isDirty = false;

		this.fire( 'findNext', {
			searchText: this._textToFind,
			matchCase: this._matchCase,
			wholeWords: this._wholeWordsOnly
		} );
	}

	/**
	 * Configures an injects the find results counter displaying a "N of M" label of the {@link #_findInputView}.
	 *
	 * @private
	 */
	_injectFindResultsCounter() {
		const locale = this.locale;
		const t = locale.t;
		const bind = this.bindTemplate;
		const resultsCounterView = new src_view_View( locale );

		this.bind( '_resultsCounterText' ).to( this, 'highlightOffset', this, 'matchCount',
			( highlightOffset, matchCount ) => t( '%0 of %1', [ highlightOffset, matchCount ] )
		);

		resultsCounterView.setTemplate( {
			tag: 'span',
			attributes: {
				class: [
					'ck',
					'ck-results-counter',
					// The counter only makes sense when the field text corresponds to search results in the editing.
					bind.if( 'isDirty', 'ck-hidden' )
				]
			},
			children: [
				{
					text: bind.to( '_resultsCounterText' )
				}
			]
		} );

		// The whole idea is that when the text of the counter changes, its width also increases/decreases and
		// it consumes more or less space over the input. The input, on the other hand, should adjust it's right
		// padding so its *entire* text always remains visible and available to the user.
		const updateFindInputPadding = () => {
			const inputElement = this._findInputView.fieldView.element;

			// Don't adjust the padding if the input (also: counter) were not rendered or not inserted into DOM yet.
			if ( !inputElement || !isVisible( inputElement ) ) {
				return;
			}

			const counterWidth = new rect_Rect( resultsCounterView.element ).width;
			const paddingPropertyName = locale.uiLanguageDirection === 'ltr' ? 'paddingRight' : 'paddingLeft';

			if ( !counterWidth ) {
				inputElement.style[ paddingPropertyName ] = null;
			} else {
				inputElement.style[ paddingPropertyName ] = `calc( 2 * var(--ck-spacing-standard) + ${ counterWidth }px )`;
			}
		};

		// Adjust the input padding when the text of the counter changes, for instance "1 of 200" is narrower than "123 of 200".
		// Using "low" priority to let the text be set by the template binding first.
		this.on( 'change:_resultsCounterText', updateFindInputPadding, { priority: 'low' } );

		// Adjust the input padding when the counter shows or hides. When hidden, there should be no padding. When it shows, the
		// padding should be set according to the text of the counter.
		// Using "low" priority to let the text be set by the template binding first.
		this.on( 'change:isDirty', updateFindInputPadding, { priority: 'low' } );

		// Put the counter element next to the <input> in the find field.
		this._findInputView.template.children[ 0 ].children.push( resultsCounterView );
	}

	/**
	 * Configures and returns the `<fieldset>` aggregating all replace controls.
	 *
	 * @private
	 * @returns {module:ui/view~View}
	 */
	_createReplaceFieldset() {
		const locale = this.locale;
		const t = locale.t;
		const fieldsetView = new src_view_View( locale );

		this._replaceButtonView.bind( 'isEnabled' ).to(
			this, '_areCommandsEnabled',
			this, '_searchResultsFound',
			( { replace }, resultsFound ) => replace && resultsFound );

		this._replaceAllButtonView.bind( 'isEnabled' ).to(
			this, '_areCommandsEnabled',
			this, '_searchResultsFound',
			( { replaceAll }, resultsFound ) => replaceAll && resultsFound );

		this._replaceInputView.bind( 'isEnabled' ).to(
			this, '_areCommandsEnabled',
			this, '_searchResultsFound',
			( { replace }, resultsFound ) => replace && resultsFound );

		this._replaceInputView.bind( 'infoText' ).to(
			this._replaceInputView, 'isEnabled',
			this._replaceInputView, 'isFocused',
			( isEnabled, isFocused ) => {
				if ( isEnabled || !isFocused ) {
					return '';
				}

				return t( 'Tip: Find some text first in order to replace it.' );
			} );

		this._replaceButtonView.on( 'execute', () => {
			this.fire( 'replace', {
				searchText: this._textToFind,
				replaceText: this._textToReplace
			} );
		} );

		this._replaceAllButtonView.on( 'execute', () => {
			this.fire( 'replaceAll', {
				searchText: this._textToFind,
				replaceText: this._textToReplace
			} );

			this.focus();
		} );

		fieldsetView.setTemplate( {
			tag: 'fieldset',
			attributes: {
				class: [ 'ck', 'ck-find-and-replace-form__replace' ]
			},
			children: [
				this._replaceInputView,
				this._optionsDropdown,
				this._replaceButtonView,
				this._replaceAllButtonView
			]
		} );

		return fieldsetView;
	}

	/**
	 * Creates, configures and returns and instance of a dropdown allowing users to narrow
	 * the search criteria down. The dropdown has a list with switch buttons for each option.
	 *
	 * @private
	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
	 */
	_createOptionsDropdown() {
		const locale = this.locale;
		const t = locale.t;
		const dropdownView = createDropdown( locale );

		dropdownView.class = 'ck-options-dropdown';

		dropdownView.buttonView.set( {
			withText: false,
			label: t( 'Show options' ),
			icon: icons.cog,
			tooltip: true
		} );

		const matchCaseModel = new model_Model( {
			withText: true,
			label: t( 'Match case' ),

			// A dummy read-only prop to make it easy to tell which switch was toggled.
			_isMatchCaseSwitch: true
		} );

		const wholeWordsOnlyModel = new model_Model( {
			withText: true,
			label: t( 'Whole words only' )
		} );

		// Let the switches be controlled by form's observable properties.
		matchCaseModel.bind( 'isOn' ).to( this, '_matchCase' );
		wholeWordsOnlyModel.bind( 'isOn' ).to( this, '_wholeWordsOnly' );

		// Update the state of the form when a switch is toggled.
		dropdownView.on( 'execute', evt => {
			if ( evt.source._isMatchCaseSwitch ) {
				this._matchCase = !this._matchCase;
			} else {
				this._wholeWordsOnly = !this._wholeWordsOnly;
			}

			// Toggling a switch makes the form dirty because this changes search criteria
			// just like typing text of the find input.
			this.isDirty = true;
		} );

		addListToDropdown( dropdownView, new Collection( [
			{ type: 'switchbutton', model: matchCaseModel },
			{ type: 'switchbutton', model: wholeWordsOnlyModel }
		] ) );

		return dropdownView;
	}

	/**
	 * Initializes the {@link #_focusables} and {@link #_focusTracker} to allow navigation
	 * using <kbd>Tab</kbd> and <kbd>Shift</kbd>+<kbd>Tab</kbd> keystrokes in the right order.
	 *
	 * @private
	 */
	_initFocusCycling() {
		const childViews = [
			this._findInputView,
			this._findButtonView,
			this._findPrevButtonView,
			this._findNextButtonView,
			this._replaceInputView,
			this._optionsDropdown,
			this._replaceButtonView,
			this._replaceAllButtonView
		];

		childViews.forEach( v => {
			// Register the view as focusable.
			this._focusables.add( v );

			// Register the view in the focus tracker.
			this._focusTracker.add( v.element );
		} );
	}

	/**
	 * Initializes the keystroke handling in the form.
	 *
	 * @private
	 */
	_initKeystrokeHandling() {
		const stopPropagation = data => data.stopPropagation();
		const stopPropagationAndPreventDefault = data => {
			data.stopPropagation();
			data.preventDefault();
		};

		// Start listening for the keystrokes coming from #element.
		this._keystrokes.listenTo( this.element );

		// Find the next result upon F3.
		this._keystrokes.set( 'f3', event => {
			stopPropagationAndPreventDefault( event );

			this._findNextButtonView.fire( 'execute' );
		} );

		// Find the previous result upon F3.
		this._keystrokes.set( 'shift+f3', event => {
			stopPropagationAndPreventDefault( event );

			this._findPrevButtonView.fire( 'execute' );
		} );

		// Find or replace upon pressing Enter in the find and replace fields.
		this._keystrokes.set( 'enter', event => {
			const target = event.target;

			if ( target === this._findInputView.fieldView.element ) {
				if ( this._areCommandsEnabled.findNext ) {
					this._findNextButtonView.fire( 'execute' );
				} else {
					this._findButtonView.fire( 'execute' );
				}
				stopPropagationAndPreventDefault( event );
			} else if ( target === this._replaceInputView.fieldView.element && !this.isDirty ) {
				this._replaceButtonView.fire( 'execute' );
				stopPropagationAndPreventDefault( event );
			}
		} );

		// Find previous upon pressing Shift+Enter in the find field.
		this._keystrokes.set( 'shift+enter', event => {
			const target = event.target;

			if ( target !== this._findInputView.fieldView.element ) {
				return;
			}

			if ( this._areCommandsEnabled.findPrevious ) {
				this._findPrevButtonView.fire( 'execute' );
			} else {
				this._findButtonView.fire( 'execute' );
			}

			stopPropagationAndPreventDefault( event );
		} );

		// Since the form is in the dropdown panel which is a child of the toolbar, the toolbar's
		// keystroke handler would take over the key management in the URL input.
		// We need to prevent this ASAP. Otherwise, the basic caret movement using the arrow keys will be impossible.
		this._keystrokes.set( 'arrowright', stopPropagation );
		this._keystrokes.set( 'arrowleft', stopPropagation );
		this._keystrokes.set( 'arrowup', stopPropagation );
		this._keystrokes.set( 'arrowdown', stopPropagation );

		// Intercept the `selectstart` event, which is blocked by default because of the default behavior
		// of the DropdownView#panelView. This blocking prevents the native select all on Ctrl+A.
		this.listenTo( this._findInputView.element, 'selectstart', ( evt, domEvt ) => {
			domEvt.stopPropagation();
		}, { priority: 'high' } );

		this.listenTo( this._replaceInputView.element, 'selectstart', ( evt, domEvt ) => {
			domEvt.stopPropagation();
		}, { priority: 'high' } );
	}

	/**
	 * Creates a button view.
	 *
	 * @private
	 * @param {Object} options The properties of the `ButtonView`.
	 * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
	 */
	_createButton( options ) {
		const button = new buttonview_ButtonView( this.locale );

		button.set( options );

		return button;
	}

	/**
	 * Creates a labeled input view.
	 *
	 * @private
	 * @param {String} label The input label.
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView} The labeled input view instance.
	 */
	_createInputField( label ) {
		const labeledInput = new LabeledFieldView( this.locale, createLabeledInputText );

		labeledInput.label = label;

		return labeledInput;
	}
}

/**
 * Fired when the find next button is triggered.
 *
 * @event findNext
 * @param {String} searchText Search text.
 */

/**
 * Fired when the find previous button is triggered.
 *
 * @event findPrevious
 * @param {String} searchText Search text.
 */

/**
 * Fired when the replace button is triggered.
 *
 * @event replace
 * @param {String} replaceText Replacement text.
 */

/**
 * Fired when the replaceAll button is triggered.
 *
 * @event replaceAll
 * @param {String} replaceText Replacement text.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/theme/icons/find-replace.svg
/* harmony default export */ const find_replace = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m12.87 13.786 1.532-1.286 3.857 4.596a1 1 0 1 1-1.532 1.286l-3.857-4.596z\"/><path d=\"M16.004 8.5a6.5 6.5 0 0 1-9.216 5.905c-1.154-.53-.863-1.415-.663-1.615.194-.194.564-.592 1.635-.141a4.5 4.5 0 0 0 5.89-5.904l-.104-.227 1.332-1.331c.045-.046.196-.041.224.007a6.47 6.47 0 0 1 .902 3.306zm-3.4-5.715c.562.305.742 1.106.354 1.494-.388.388-.995.414-1.476.178a4.5 4.5 0 0 0-6.086 5.882l.114.236-1.348 1.349c-.038.037-.17.022-.198-.023a6.5 6.5 0 0 1 5.54-9.9 6.469 6.469 0 0 1 3.1.784z\"/><path d=\"M4.001 11.93.948 8.877a.2.2 0 0 1 .141-.341h6.106a.2.2 0 0 1 .141.341L4.283 11.93a.2.2 0 0 1-.282 0zm11.083-6.789 3.053 3.053a.2.2 0 0 1-.14.342H11.89a.2.2 0 0 1-.14-.342l3.052-3.053a.2.2 0 0 1 .282 0z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/findandreplaceui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/findandreplaceui
 */







/**
 * The default find and replace UI.
 *
 * It registers the `'findAndReplace'` UI button in the editor's {@link module:ui/componentfactory~ComponentFactory component factory}.
 * that uses the {@link module:find-and-replace/findandreplace~FindAndReplace FindAndReplace} plugin API.
 *
 * @extends module:core/plugin~Plugin
 */
class FindAndReplaceUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FindAndReplaceUI';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * A reference to the find and replace form view.
		 *
		 * @member {module:find-and-replace/ui/findandreplaceformview~FindAndReplaceFormView} #formView
		 */
		this.formView = null;
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Register the toolbar dropdown component.
		editor.ui.componentFactory.add( 'findAndReplace', locale => {
			const dropdown = createDropdown( locale );
			const formView = this.formView = new FindAndReplaceFormView( editor.locale );

			// Dropdown should be disabled when in source editing mode. See #10001.
			dropdown.bind( 'isEnabled' ).to( editor.commands.get( 'find' ) );
			dropdown.panelView.children.add( formView );

			// Every time a dropdown is opened, the search text field should get focused and selected for better UX.
			// Note: Using the low priority here to make sure the following listener starts working after
			// the default action of the drop-down is executed (i.e. the panel showed up). Otherwise,
			// the invisible form/input cannot be focused/selected.
			//
			// Each time a dropdown is closed, move the focus back to the find and replace toolbar button
			// and let the find and replace editing feature know that all search results can be invalidated
			// and no longer should be marked in the content.
			dropdown.on( 'change:isOpen', ( event, name, isOpen ) => {
				if ( isOpen ) {
					formView.disableCssTransitions();

					formView.reset();
					formView._findInputView.fieldView.select();

					formView.enableCssTransitions();
				} else {
					this.fire( 'searchReseted' );
				}
			}, { priority: 'low' } );

			this._setupDropdownButton( dropdown );
			this._setupFormView( formView );

			return dropdown;
		} );
	}

	/**
	 * Sets up the find and replace button.
	 *
	 * @private
	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdown
	 */
	_setupDropdownButton( dropdown ) {
		const editor = this.editor;
		const t = editor.locale.t;

		dropdown.buttonView.set( {
			icon: find_replace,
			label: t( 'Find and replace' ),
			keystroke: 'CTRL+F',
			tooltip: true
		} );

		editor.keystrokes.set( 'Ctrl+F', ( data, cancelEvent ) => {
			dropdown.isOpen = true;
			cancelEvent();
		} );
	}

	/**
	 * Sets up the form view for the find and replace.
	 *
	 * @private
	 * @param {module:find-and-replace/ui/findandreplaceformview~FindAndReplaceFormView} formView A related form view.
	 */
	_setupFormView( formView ) {
		const editor = this.editor;
		const commands = editor.commands;
		const findAndReplaceEditing = this.editor.plugins.get( 'FindAndReplaceEditing' );
		const editingState = findAndReplaceEditing.state;
		const sortMapping = { before: -1, same: 0, after: 1 };

		// Let the form know which result is being highlighted.
		formView.bind( 'highlightOffset' ).to( editingState, 'highlightedResult', highlightedResult => {
			if ( !highlightedResult ) {
				return 0;
			}

			return Array.from( editingState.results )
				.sort( ( a, b ) => sortMapping[ a.marker.getStart().compareWith( b.marker.getStart() ) ] )
				.indexOf( highlightedResult ) + 1;
		} );

		// Let the form know how many results were found in total.
		formView.listenTo( editingState.results, 'change', () => {
			formView.matchCount = editingState.results.length;
		} );

		// Command states are used to enable/disable individual form controls.
		// To keep things simple, instead of binding 4 individual observables, there's only one that combines every
		// commands' isEnabled state. Yes, it will change more often but this simplifies the structure of the form.
		formView.bind( '_areCommandsEnabled' ).to(
			commands.get( 'findNext' ), 'isEnabled',
			commands.get( 'findPrevious' ), 'isEnabled',
			commands.get( 'replace' ), 'isEnabled',
			commands.get( 'replaceAll' ), 'isEnabled',
			( findNext, findPrevious, replace, replaceAll ) => ( { findNext, findPrevious, replace, replaceAll } )
		);

		// The UI plugin works as an interface between the form and the editing part of the feature.
		formView.delegate( 'findNext', 'findPrevious', 'replace', 'replaceAll' ).to( this );

		// Let the feature know that search results are no longer relevant because the user changed the searched phrase
		// (or options) but didn't hit the "Find" button yet (e.g. still typing).
		formView.on( 'change:isDirty', ( evt, data, isDirty ) => {
			if ( isDirty ) {
				this.fire( 'searchReseted' );
			}
		} );
	}
}

/**
 * Fired when the find next button is triggered.
 *
 * @event findNext
 * @param {String} searchText Search text.
 */

/**
 * Fired when the find previous button is triggered.
 *
 * @event findPrevious
 * @param {String} searchText Search text.
 */

/**
 * Fired when the replace button is triggered.
 *
 * @event replace
 * @param {String} replaceText Replacement text.
 */

/**
 * Fired when the replaceAll button is triggered.
 *
 * @event replaceAll
 * @param {String} replaceText Replacement text.
 */

/**
 * Fired when the UI was reset and the search results marked in the editing root should be invalidated,
 * for instance, because the user changed the searched phrase (or options) but didn't hit
 * the "Find" button yet.
 *
 * @event searchReseted
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/utils
 */




/**
 * Executes findCallback and updates search results list.
 *
 * @param {module:engine/model/range~Range} range The model range to scan for matches.
 * @param {module:engine/model/model~Model} model The model.
 * @param {Function} findCallback The callback that should return `true` if provided text matches the search term.
 * @param {module:utils/collection~Collection} [startResults] An optional collection of find matches that the function should
 * start with. This would be a collection returned by a previous `updateFindResultFromRange()` call.
 * @returns {module:utils/collection~Collection} A collection of objects describing find match.
 *
 * An example structure:
 *
 * ```js
 * {
 *	id: resultId,
 *	label: foundItem.label,
 *	marker
 *	}
 * ```
 */
function updateFindResultFromRange( range, model, findCallback, startResults ) {
	const results = startResults || new Collection();

	model.change( writer => {
		[ ...range ].forEach( ( { type, item } ) => {
			if ( type === 'elementStart' ) {
				if ( model.schema.checkChild( item, '$text' ) ) {
					const foundItems = findCallback( {
						item,
						text: rangeToText( model.createRangeIn( item ) )
					} );

					if ( !foundItems ) {
						return;
					}

					foundItems.forEach( foundItem => {
						const resultId = `findResult:${ uid() }`;
						const marker = writer.addMarker( resultId, {
							usingOperation: false,
							affectsData: false,
							range: writer.createRange(
								writer.createPositionAt( item, foundItem.start ),
								writer.createPositionAt( item, foundItem.end )
							)
						} );

						const index = findInsertIndex( results, marker );

						results.add(
							{
								id: resultId,
								label: foundItem.label,
								marker
							},
							index
						);
					} );
				}
			}
		} );
	} );

	return results;
}

/**
 * Returns text representation of a range. The returned text length should be the same as range length.
 * In order to achieve this this function will replace inline elements (text-line) as new line character ("\n").
 *
 * @param {module:engine/model/range~Range} range The model range.
 * @returns {String} The text content of the provided range.
 */
function rangeToText( range ) {
	return Array.from( range.getItems() ).reduce( ( rangeText, node ) => {
		// Trim text to a last occurrence of an inline element and update range start.
		if ( !( node.is( 'text' ) || node.is( 'textProxy' ) ) ) {
			// Editor has only one inline element defined in schema: `<softBreak>` which is treated as new line character in blocks.
			// Special handling might be needed for other inline elements (inline widgets).
			return `${ rangeText }\n`;
		}

		return rangeText + node.data;
	}, '' );
}

// Finds the appropriate index in the resultsList Collection.
function findInsertIndex( resultsList, markerToInsert ) {
	const result = resultsList.find( ( { marker } ) => {
		return markerToInsert.getStart().isBefore( marker.getStart() );
	} );

	return result ? resultsList.getIndex( result ) : resultsList.length;
}

// Maps RegExp match result to find result.
function regexpMatchToFindResult( matchResult ) {
	const lastGroupIndex = matchResult.length - 1;

	let startOffset = matchResult.index;

	// Searches with match all flag have an extra matching group with empty string or white space matched before the word.
	// If the search term starts with the space already, there is no extra group even with match all flag on.
	if ( matchResult.length === 3 ) {
		startOffset += matchResult[ 1 ].length;
	}

	return {
		label: matchResult[ lastGroupIndex ],
		start: startOffset,
		end: startOffset + matchResult[ lastGroupIndex ].length
	};
}

/**
 * Creates a text matching callback for a specified search term and matching options.
 *
 * @param {String} searchTerm The search term.
 * @param {Object} [options] Matching options.
 * @param {Boolean} [options.matchCase=false] If set to `true` letter casing will be ignored.
 * @param {Boolean} [options.wholeWords=false] If set to `true` only whole words that match `callbackOrText` will be matched.
 * @returns {Function}
 */
function findByTextCallback( searchTerm, options ) {
	let flags = 'gu';

	if ( !options.matchCase ) {
		flags += 'i';
	}

	let regExpQuery = `(${ lodash_es_escapeRegExp( searchTerm ) })`;

	if ( options.wholeWords ) {
		const nonLetterGroup = '[^a-zA-Z\u00C0-\u024F\u1E00-\u1EFF]';

		if ( !new RegExp( '^' + nonLetterGroup ).test( searchTerm ) ) {
			regExpQuery = `(^|${ nonLetterGroup }|_)${ regExpQuery }`;
		}

		if ( !new RegExp( nonLetterGroup + '$' ).test( searchTerm ) ) {
			regExpQuery = `${ regExpQuery }(?=_|${ nonLetterGroup }|$)`;
		}
	}

	const regExp = new RegExp( regExpQuery, flags );

	function findCallback( { text } ) {
		const matches = [ ...text.matchAll( regExp ) ];

		return matches.map( regexpMatchToFindResult );
	}

	return findCallback;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/findcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/findcommand
*/




/**
 * The find command. It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
 *
 * @extends module:core/command~Command
 */
class FindCommand extends command_Command {
	/**
	 * Creates a new `FindCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor on which this command will be used.
	 * @param {module:find-and-replace/findandreplacestate~FindAndReplaceState} state An object to hold plugin state.
	 */
	constructor( editor, state ) {
		super( editor );

		// The find command is always enabled.
		this.isEnabled = true;

		// It does not affect data so should be enabled in read-only mode.
		this.affectsData = false;

		/**
		 * The find and replace state object used for command operations.
		 *
		 * @private
		 * @member {module:find-and-replace/findandreplacestate~FindAndReplaceState} #_state
		 */
		this._state = state;
	}

	/**
	 * Executes the command.
	 *
	 * @param {Function|String} callbackOrText
	 * @param {Object} [options]
	 * @param {Boolean} [options.matchCase=false] If set to `true`, the letter case will be matched.
	 * @param {Boolean} [options.wholeWords=false] If set to `true`, only whole words that match `callbackOrText` will be matched.
	 *
	 * @fires execute
	 */
	execute( callbackOrText, { matchCase, wholeWords } = {} ) {
		const { editor } = this;
		const { model } = editor;

		let findCallback;

		// Allow to execute `find()` on a plugin with a keyword only.
		if ( typeof callbackOrText === 'string' ) {
			findCallback = findByTextCallback( callbackOrText, { matchCase, wholeWords } );

			this._state.searchText = callbackOrText;
		} else {
			findCallback = callbackOrText;
		}

		// Initial search is done on all nodes in all roots inside the content.
		const results = model.document.getRootNames()
			.reduce( ( ( currentResults, rootName ) => updateFindResultFromRange(
				model.createRangeIn( model.document.getRoot( rootName ) ),
				model,
				findCallback,
				currentResults
			) ), null );

		this._state.clear( model );
		this._state.results.addMany( Array.from( results ) );
		this._state.highlightedResult = results.get( 0 );

		if ( typeof callbackOrText === 'string' ) {
			this._state.searchText = callbackOrText;
		}

		this._state.matchCase = !!matchCase;
		this._state.matchWholeWords = !!wholeWords;

		return {
			results,
			findCallback
		};
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/replacecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/replacecommand
*/



/**
 * The replace command. It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
 *
 * @extends module:core/command~Command
 */
class ReplaceCommand extends command_Command {
	/**
	 * Creates a new `ReplaceCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor Editor on which this command will be used.
	 * @param {module:find-and-replace/findandreplacestate~FindAndReplaceState} state An object to hold plugin state.
	 */
	constructor( editor, state ) {
		super( editor );

		// The replace command is always enabled.
		this.isEnabled = true;

		/**
		 * The find and replace state object used for command operations.
		 *
		 * @protected
		 * @member {module:find-and-replace/findandreplacestate~FindAndReplaceState} #_state
		 */
		this._state = state;
	}

	/**
	 * Replace a given find result by a string or a callback.
	 *
	 * @param {String} replacementText
	 * @param {Object} result A single result from the find command.
	 *
	 * @fires module:core/command~Command#event:execute
	 */
	execute( replacementText, result ) {
		const { model } = this.editor;

		model.change( writer => {
			const range = result.marker.getRange();

			// Don't replace a result (marker) that found its way into the $graveyard (e.g. removed by collaborators).
			if ( range.root.rootName === '$graveyard' ) {
				this._state.results.remove( result );

				return;
			}

			let textAttributes = {};

			for ( const item of range.getItems() ) {
				if ( item.is( '$text' ) || item.is( '$textProxy' ) ) {
					textAttributes = item.getAttributes();
					break;
				}
			}

			model.insertContent( writer.createText( replacementText, textAttributes ), range );

			if ( this._state.results.has( result ) ) {
				this._state.results.remove( result );
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/replaceallcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/replaceallcommand
 */





/**
 * The replace all command. It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
 *
 * @extends module:find-and-replace/replacecommand~ReplaceCommand
 */
class ReplaceAllCommand extends ReplaceCommand {
	/**
	 * Replaces all the occurrences of `textToReplace` with a given `newText` string.
	 *
	 * ```js
	 *	replaceAllCommand.execute( 'replaceAll', 'new text replacement', 'text to replace' );
	 * ```
	 *
	 * Alternatively you can call it from editor instance:
	 *
	 * ```js
	 *	editor.execute( 'replaceAll', 'new text', 'old text' );
	 * ```
	 *
	 * @param {String} newText Text that will be inserted to the editor for each match.
	 * @param {String|module:utils/collection~Collection} textToReplace Text to be replaced or a collection of matches
	 * as returned by the find command.
	 *
	 * @fires module:core/command~Command#event:execute
	 */
	execute( newText, textToReplace ) {
		const { editor } = this;
		const { model } = editor;

		const results = textToReplace instanceof Collection ?
			textToReplace : model.document.getRootNames()
				.reduce( ( ( currentResults, rootName ) => updateFindResultFromRange(
					model.createRangeIn( model.document.getRoot( rootName ) ),
					model,
					findByTextCallback( textToReplace, this._state ),
					currentResults
				) ), null );

		if ( results.length ) {
			model.change( () => {
				[ ...results ].forEach( searchResult => {
					// Just reuse logic from the replace command to replace a single match.
					super.execute( newText, searchResult );
				} );
			} );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/findnextcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/findnextcommand
*/



/**
 * The find next command. Moves the highlight to the next search result.
 *
 * It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
 *
 * @extends module:core/command~Command
 */
class FindNextCommand extends command_Command {
	/**
	 * Creates a new `FindNextCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor on which this command will be used.
	 * @param {module:find-and-replace/findandreplacestate~FindAndReplaceState} state An object to hold plugin state.
	 */
	constructor( editor, state ) {
		super( editor );

		// It does not affect data so should be enabled in read-only mode.
		this.affectsData = false;

		/**
		 * The find and replace state object used for command operations.
		 *
		 * @protected
		 * @member {module:find-and-replace/findandreplacestate~FindAndReplaceState} #_state
		 */
		this._state = state;

		this.isEnabled = false;

		this.listenTo( this._state.results, 'change', () => {
			this.isEnabled = this._state.results.length > 1;
		} );
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.isEnabled = this._state.results.length > 1;
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const results = this._state.results;
		const currentIndex = results.getIndex( this._state.highlightedResult );
		const nextIndex = currentIndex + 1 >= results.length ?
			0 : currentIndex + 1;

		this._state.highlightedResult = this._state.results.get( nextIndex );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/findpreviouscommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/findpreviouscommand
*/



/**
 * The find previous command. Moves the highlight to the previous search result.
 *
 * It is used by the {@link module:find-and-replace/findandreplace~FindAndReplace find and replace feature}.
 *
 * @extends module:find-and-replace/findnextcommand~FindNextCommand
 */
class FindPreviousCommand extends FindNextCommand {
	/**
	 * @inheritDoc
	 */
	execute() {
		const results = this._state.results;
		const currentIndex = results.getIndex( this._state.highlightedResult );
		const previousIndex = currentIndex - 1 < 0 ?
			this._state.results.length - 1 : currentIndex - 1;

		this._state.highlightedResult = this._state.results.get( previousIndex );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/findandreplacestate.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/findandreplacestate
 */



/**
 * The object storing find and replace plugin state for a given editor instance.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class FindAndReplaceState {
	/**
	 * Creates an instance of the state.
	 *
	 * @param {module:engine/model/model~Model} model
	 */
	constructor( model ) {
		/**
		 * A collection of find matches.
		 *
		 * @protected
		 * @observable
		 * @member {module:utils/collection~Collection} #results
		 */
		this.set( 'results', new Collection() );

		/**
		 * Currently highlighted search result in {@link #results matched results}.
		 *
		 * @readonly
		 * @observable
		 * @member {Object|null} #highlightedResult
		 */
		this.set( 'highlightedResult', null );

		/**
		 * Searched text value.
		 *
		 * @readonly
		 * @observable
		 * @member {String} #searchText
		 */
		this.set( 'searchText', '' );

		/**
		 * Replace text value.
		 *
		 * @readonly
		 * @observable
		 * @member {String} #replaceText
		 */
		this.set( 'replaceText', '' );

		/**
		 * Indicates whether the matchCase checkbox has been checked.
		 *
		 * @readonly
		 * @observable
		 * @member {Boolean} #matchCase
		 */
		this.set( 'matchCase', false );

		/**
		 * Indicates whether the matchWholeWords checkbox has been checked.
		 *
		 * @readonly
		 * @observable
		 * @member {Boolean} #matchWholeWords
		 */
		this.set( 'matchWholeWords', false );

		this.results.on( 'change', ( eventInfo, { removed, index } ) => {
			removed = Array.from( removed );

			if ( removed.length ) {
				let highlightedResultRemoved = false;

				model.change( writer => {
					for ( const removedResult of removed ) {
						if ( this.highlightedResult === removedResult ) {
							highlightedResultRemoved = true;
						}

						if ( model.markers.has( removedResult.marker.name ) ) {
							writer.removeMarker( removedResult.marker );
						}
					}
				} );

				if ( highlightedResultRemoved ) {
					const nextHighlightedIndex = index >= this.results.length ? 0 : index;
					this.highlightedResult = this.results.get( nextHighlightedIndex );
				}
			}
		} );
	}

	/**
	 * Cleans the state up and removes markers from the model.
	 *
	 * @param {module:engine/model/model~Model} model
	 */
	clear( model ) {
		this.searchText = '';

		model.change( writer => {
			if ( this.highlightedResult ) {
				const oldMatchId = this.highlightedResult.marker.name.split( ':' )[ 1 ];
				const oldMarker = model.markers.get( `findResultHighlighted:${ oldMatchId }` );

				if ( oldMarker ) {
					writer.removeMarker( oldMarker );
				}
			}

			[ ...this.results ].forEach( ( { marker } ) => {
				writer.removeMarker( marker );
			} );
		} );

		this.results.clear();
	}
}

mix( FindAndReplaceState, ObservableMixin );

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-find-and-replace/theme/findandreplace.css
var findandreplace = __webpack_require__(5436);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/theme/findandreplace.css

            

var findandreplace_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

findandreplace_options.insert = "head";
findandreplace_options.singleton = true;

var findandreplace_update = injectStylesIntoStyleTag_default()(findandreplace/* default */.Z, findandreplace_options);



/* harmony default export */ const theme_findandreplace = (findandreplace/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/findandreplaceediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/findandreplaceediting
 */










// eslint-disable-next-line ckeditor5-rules/ckeditor-imports






const findandreplaceediting_HIGHLIGHT_CLASS = 'ck-find-result_selected';

// Reacts to document changes in order to update search list.
function onDocumentChange( results, model, searchCallback ) {
	const changedNodes = new Set();
	const removedMarkers = new Set();

	const changes = model.document.differ.getChanges();

	// Get nodes in which changes happened to re-run a search callback on them.
	changes.forEach( change => {
		if ( change.name === '$text' || model.schema.isInline( change.position.nodeAfter ) ) {
			changedNodes.add( change.position.parent );

			[ ...model.markers.getMarkersAtPosition( change.position ) ].forEach( markerAtChange => {
				removedMarkers.add( markerAtChange.name );
			} );
		} else if ( change.type === 'insert' ) {
			changedNodes.add( change.position.nodeAfter );
		}
	} );

	// Get markers from removed nodes also.
	model.document.differ.getChangedMarkers().forEach( ( { name, data: { newRange } } ) => {
		if ( newRange && newRange.start.root.rootName === '$graveyard' ) {
			removedMarkers.add( name );
		}
	} );

	// Get markers from the updated nodes and remove all (search will be re-run on these nodes).
	changedNodes.forEach( node => {
		const markersInNode = [ ...model.markers.getMarkersIntersectingRange( model.createRangeIn( node ) ) ];

		markersInNode.forEach( marker => removedMarkers.add( marker.name ) );
	} );

	// Remove results & markers from the changed part of content.
	model.change( writer => {
		removedMarkers.forEach( markerName => {
			// Remove the result first - in order to prevent rendering a removed marker.
			if ( results.has( markerName ) ) {
				results.remove( markerName );
			}

			writer.removeMarker( markerName );
		} );
	} );

	// Run search callback again on updated nodes.
	changedNodes.forEach( nodeToCheck => {
		updateFindResultFromRange( model.createRangeOn( nodeToCheck ), model, searchCallback, results );
	} );
}

/**
 * Implements the editing part for find and replace plugin. For example conversion, commands etc.
 *
 * @extends module:core/plugin~Plugin
 */
class FindAndReplaceEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FindAndReplaceEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		/**
		 * The collection of currently highlighted search results.
		 *
		 * @private
		 * @member {module:utils/collection~Collection} #_activeResults
		 */
		this._activeResults = null;

		/**
		 * An object storing the find and replace state within a given editor instance.
		 *
		 * @member {module:find-and-replace/findandreplacestate~FindAndReplaceState} #state
		 */
		this.state = new FindAndReplaceState( this.editor.model );

		this._defineConverters();
		this._defineCommands();

		this.listenTo( this.state, 'change:highlightedResult', ( eventInfo, name, newValue, oldValue ) => {
			const { model } = this.editor;

			model.change( writer => {
				if ( oldValue ) {
					const oldMatchId = oldValue.marker.name.split( ':' )[ 1 ];
					const oldMarker = model.markers.get( `findResultHighlighted:${ oldMatchId }` );

					if ( oldMarker ) {
						writer.removeMarker( oldMarker );
					}
				}

				if ( newValue ) {
					const newMatchId = newValue.marker.name.split( ':' )[ 1 ];
					writer.addMarker( `findResultHighlighted:${ newMatchId }`, {
						usingOperation: false,
						affectsData: false,
						range: newValue.marker.getRange()
					} );
				}
			} );
		} );

		const debouncedScrollListener = lodash_es_debounce( scrollToHighlightedResult.bind( this ), 32 );
		// Debounce scroll as highlight might be changed very frequently, e.g. when there's a replace all command.
		this.listenTo( this.state, 'change:highlightedResult', debouncedScrollListener, { priority: 'low' } );

		// It's possible that the editor will get destroyed before debounced call kicks in.
		// This would result with accessing a view three that is no longer in DOM.
		this.listenTo( this.editor, 'destroy', debouncedScrollListener.cancel );

		/* istanbul ignore next */
		function scrollToHighlightedResult( eventInfo, name, newValue ) {
			if ( newValue ) {
				const domConverter = this.editor.editing.view.domConverter;
				const viewRange = this.editor.editing.mapper.toViewRange( newValue.marker.getRange() );

				scrollViewportToShowTarget( {
					target: domConverter.viewRangeToDom( viewRange ),
					viewportOffset: 40
				} );
			}
		}
	}

	/**
	 * Initiate a search.
	 *
	 * @param {Function|String} callbackOrText
	 * @returns {module:utils/collection~Collection}
	 */
	find( callbackOrText ) {
		const { editor } = this;
		const { model } = editor;

		const { findCallback, results } = editor.execute( 'find', callbackOrText );

		this._activeResults = results;

		// @todo: handle this listener, another copy is in findcommand.js file.
		this.listenTo( model.document, 'change:data', () => onDocumentChange( this._activeResults, model, findCallback ) );

		return this._activeResults;
	}

	/**
	 * Stops active results from updating, and clears out the results.
	 */
	stop() {
		if ( !this._activeResults ) {
			return;
		}

		this.stopListening( this.editor.model.document );

		this.state.clear( this.editor.model );

		this._activeResults = null;
	}

	/**
	 * Sets up the commands.
	 *
	 * @private
	 */
	_defineCommands() {
		this.editor.commands.add( 'find', new FindCommand( this.editor, this.state ) );
		this.editor.commands.add( 'findNext', new FindNextCommand( this.editor, this.state ) );
		this.editor.commands.add( 'findPrevious', new FindPreviousCommand( this.editor, this.state ) );
		this.editor.commands.add( 'replace', new ReplaceCommand( this.editor, this.state ) );
		this.editor.commands.add( 'replaceAll', new ReplaceAllCommand( this.editor, this.state ) );
	}

	/**
	 * Sets up the marker downcast converters for search results highlighting.
	 *
	 * @private
	 */
	_defineConverters() {
		const { editor } = this;

		// Setup the marker highlighting conversion.
		editor.conversion.for( 'editingDowncast' ).markerToHighlight( {
			model: 'findResult',
			view: ( { markerName } ) => {
				const [ , id ] = markerName.split( ':' );

				// Marker removal from the view has a bug: https://github.com/ckeditor/ckeditor5/issues/7499
				// A minimal option is to return a new object for each converted marker...
				return {
					name: 'span',
					classes: [ 'ck-find-result' ],
					attributes: {
						// ...however, adding a unique attribute should be future-proof..
						'data-find-result': id
					}
				};
			}
		} );

		editor.conversion.for( 'editingDowncast' ).markerToHighlight( {
			model: 'findResultHighlighted',
			view: ( { markerName } ) => {
				const [ , id ] = markerName.split( ':' );

				// Marker removal from the view has a bug: https://github.com/ckeditor/ckeditor5/issues/7499
				// A minimal option is to return a new object for each converted marker...
				return {
					name: 'span',
					classes: [ findandreplaceediting_HIGHLIGHT_CLASS ],
					attributes: {
						// ...however, adding a unique attribute should be future-proof..
						'data-find-result': id
					}
				};
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-find-and-replace/src/findandreplace.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module find-and-replace/findandreplace
 */





/**
 * The find and replace plugin.
 *
 * For a detailed overview, check the {@glink features/find-and-replace Find and replace feature documentation}.
 *
 * This is a "glue" plugin which loads the following plugins:
 *
 * * The {@link module:find-and-replace/findandreplaceediting~FindAndReplaceEditing find and replace editing feature},
 * * The {@link module:find-and-replace/findandreplaceui~FindAndReplaceUI find and replace UI feature}
 *
 * @extends module:core/plugin~Plugin
 */
class FindAndReplace extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ FindAndReplaceEditing, FindAndReplaceUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FindAndReplace';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const ui = this.editor.plugins.get( 'FindAndReplaceUI' );
		const findAndReplaceEditing = this.editor.plugins.get( 'FindAndReplaceEditing' );
		const state = findAndReplaceEditing.state;

		ui.on( 'findNext', ( event, data ) => {
			// Data is contained only for the "find" button.
			if ( data ) {
				state.searchText = data.searchText;
				this.editor.execute( 'find', data.searchText, data );
			} else {
				// Find next arrow button press.
				this.editor.execute( 'findNext' );
			}
		} );

		ui.on( 'findPrevious', ( event, data ) => {
			if ( data && state.searchText !== data.searchText ) {
				this.editor.execute( 'find', data.searchText );
			} else {
				// Subsequent calls.
				this.editor.execute( 'findPrevious' );
			}
		} );

		ui.on( 'replace', ( event, data ) => {
			if ( state.searchText !== data.searchText ) {
				this.editor.execute( 'find', data.searchText );
			}

			const highlightedResult = state.highlightedResult;

			if ( highlightedResult ) {
				this.editor.execute( 'replace', data.replaceText, highlightedResult );
			}
		} );

		ui.on( 'replaceAll', ( event, data ) => {
			// The state hadn't been yet built for this search text.
			if ( state.searchText !== data.searchText ) {
				this.editor.execute( 'find', data.searchText );
			}

			this.editor.execute( 'replaceAll', data.replaceText, state.results );
		} );

		// Reset the state when the user invalidated last search results, for instance,
		// by starting typing another search query or changing options.
		ui.on( 'searchReseted', () => {
			state.clear( this.editor.model );
			findAndReplaceEditing.stop();
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontcommand
 */



/**
 * The base font command.
 *
 * @extends module:core/command~Command
 */
class FontCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor Editor instance.
	 * @param {String} attributeKey The name of a model attribute on which this command operates.
	 */
	constructor( editor, attributeKey ) {
		super( editor );

		/**
		 * When set, it reflects the {@link #attributeKey} value of the selection.
		 *
		 * @observable
		 * @readonly
		 * @member {String} module:font/fontcommand~FontCommand#value
		 */

		/**
		 * A model attribute on which this command operates.
		 *
		 * @readonly
		 * @member {Boolean} module:font/fontcommand~FontCommand#attributeKey
		 */
		this.attributeKey = attributeKey;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const doc = model.document;

		this.value = doc.selection.getAttribute( this.attributeKey );
		this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, this.attributeKey );
	}

	/**
	 * Executes the command. Applies the `value` of the {@link #attributeKey} to the selection.
	 * If no `value` is passed, it removes the attribute from the selection.
	 *
	 * @protected
	 * @param {Object} [options] Options for the executed command.
	 * @param {String} [options.value] The value to apply.
	 * @fires execute
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;
		const selection = document.selection;

		const value = options.value;

		model.change( writer => {
			if ( selection.isCollapsed ) {
				if ( value ) {
					writer.setSelectionAttribute( this.attributeKey, value );
				} else {
					writer.removeSelectionAttribute( this.attributeKey );
				}
			} else {
				const ranges = model.schema.getValidRanges( selection.getRanges(), this.attributeKey );

				for ( const range of ranges ) {
					if ( value ) {
						writer.setAttribute( this.attributeKey, value, range );
					} else {
						writer.removeAttribute( this.attributeKey, range );
					}
				}
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/documentcolorcollection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/documentcolorcollection
 */



/**
 * A collection to store document colors. It enforces colors to be unique.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 * @extends module:utils/collection~Collection
 */
class DocumentColorCollection extends Collection {
	constructor( options ) {
		super( options );

		/**
		 * Indicates whether the document color collection is empty.
		 *
		 * @observable
		 * @readonly
		 * @member {Boolean} #isEmpty
		 */
		this.set( 'isEmpty', true );

		this.on( 'change', () => {
			this.set( 'isEmpty', this.length === 0 );
		} );
	}

	/**
	 * Adds a color to the document color collection.
	 *
	 * This method ensures that no color duplicates are inserted (compared using
	 * the color value of the {@link module:ui/colorgrid/colorgrid~ColorDefinition}).
	 *
	 * If the item does not have an ID, it will be automatically generated and set on the item.
	 *
	 * @chainable
	 * @param {module:ui/colorgrid/colorgrid~ColorDefinition} item
	 * @param {Number} [index] The position of the item in the collection. The item
	 * is pushed to the collection when `index` is not specified.
	 * @fires add
	 * @fires change
	 */
	add( item, index ) {
		if ( this.find( element => element.color === item.color ) ) {
			// No duplicates are allowed.
			return;
		}

		super.add( item, index );
	}

	/**
	 * Checks if an object with given colors is present in the document color collection.
	 *
	 * @param {String} color
	 * @returns {Boolean}
	 */
	hasColor( color ) {
		return !!this.find( item => item.color === color );
	}
}

mix( DocumentColorCollection, ObservableMixin );

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-font/theme/fontcolor.css
var fontcolor = __webpack_require__(2585);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/theme/fontcolor.css

            

var fontcolor_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

fontcolor_options.insert = "head";
fontcolor_options.singleton = true;

var fontcolor_update = injectStylesIntoStyleTag_default()(fontcolor/* default */.Z, fontcolor_options);



/* harmony default export */ const theme_fontcolor = (fontcolor/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/ui/colortableview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/ui/colortableview
 */









/**
 * A class which represents a view with the following sub–components:
 *
 * * A remove color button,
 * * A static {@link module:ui/colorgrid/colorgrid~ColorGridView} of colors defined in the configuration,
 * * A dynamic {@link module:ui/colorgrid/colorgrid~ColorGridView} of colors used in the document.
 *
 * @extends module:ui/view~View
 */
class ColorTableView extends src_view_View {
	/**
	 * Creates a view to be inserted as a child of {@link module:ui/dropdown/dropdownview~DropdownView}.
	 *
	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
	 * @param {Object} config The configuration object.
	 * @param {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>} config.colors An array with definitions of colors to
	 * be displayed in the table.
	 * @param {Number} config.columns The number of columns in the color grid.
	 * @param {String} config.removeButtonLabel The label of the button responsible for removing the color.
	 * @param {String} config.documentColorsLabel The label for the section with the document colors.
	 * @param {Number} config.documentColorsCount The number of colors in the document colors section inside the color dropdown.
	 */
	constructor( locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount } ) {
		super( locale );

		/**
		 * A collection of the children of the table.
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.items = this.createCollection();

		/**
		 * An array with objects representing colors to be displayed in the grid.
		 *
		 * @type {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>}
		 */
		this.colorDefinitions = colors;

		/**
		 * Tracks information about the DOM focus in the list.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * Keeps the value of the command associated with the table for the current selection.
		 *
		 * @type {String}
		 */
		this.set( 'selectedColor' );

		/**
		 * The label of the button responsible for removing color attributes.
		 *
		 * @type {String}
		 */
		this.removeButtonLabel = removeButtonLabel;

		/**
		 * The number of columns in the color grid.
		 *
		 * @type {Number}
		 */
		this.columns = columns;

		/**
		 * A collection of definitions that store the document colors.
		 *
		 * @readonly
		 * @member {module:font/documentcolorcollection~DocumentColorCollection}
		 */
		this.documentColors = new DocumentColorCollection();

		/**
		 * The maximum number of colors in the document colors section.
		 * If it equals 0, the document colors section is not added.
		 *
		 * @readonly
		 * @type {Number}
		 */
		this.documentColorsCount = documentColorsCount;

		/**
		 * A collection of views that can be focused in the view.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Preserves the reference to {@link module:ui/colorgrid/colorgrid~ColorGridView} used to create
		 * the default (static) color set.
		 *
		 * The property is loaded once the the parent dropdown is opened the first time.
		 *
		 * @readonly
		 * @member {module:ui/colorgrid/colorgrid~ColorGridView|undefined} #staticColorsGrid
		 */

		/**
		 * Preserves the reference to {@link module:ui/colorgrid/colorgrid~ColorGridView} used to create
		 * the document colors. It remains undefined if the document colors feature is disabled.
		 *
		 * The property is loaded once the the parent dropdown is opened the first time.
		 *
		 * @readonly
		 * @member {module:ui/colorgrid/colorgrid~ColorGridView|undefined} #documentColorsGrid
		 */

		/**
		 * Helps cycling over focusable {@link #items} in the list.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate list items backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke.
				focusPrevious: 'shift + tab',

				// Navigate list items forwards using the <kbd>Tab</kbd> key.
				focusNext: 'tab'
			}
		} );

		/**
		 * Document color section's label.
		 *
		 * @private
		 * @readonly
		 * @type {String}
		 */
		this._documentColorsLabel = documentColorsLabel;

		this.setTemplate( {
			tag: 'div',
			attributes: {
				class: [
					'ck',
					'ck-color-table'
				]
			},
			children: this.items
		} );

		this.items.add( this._createRemoveColorButton() );
	}

	/**
	 * Scans through the editor model and searches for text node attributes with the given attribute name.
	 * Found entries are set as document colors.
	 *
	 * All the previously stored document colors will be lost in the process.
	 *
	 * @param {module:engine/model/model~Model} model The model used as a source to obtain the document colors.
	 * @param {String} attributeName Determines the name of the related model's attribute for a given dropdown.
	 */
	updateDocumentColors( model, attributeName ) {
		const document = model.document;
		const maxCount = this.documentColorsCount;

		this.documentColors.clear();

		for ( const rootName of document.getRootNames() ) {
			const root = document.getRoot( rootName );
			const range = model.createRangeIn( root );

			for ( const node of range.getItems() ) {
				if ( node.is( '$textProxy' ) && node.hasAttribute( attributeName ) ) {
					this._addColorToDocumentColors( node.getAttribute( attributeName ) );

					if ( this.documentColors.length >= maxCount ) {
						return;
					}
				}
			}
		}
	}

	/**
	 * Refreshes the state of the selected color in one or both {@link module:ui/colorgrid/colorgrid~ColorGridView}s
	 * available in the {@link module:font/ui/colortableview~ColorTableView}. It guarantees that the selection will occur only in one
	 * of them.
	 */
	updateSelectedColors() {
		const documentColorsGrid = this.documentColorsGrid;
		const staticColorsGrid = this.staticColorsGrid;
		const selectedColor = this.selectedColor;

		staticColorsGrid.selectedColor = selectedColor;

		if ( documentColorsGrid ) {
			documentColorsGrid.selectedColor = selectedColor;
		}
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		// Start listening for the keystrokes coming from #element.
		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Appends {@link #staticColorsGrid} and {@link #documentColorsGrid} views.
	 */
	appendGrids() {
		if ( this.staticColorsGrid ) {
			return;
		}

		this.staticColorsGrid = this._createStaticColorsGrid();

		this.items.add( this.staticColorsGrid );
		this.focusTracker.add( this.staticColorsGrid.element );
		this._focusables.add( this.staticColorsGrid );

		if ( this.documentColorsCount ) {
			// Create a label for document colors.
			const bind = Template.bind( this.documentColors, this.documentColors );
			const label = new LabelView( this.locale );
			label.text = this._documentColorsLabel;
			label.extendTemplate( {
				attributes: {
					class: [
						'ck',
						'ck-color-grid__label',
						bind.if( 'isEmpty', 'ck-hidden' )
					]
				}
			} );
			this.items.add( label );
			this.documentColorsGrid = this._createDocumentColorsGrid();

			this.items.add( this.documentColorsGrid );
			this.focusTracker.add( this.documentColorsGrid.element );
			this._focusables.add( this.documentColorsGrid );
		}
	}

	/**
	 * Focuses the first focusable element in {@link #items}.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}

	/**
	 * Focuses the last focusable element in {@link #items}.
	 */
	focusLast() {
		this._focusCycler.focusLast();
	}

	/**
	 * Adds the remove color button as a child of the current view.
	 *
	 * @private
	 * @returns {module:ui/button/buttonview~ButtonView}
	 */
	_createRemoveColorButton() {
		const buttonView = new buttonview_ButtonView();

		buttonView.set( {
			withText: true,
			icon: icons.eraser,
			label: this.removeButtonLabel
		} );

		buttonView.class = 'ck-color-table__remove-color';
		buttonView.on( 'execute', () => {
			this.fire( 'execute', { value: null } );
		} );

		buttonView.render();

		this.focusTracker.add( buttonView.element );
		this._focusables.add( buttonView );

		return buttonView;
	}

	/**
	 * Creates a static color table grid based on the editor configuration.
	 *
	 * @private
	 * @returns {module:ui/colorgrid/colorgrid~ColorGridView}
	 */
	_createStaticColorsGrid() {
		const colorGrid = new ColorGridView( this.locale, {
			colorDefinitions: this.colorDefinitions,
			columns: this.columns
		} );

		colorGrid.delegate( 'execute' ).to( this );

		return colorGrid;
	}

	/**
	 * Creates the document colors section view and binds it to {@link #documentColors}.
	 *
	 * @private
	 * @returns {module:ui/colorgrid/colorgrid~ColorGridView}
	 */
	_createDocumentColorsGrid() {
		const bind = Template.bind( this.documentColors, this.documentColors );
		const documentColorsGrid = new ColorGridView( this.locale, {
			columns: this.columns
		} );

		documentColorsGrid.delegate( 'execute' ).to( this );

		documentColorsGrid.extendTemplate( {
			attributes: {
				class: bind.if( 'isEmpty', 'ck-hidden' )
			}
		} );

		documentColorsGrid.items.bindTo( this.documentColors ).using(
			colorObj => {
				const colorTile = new ColorTileView();

				colorTile.set( {
					color: colorObj.color,
					hasBorder: colorObj.options && colorObj.options.hasBorder
				} );

				if ( colorObj.label ) {
					colorTile.set( {
						label: colorObj.label,
						tooltip: true
					} );
				}

				colorTile.on( 'execute', () => {
					this.fire( 'execute', {
						value: colorObj.color
					} );
				} );

				return colorTile;
			}
		);

		// Selected color should be cleared when document colors became empty.
		this.documentColors.on( 'change:isEmpty', ( evt, name, val ) => {
			if ( val ) {
				documentColorsGrid.selectedColor = null;
			}
		} );

		return documentColorsGrid;
	}

	/**
	 * Adds a given color to the document colors list. If possible, the method will attempt to use
	 * data from the {@link #colorDefinitions} (label, color options).
	 *
	 * @private
	 * @param {String} color A string that stores the value of the recently applied color.
	 */
	_addColorToDocumentColors( color ) {
		const predefinedColor = this.colorDefinitions
			.find( definition => definition.color === color );

		if ( !predefinedColor ) {
			this.documentColors.add( {
				color,
				label: color,
				options: {
					hasBorder: false
				}
			} );
		} else {
			this.documentColors.add( Object.assign( {}, predefinedColor ) );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/utils
 */



/**
 * The name of the font size plugin.
 */
const FONT_SIZE = 'fontSize';

/**
 * The name of the font family plugin.
 */
const FONT_FAMILY = 'fontFamily';

/**
 * The name of the font color plugin.
 */
const FONT_COLOR = 'fontColor';

/**
 * The name of the font background color plugin.
 */
const FONT_BACKGROUND_COLOR = 'fontBackgroundColor';

/**
 * Builds a proper {@link module:engine/conversion/conversion~ConverterDefinition converter definition} out of input data.
 *
 * @param {String} modelAttributeKey Key
 * @param {Array.<module:font/fontfamily~FontFamilyOption>|Array.<module:font/fontsize~FontSizeOption>} options
 * @returns {module:engine/conversion/conversion~ConverterDefinition}
 */
function buildDefinition( modelAttributeKey, options ) {
	const definition = {
		model: {
			key: modelAttributeKey,
			values: []
		},
		view: {},
		upcastAlso: {}
	};

	for ( const option of options ) {
		definition.model.values.push( option.model );
		definition.view[ option.model ] = option.view;

		if ( option.upcastAlso ) {
			definition.upcastAlso[ option.model ] = option.upcastAlso;
		}
	}

	return definition;
}

/**
 * A {@link module:font/fontcolor~FontColor font color} and
 * {@link module:font/fontbackgroundcolor~FontBackgroundColor font background color} helper
 * responsible for upcasting data to the model.
 *
 * **Note**: The `styleAttr` parameter should be either `'color'` or `'background-color'`.
 *
 * @param {String} styleAttr
 * @return {String}
 */
function renderUpcastAttribute( styleAttr ) {
	return viewElement => normalizeColorCode( viewElement.getStyle( styleAttr ) );
}

/**
 * A {@link module:font/fontcolor~FontColor font color} and
 * {@link module:font/fontbackgroundcolor~FontBackgroundColor font background color} helper
 * responsible for downcasting a color attribute to a `<span>` element.
 *
 * **Note**: The `styleAttr` parameter should be either `'color'` or `'background-color'`.
 *
 * @param {String} styleAttr
 */
function renderDowncastElement( styleAttr ) {
	return ( modelAttributeValue, { writer } ) => writer.createAttributeElement( 'span', {
		style: `${ styleAttr }:${ modelAttributeValue }`
	}, { priority: 7 } );
}

/**
 * A helper that adds {@link module:font/ui/colortableview~ColorTableView} to the color dropdown with proper initial values.
 *
 * @param {Object} config The configuration object.
 * @param {module:ui/dropdown/dropdownview~DropdownView} config.dropdownView The dropdown view to which
 * a {@link module:font/ui/colortableview~ColorTableView} will be added.
 * @param {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>} config.colors An array with definitions
 * representing colors to be displayed in the color table.
 * @param {String} config.removeButtonLabel The label for the button responsible for removing the color.
 * @param {String} config.documentColorsLabel The label for the section with document colors.
 * @param {String} config.documentColorsCount The number of document colors inside the dropdown.
 * @returns {module:font/ui/colortableview~ColorTableView} The new color table view.
 */
function addColorTableToDropdown( { dropdownView, colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount } ) {
	const locale = dropdownView.locale;
	const colorTableView = new ColorTableView( locale, { colors, columns, removeButtonLabel, documentColorsLabel, documentColorsCount } );

	dropdownView.colorTableView = colorTableView;
	dropdownView.panelView.children.add( colorTableView );

	colorTableView.delegate( 'execute' ).to( dropdownView, 'execute' );

	return colorTableView;
}

// Fixes the color value string.
//
// @param {String} value
// @returns {String}
function normalizeColorCode( value ) {
	return value.replace( /\s/g, '' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontbackgroundcolor/fontbackgroundcolorcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontbackgroundcolor/fontbackgroundcolorcommand
 */




/**
 * The font background color command. It is used by
 * {@link module:font/fontbackgroundcolor/fontbackgroundcolorediting~FontBackgroundColorEditing}
 * to apply the font background color.
 *
 *		editor.execute( 'fontBackgroundColor', { value: 'rgb(250, 20, 20)' } );
 *
 * **Note**: Executing the command with the `null` value removes the attribute from the model.
 *
 * @extends module:font/fontcommand~FontCommand
 */
class FontBackgroundColorCommand extends FontCommand {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor, FONT_BACKGROUND_COLOR );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontbackgroundcolor/fontbackgroundcolorediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontbackgroundcolor/fontbackgroundcolorediting
 */







/**
 * The font background color editing feature.
 *
 * It introduces the {@link module:font/fontbackgroundcolor/fontbackgroundcolorcommand~FontBackgroundColorCommand command} and
 * the `fontBackgroundColor` attribute in the {@link module:engine/model/model~Model model} which renders
 * in the {@link module:engine/view/view view} as a `<span>` element (`<span style="background-color: ...">`),
 * depending on the {@link module:font/fontbackgroundcolor~FontBackgroundColorConfig configuration}.
 *
 * @extends module:core/plugin~Plugin
 */
class FontBackgroundColorEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontBackgroundColorEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( FONT_BACKGROUND_COLOR, {
			colors: [
				{
					color: 'hsl(0, 0%, 0%)',
					label: 'Black'
				},
				{
					color: 'hsl(0, 0%, 30%)',
					label: 'Dim grey'
				},
				{
					color: 'hsl(0, 0%, 60%)',
					label: 'Grey'
				},
				{
					color: 'hsl(0, 0%, 90%)',
					label: 'Light grey'
				},
				{
					color: 'hsl(0, 0%, 100%)',
					label: 'White',
					hasBorder: true
				},
				{
					color: 'hsl(0, 75%, 60%)',
					label: 'Red'
				},
				{
					color: 'hsl(30, 75%, 60%)',
					label: 'Orange'
				},
				{
					color: 'hsl(60, 75%, 60%)',
					label: 'Yellow'
				},
				{
					color: 'hsl(90, 75%, 60%)',
					label: 'Light green'
				},
				{
					color: 'hsl(120, 75%, 60%)',
					label: 'Green'
				},
				{
					color: 'hsl(150, 75%, 60%)',
					label: 'Aquamarine'
				},
				{
					color: 'hsl(180, 75%, 60%)',
					label: 'Turquoise'
				},
				{
					color: 'hsl(210, 75%, 60%)',
					label: 'Light blue'
				},
				{
					color: 'hsl(240, 75%, 60%)',
					label: 'Blue'
				},
				{
					color: 'hsl(270, 75%, 60%)',
					label: 'Purple'
				}
			],
			columns: 5
		} );

		editor.data.addStyleProcessorRules( addBackgroundRules );
		editor.conversion.for( 'upcast' ).elementToAttribute( {
			view: {
				name: 'span',
				styles: {
					'background-color': /[\s\S]+/
				}
			},
			model: {
				key: FONT_BACKGROUND_COLOR,
				value: renderUpcastAttribute( 'background-color' )
			}
		} );

		editor.conversion.for( 'downcast' ).attributeToElement( {
			model: FONT_BACKGROUND_COLOR,
			view: renderDowncastElement( 'background-color' )
		} );

		editor.commands.add( FONT_BACKGROUND_COLOR, new FontBackgroundColorCommand( editor ) );

		// Allow the font backgroundColor attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: FONT_BACKGROUND_COLOR } );

		editor.model.schema.setAttributeProperties( FONT_BACKGROUND_COLOR, {
			isFormatting: true,
			copyOnEnter: true
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/ui/colorui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/ui/colorui
 */






/**
 * The color UI plugin which isolates the common logic responsible for displaying dropdowns with color grids.
 *
 * It is used to create the `'fontBackgroundColor'` and `'fontColor'` dropdowns, each hosting
 * a {@link module:font/ui/colortableview~ColorTableView}.
 *
 * @extends module:core/plugin~Plugin
 */
class ColorUI extends plugin_Plugin {
	/**
	 * Creates a plugin which introduces a dropdown with a pre–configured {@link module:font/ui/colortableview~ColorTableView}.
	 *
	 * @param {module:core/editor/editor~Editor} editor
	 * @param {Object} config The configuration object.
	 * @param {String} config.commandName The name of the command which will be executed when a color tile is clicked.
	 * @param {String} config.componentName The name of the dropdown in the {@link module:ui/componentfactory~ComponentFactory}
	 * and the configuration scope name in `editor.config`.
	 * @param {String} config.icon The SVG icon used by the dropdown.
	 * @param {String} config.dropdownLabel The label used by the dropdown.
	 */
	constructor( editor, { commandName, icon, componentName, dropdownLabel } ) {
		super( editor );

		/**
		 * The name of the command which will be executed when a color tile is clicked.
		 *
		 * @type {String}
		 */
		this.commandName = commandName;

		/**
		 * The name of this component in the {@link module:ui/componentfactory~ComponentFactory}.
		 * Also the configuration scope name in `editor.config`.
		 *
		 * @type {String}
		 */
		this.componentName = componentName;

		/**
		 * The SVG icon used by the dropdown.
		 * @type {String}
		 */
		this.icon = icon;

		/**
		 * The label used by the dropdown.
		 *
		 * @type {String}
		 */
		this.dropdownLabel = dropdownLabel;

		/**
		 * The number of columns in the color grid.
		 *
		 * @type {Number}
		 */
		this.columns = editor.config.get( `${ this.componentName }.columns` );

		/**
		 * Keeps a reference to {@link module:font/ui/colortableview~ColorTableView}.
		 *
		 * @member {module:font/ui/colortableview~ColorTableView}
		 */
		this.colorTableView = undefined;
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const locale = editor.locale;
		const t = locale.t;
		const command = editor.commands.get( this.commandName );
		const colorsConfig = normalizeColorOptions( editor.config.get( this.componentName ).colors );
		const localizedColors = getLocalizedColorOptions( locale, colorsConfig );
		const documentColorsCount = editor.config.get( `${ this.componentName }.documentColors` );

		// Register the UI component.
		editor.ui.componentFactory.add( this.componentName, locale => {
			const dropdownView = createDropdown( locale );
			this.colorTableView = addColorTableToDropdown( {
				dropdownView,
				colors: localizedColors.map( option => ( {
					label: option.label,
					color: option.model,
					options: {
						hasBorder: option.hasBorder
					}
				} ) ),
				columns: this.columns,
				removeButtonLabel: t( 'Remove color' ),
				documentColorsLabel: documentColorsCount !== 0 ? t( 'Document colors' ) : undefined,
				documentColorsCount: documentColorsCount === undefined ? this.columns : documentColorsCount
			} );

			this.colorTableView.bind( 'selectedColor' ).to( command, 'value' );

			dropdownView.buttonView.set( {
				label: this.dropdownLabel,
				icon: this.icon,
				tooltip: true
			} );

			dropdownView.extendTemplate( {
				attributes: {
					class: 'ck-color-ui-dropdown'
				}
			} );

			dropdownView.bind( 'isEnabled' ).to( command );

			dropdownView.on( 'execute', ( evt, data ) => {
				editor.execute( this.commandName, data );
				editor.editing.view.focus();
			} );

			dropdownView.on( 'change:isOpen', ( evt, name, isVisible ) => {
				// Grids rendering is deferred (#6192).
				dropdownView.colorTableView.appendGrids();

				if ( isVisible ) {
					if ( documentColorsCount !== 0 ) {
						this.colorTableView.updateDocumentColors( editor.model, this.componentName );
					}
					this.colorTableView.updateSelectedColors();
				}
			} );

			// Accessibility: focus the first active color when opening the dropdown.
			focusChildOnDropdownOpen( dropdownView, () => dropdownView.colorTableView.staticColorsGrid.items.find( item => item.isOn ) );

			return dropdownView;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/theme/icons/font-background.svg
/* harmony default export */ const font_background = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M4 2h12a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2zm8.38 9.262H7.62L10 5.506l2.38 5.756zm.532 1.285L14.34 16h1.426L10.804 4H9.196L4.234 16H5.66l1.428-3.453h5.824z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontbackgroundcolor/fontbackgroundcolorui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontbackgroundcolor/fontbackgroundcolorui
 */





/**
 * The font background color UI plugin. It introduces the `'fontBackgroundColor'` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class FontBackgroundColorUI extends ColorUI {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		const t = editor.locale.t;

		super( editor, {
			commandName: FONT_BACKGROUND_COLOR,
			componentName: FONT_BACKGROUND_COLOR,
			icon: font_background,
			dropdownLabel: t( 'Font Background Color' )
		} );
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontBackgroundColorUI';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontbackgroundcolor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontbackgroundcolor
 */





/**
 * The font background color plugin.
 *
 * For a detailed overview, check the {@glink features/font font feature} documentation
 * and the {@glink api/font package page}.
 *
 * This is a "glue" plugin which loads
 * the {@link module:font/fontbackgroundcolor/fontbackgroundcolorediting~FontBackgroundColorEditing} and
 * {@link module:font/fontbackgroundcolor/fontbackgroundcolorui~FontBackgroundColorUI} features in the editor.
 *
 * @extends module:core/plugin~Plugin
 */
class FontBackgroundColor extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ FontBackgroundColorEditing, FontBackgroundColorUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontBackgroundColor';
	}
}

/**
 * The configuration of the font background color feature.
 * It is introduced by the {@link module:font/fontbackgroundcolor/fontbackgroundcolorediting~FontBackgroundColorEditing} feature.
 *
 * Read more in {@link module:font/fontbackgroundcolor~FontBackgroundColorConfig}.
 *
 * @member {module:font/fontbackgroundcolor~FontBackgroundColorConfig} module:core/editor/editorconfig~EditorConfig#fontBackgroundColor
 */

/**
 * The configuration of the font background color feature.
 * This option is used by the {@link module:font/fontbackgroundcolor/fontbackgroundcolorediting~FontBackgroundColorEditing} feature.
 *
 *		ClassicEditor
 *			.create( {
 *				fontBackgroundColor: ... // Font background color feature configuration.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface module:font/fontbackgroundcolor~FontBackgroundColorConfig
 */

/**
 * Available font background colors defined as an array of strings or objects.
 *
 * The default value registers the following colors:
 *
 *		const fontBackgroundColorConfig = {
 *			colors: [
 *				{
 *					color: 'hsl(0, 0%, 0%)',
 *					label: 'Black'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 30%)',
 *					label: 'Dim grey'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 60%)',
 *					label: 'Grey'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 90%)',
 *					label: 'Light grey'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 100%)',
 *					label: 'White',
 *					hasBorder: true
 *				},
 *				{
 *					color: 'hsl(0, 75%, 60%)',
 *					label: 'Red'
 *				},
 *				{
 *					color: 'hsl(30, 75%, 60%)',
 *					label: 'Orange'
 *				},
 *				{
 *					color: 'hsl(60, 75%, 60%)',
 *					label: 'Yellow'
 *				},
 *				{
 *					color: 'hsl(90, 75%, 60%)',
 *					label: 'Light green'
 *				},
 *				{
 *					color: 'hsl(120, 75%, 60%)',
 *					label: 'Green'
 *				},
 *				{
 *					color: 'hsl(150, 75%, 60%)',
 *					label: 'Aquamarine'
 *				},
 *				{
 *					color: 'hsl(180, 75%, 60%)',
 *					label: 'Turquoise'
 *				},
 *				{
 *					color: 'hsl(210, 75%, 60%)',
 *					label: 'Light blue'
 *				},
 *				{
 *					color: 'hsl(240, 75%, 60%)',
 *					label: 'Blue'
 *				},
 *				{
 *					color: 'hsl(270, 75%, 60%)',
 *					label: 'Purple'
 *				}
 *			]
 *		};
 *
 * **Note**: The colors are displayed in the `'fontBackgroundColor'` dropdown.
 *
 * @member {Array.<String|Object>} module:font/fontbackgroundcolor~FontBackgroundColorConfig#colors
 */

/**
 * Represents the number of columns in the font background color dropdown.
 *
 * The default value is:
 *
 *		const fontBackgroundColorConfig = {
 *			columns: 5
 *		}
 *
 * @member {Number} module:font/fontbackgroundcolor~FontBackgroundColorConfig#columns
 */

/**
 * Determines the maximum number of available document colors.
 * Setting it to `0` will disable the document colors feature.
 *
 * By default it equals to the {@link module:font/fontbackgroundcolor~FontBackgroundColorConfig#columns} value.
 *
 * Examples:
 *
 * 	// 1) Neither document colors nor columns are defined in the configuration.
 * 	// Document colors will equal 5,
 * 	// because the value will be inherited from columns,
 * 	// which has a predefined value of 5.
 * 	const fontBackgroundColorConfig = {}
 *
 * 	// 2) Document colors will equal 8, because the value will be inherited from columns.
 * 	const fontBackgroundColorConfig = {
 * 		columns: 8
 * 	}
 *
 * 	// 3) Document colors will equal 24, because it has its own value defined.
 * 	const fontBackgroundColorConfig = {
 * 		columns: 8,
 * 		documentColors: 24
 * 	}
 *
 * 	// 4) The document colors feature will be disabled.
 * 	const fontBackgroundColorConfig = {
 * 		columns: 8,
 * 		documentColors: 0
 * 	}
 *
 * @member {Number} module:font/fontbackgroundcolor~FontBackgroundColorConfig#documentColors
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontcolor/fontcolorcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontcolor/fontcolorcommand
 */




/**
 * The font color command. It is used by {@link module:font/fontcolor/fontcolorediting~FontColorEditing}
 * to apply the font color.
 *
 *		editor.execute( 'fontColor', { value: 'rgb(250, 20, 20)' } );
 *
 * **Note**: Executing the command with the `null` value removes the attribute from the model.
 *
 * @extends module:font/fontcommand~FontCommand
 */
class FontColorCommand extends FontCommand {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor, FONT_COLOR );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontcolor/fontcolorediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontcolor/fontcolorediting
 */





/**
 * The font color editing feature.
 *
 * It introduces the {@link module:font/fontcolor/fontcolorcommand~FontColorCommand command} and
 * the `fontColor` attribute in the {@link module:engine/model/model~Model model} which renders
 * in the {@link module:engine/view/view view} as a `<span>` element (`<span style="color: ...">`),
 * depending on the {@link module:font/fontcolor~FontColorConfig configuration}.
 *
 * @extends module:core/plugin~Plugin
 */
class FontColorEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontColorEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( FONT_COLOR, {
			colors: [
				{
					color: 'hsl(0, 0%, 0%)',
					label: 'Black'
				},
				{
					color: 'hsl(0, 0%, 30%)',
					label: 'Dim grey'
				},
				{
					color: 'hsl(0, 0%, 60%)',
					label: 'Grey'
				},
				{
					color: 'hsl(0, 0%, 90%)',
					label: 'Light grey'
				},
				{
					color: 'hsl(0, 0%, 100%)',
					label: 'White',
					hasBorder: true
				},
				{
					color: 'hsl(0, 75%, 60%)',
					label: 'Red'
				},
				{
					color: 'hsl(30, 75%, 60%)',
					label: 'Orange'
				},
				{
					color: 'hsl(60, 75%, 60%)',
					label: 'Yellow'
				},
				{
					color: 'hsl(90, 75%, 60%)',
					label: 'Light green'
				},
				{
					color: 'hsl(120, 75%, 60%)',
					label: 'Green'
				},
				{
					color: 'hsl(150, 75%, 60%)',
					label: 'Aquamarine'
				},
				{
					color: 'hsl(180, 75%, 60%)',
					label: 'Turquoise'
				},
				{
					color: 'hsl(210, 75%, 60%)',
					label: 'Light blue'
				},
				{
					color: 'hsl(240, 75%, 60%)',
					label: 'Blue'
				},
				{
					color: 'hsl(270, 75%, 60%)',
					label: 'Purple'
				}
			],
			columns: 5
		} );

		editor.conversion.for( 'upcast' ).elementToAttribute( {
			view: {
				name: 'span',
				styles: {
					'color': /[\s\S]+/
				}
			},
			model: {
				key: FONT_COLOR,
				value: renderUpcastAttribute( 'color' )
			}
		} );

		// Support legacy `<font color="..">` formatting.
		editor.conversion.for( 'upcast' ).elementToAttribute( {
			view: {
				name: 'font',
				attributes: {
					'color': /^#?\w+$/
				}
			},
			model: {
				key: FONT_COLOR,
				value: viewElement => viewElement.getAttribute( 'color' )
			}
		} );

		editor.conversion.for( 'downcast' ).attributeToElement( {
			model: FONT_COLOR,
			view: renderDowncastElement( 'color' )
		} );

		editor.commands.add( FONT_COLOR, new FontColorCommand( editor ) );

		// Allow the font color attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: FONT_COLOR } );

		editor.model.schema.setAttributeProperties( FONT_COLOR, {
			isFormatting: true,
			copyOnEnter: true
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/theme/icons/font-color.svg
/* harmony default export */ const font_color = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.4 10.3 10 4.5l-2.4 5.8h4.8zm.5 1.2H7.1L5.7 15H4.2l5-12h1.6l5 12h-1.5L13 11.5zm3.1 7H4a1 1 0 0 1 0-2h12a1 1 0 0 1 0 2z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontcolor/fontcolorui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontcolor/fontcolorui
 */





/**
 * The font color UI plugin. It introduces the `'fontColor'` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class FontColorUI extends ColorUI {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		const t = editor.locale.t;

		super( editor, {
			commandName: FONT_COLOR,
			componentName: FONT_COLOR,
			icon: font_color,
			dropdownLabel: t( 'Font Color' )
		} );
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontColorUI';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontcolor.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontcolor
 */





/**
 * The font color plugin.
 *
 * For a detailed overview, check the {@glink features/font font feature} documentation
 * and the {@glink api/font package page}.
 *
 * This is a "glue" plugin which loads the {@link module:font/fontcolor/fontcolorediting~FontColorEditing} and
 * {@link module:font/fontcolor/fontcolorui~FontColorUI} features in the editor.
 *
 * @extends module:core/plugin~Plugin
 */
class FontColor extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ FontColorEditing, FontColorUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontColor';
	}
}

/**
 * The configuration of the font color feature.
 * It is introduced by the {@link module:font/fontcolor/fontcolorediting~FontColorEditing} feature.
 *
 * Read more in {@link module:font/fontcolor~FontColorConfig}.
 *
 * @member {module:font/fontcolor~FontColorConfig} module:core/editor/editorconfig~EditorConfig#fontColor
 */

/**
 * The configuration of the font color feature.
 * This option is used by the {@link module:font/fontcolor/fontcolorediting~FontColorEditing} feature.
 *
 *		ClassicEditor
 *			.create( {
 *				fontColor: ... // Font color feature configuration.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface module:font/fontcolor~FontColorConfig
 */

/**
 * Available font colors defined as an array of strings or objects.
 *
 * The default value registers the following colors:
 *
 *		const fontColorConfig = {
 *			colors: [
 *				{
 *					color: 'hsl(0, 0%, 0%)',
 *					label: 'Black'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 30%)',
 *					label: 'Dim grey'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 60%)',
 *					label: 'Grey'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 90%)',
 *					label: 'Light grey'
 *				},
 *				{
 *					color: 'hsl(0, 0%, 100%)',
 *					label: 'White',
 *					hasBorder: true
 *				},
 *				{
 *					color: 'hsl(0, 75%, 60%)',
 *					label: 'Red'
 *				},
 *				{
 *					color: 'hsl(30, 75%, 60%)',
 *					label: 'Orange'
 *				},
 *				{
 *					color: 'hsl(60, 75%, 60%)',
 *					label: 'Yellow'
 *				},
 *				{
 *					color: 'hsl(90, 75%, 60%)',
 *					label: 'Light green'
 *				},
 *				{
 *					color: 'hsl(120, 75%, 60%)',
 *					label: 'Green'
 *				},
 *				{
 *					color: 'hsl(150, 75%, 60%)',
 *					label: 'Aquamarine'
 *				},
 *				{
 *					color: 'hsl(180, 75%, 60%)',
 *					label: 'Turquoise'
 *				},
 *				{
 *					color: 'hsl(210, 75%, 60%)',
 *					label: 'Light blue'
 *				},
 *				{
 *					color: 'hsl(240, 75%, 60%)',
 *					label: 'Blue'
 *				},
 *				{
 *					color: 'hsl(270, 75%, 60%)',
 *					label: 'Purple'
 *				}
 *			]
 *		};
 *
 * **Note**: The colors are displayed in the `'fontColor'` dropdown.
 *
 * @member {Array.<String|Object>} module:font/fontcolor~FontColorConfig#colors
 */

/**
 * Represents the number of columns in the font color dropdown.
 *
 * The default value is:
 *
 *		const fontColorConfig = {
 *			columns: 5
 *		}
 *
 * @member {Number} module:font/fontcolor~FontColorConfig#columns
 */

/**
 * Determines the maximum number of available document colors.
 * Setting it to `0` will disable the document colors feature.
 *
 * By default it equals to the {@link module:font/fontcolor~FontColorConfig#columns} value.
 *
 * Examples:
 *
 * 	// 1) Neither document colors nor columns are defined in the configuration.
 * 	// Document colors will equal 5,
 * 	// because the value will be inherited from columns,
 * 	// which has a predefined value of 5.
 * 	const fontColorConfig = {}
 *
 * 	// 2) Document colors will equal 8, because the value will be inherited from columns.
 * 	const fontColorConfig = {
 * 		columns: 8
 * 	}
 *
 * 	// 3) Document colors will equal 24, because it has its own value defined.
 * 	const fontColorConfig = {
 * 		columns: 8,
 * 		documentColors: 24
 * 	}
 *
 * 	// 4) The document colors feature will be disabled.
 * 	const fontColorConfig = {
 * 		columns: 8,
 * 		documentColors: 0
 * 	}
 *
 * @member {Number} module:font/fontcolor~FontColorConfig#documentColors
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontfamily/fontfamilycommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontfamily/fontfamilycommand
 */




/**
 * The font family command. It is used by {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing}
 * to apply the font family.
 *
 *		editor.execute( 'fontFamily', { value: 'Arial' } );
 *
 * **Note**: Executing the command without the value removes the attribute from the model.
 *
 * @extends module:font/fontcommand~FontCommand
 */
class FontFamilyCommand extends FontCommand {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor, FONT_FAMILY );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontfamily/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontfamily/utils
 */

/**
 * Normalizes the {@link module:font/fontfamily~FontFamilyConfig#options configuration options}
 * to the {@link module:font/fontfamily~FontFamilyOption} format.
 *
 * @param {Array.<String|Object>} configuredOptions An array of options taken from the configuration.
 * @returns {Array.<module:font/fontfamily~FontFamilyOption>}
 */
function normalizeOptions( configuredOptions ) {
	// Convert options to objects.
	return configuredOptions
		.map( getOptionDefinition )
		// Filter out undefined values that `getOptionDefinition` might return.
		.filter( option => !!option );
}

// Returns an option definition either created from string shortcut.
// If object is passed then this method will return it without alternating it. Returns undefined for item than cannot be parsed.
//
// @param {String|Object} option
// @returns {undefined|module:font/fontfamily~FontFamilyOption}
function getOptionDefinition( option ) {
	// Treat any object as full item definition provided by user in configuration.
	if ( typeof option === 'object' ) {
		return option;
	}

	// Handle 'default' string as a special case. It will be used to remove the fontFamily attribute.
	if ( option === 'default' ) {
		return {
			title: 'Default',
			model: undefined
		};
	}

	// Ignore values that we cannot parse to a definition.
	if ( typeof option !== 'string' ) {
		return;
	}

	// Return font family definition from font string.
	return generateFontPreset( option );
}

// Creates a predefined preset for pixel size. It deconstructs font-family like string into full configuration option.
// A font definition is passed as coma delimited set of font family names. Font names might be quoted.
//
// @param {String} A font definition form configuration.
function generateFontPreset( fontDefinition ) {
	// Remove quotes from font names. They will be normalized later.
	const fontNames = fontDefinition.replace( /"|'/g, '' ).split( ',' );

	// The first matched font name will be used as dropdown list item title and as model value.
	const firstFontName = fontNames[ 0 ];

	// CSS-compatible font names.
	const cssFontNames = fontNames.map( normalizeFontNameForCSS ).join( ', ' );

	return {
		title: firstFontName,
		model: cssFontNames,
		view: {
			name: 'span',
			styles: {
				'font-family': cssFontNames
			},
			priority: 7
		}
	};
}

// Normalizes font name for the style attribute. It adds braces (') if font name contains spaces.
//
// @param {String} fontName
// @returns {String}
function normalizeFontNameForCSS( fontName ) {
	fontName = fontName.trim();

	// Compound font names should be quoted.
	if ( fontName.indexOf( ' ' ) > 0 ) {
		fontName = `'${ fontName }'`;
	}

	return fontName;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontfamily/fontfamilyediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontfamily/fontfamilyediting
 */







/**
 * The font family editing feature.
 *
 * It introduces the {@link module:font/fontfamily/fontfamilycommand~FontFamilyCommand command} and
 * the `fontFamily` attribute in the {@link module:engine/model/model~Model model} which renders
 * in the {@link module:engine/view/view view} as an inline `<span>` element (`<span style="font-family: Arial">`),
 * depending on the {@link module:font/fontfamily~FontFamilyConfig configuration}.
 *
 * @extends module:core/plugin~Plugin
 */
class FontFamilyEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontFamilyEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		// Define default configuration using font families shortcuts.
		editor.config.define( FONT_FAMILY, {
			options: [
				'default',
				'Arial, Helvetica, sans-serif',
				'Courier New, Courier, monospace',
				'Georgia, serif',
				'Lucida Sans Unicode, Lucida Grande, sans-serif',
				'Tahoma, Geneva, sans-serif',
				'Times New Roman, Times, serif',
				'Trebuchet MS, Helvetica, sans-serif',
				'Verdana, Geneva, sans-serif'
			],
			supportAllValues: false
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow fontFamily attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: FONT_FAMILY } );
		editor.model.schema.setAttributeProperties( FONT_FAMILY, {
			isFormatting: true,
			copyOnEnter: true
		} );

		// Get configured font family options without "default" option.
		const options = normalizeOptions( editor.config.get( 'fontFamily.options' ) ).filter( item => item.model );
		const definition = buildDefinition( FONT_FAMILY, options );

		// Set-up the two-way conversion.
		if ( editor.config.get( 'fontFamily.supportAllValues' ) ) {
			this._prepareAnyValueConverters();
			this._prepareCompatibilityConverter();
		} else {
			editor.conversion.attributeToElement( definition );
		}

		editor.commands.add( FONT_FAMILY, new FontFamilyCommand( editor ) );
	}

	/**
	 * These converters enable keeping any value found as `style="font-family: *"` as a value of an attribute on a text even
	 * if it is not defined in the plugin configuration.
	 *
	 * @private
	 */
	_prepareAnyValueConverters() {
		const editor = this.editor;

		editor.conversion.for( 'downcast' ).attributeToElement( {
			model: FONT_FAMILY,
			view: ( attributeValue, { writer } ) => {
				return writer.createAttributeElement( 'span', { style: 'font-family:' + attributeValue }, { priority: 7 } );
			}
		} );

		editor.conversion.for( 'upcast' ).elementToAttribute( {
			model: {
				key: FONT_FAMILY,
				value: viewElement => viewElement.getStyle( 'font-family' )
			},
			view: {
				name: 'span',
				styles: {
					'font-family': /.*/
				}
			}
		} );
	}

	/**
	 * Adds support for legacy `<font face="..">` formatting.
	 *
	 * @private
	 */
	_prepareCompatibilityConverter() {
		const editor = this.editor;

		editor.conversion.for( 'upcast' ).elementToAttribute( {
			view: {
				name: 'font',
				attributes: {
					'face': /.*/
				}
			},
			model: {
				key: FONT_FAMILY,
				value: viewElement => viewElement.getAttribute( 'face' )
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/theme/icons/font-family.svg
/* harmony default export */ const font_family = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M11.03 3h6.149a.75.75 0 1 1 0 1.5h-5.514L11.03 3zm1.27 3h4.879a.75.75 0 1 1 0 1.5h-4.244L12.3 6zm1.27 3h3.609a.75.75 0 1 1 0 1.5h-2.973L13.57 9zm-2.754 2.5L8.038 4.785 5.261 11.5h5.555zm.62 1.5H4.641l-1.666 4.028H1.312l5.789-14h1.875l5.789 14h-1.663L11.436 13z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontfamily/fontfamilyui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontfamily/fontfamilyui
 */










/**
 * The font family UI plugin. It introduces the `'fontFamily'` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class FontFamilyUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontFamilyUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		const options = this._getLocalizedOptions();

		const command = editor.commands.get( FONT_FAMILY );

		// Register UI component.
		editor.ui.componentFactory.add( FONT_FAMILY, locale => {
			const dropdownView = createDropdown( locale );
			addListToDropdown( dropdownView, _prepareListOptions( options, command ) );

			dropdownView.buttonView.set( {
				label: t( 'Font Family' ),
				icon: font_family,
				tooltip: true
			} );

			dropdownView.extendTemplate( {
				attributes: {
					class: 'ck-font-family-dropdown'
				}
			} );

			dropdownView.bind( 'isEnabled' ).to( command );

			// Execute command when an item from the dropdown is selected.
			this.listenTo( dropdownView, 'execute', evt => {
				editor.execute( evt.source.commandName, { value: evt.source.commandParam } );
				editor.editing.view.focus();
			} );

			return dropdownView;
		} );
	}

	/**
	 * Returns options as defined in `config.fontFamily.options` but processed to account for
	 * editor localization, i.e. to display {@link module:font/fontfamily~FontFamilyOption}
	 * in the correct language.
	 *
	 * Note: The reason behind this method is that 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.
	 *
	 * @private
	 * @returns {Array.<module:font/fontfamily~FontFamilyOption>}.
	 */
	_getLocalizedOptions() {
		const editor = this.editor;
		const t = editor.t;

		const options = normalizeOptions( editor.config.get( FONT_FAMILY ).options );

		return options.map( option => {
			// The only title to localize is "Default" others are font names.
			if ( option.title === 'Default' ) {
				option.title = t( 'Default' );
			}

			return option;
		} );
	}
}

// Prepares FontFamily dropdown items.
// @private
// @param {Array.<module:font/fontsize~FontSizeOption>} options
// @param {module:font/fontsize/fontsizecommand~FontSizeCommand} command
function _prepareListOptions( options, command ) {
	const itemDefinitions = new Collection();

	// Create dropdown items.
	for ( const option of options ) {
		const def = {
			type: 'button',
			model: new model_Model( {
				commandName: FONT_FAMILY,
				commandParam: option.model,
				label: option.title,
				withText: true
			} )
		};

		def.model.bind( 'isOn' ).to( command, 'value', value => {
			// "Default" or check in strict font-family converters mode.
			if ( value === option.model ) {
				return true;
			}

			if ( !value || !option.model ) {
				return false;
			}

			return value.split( ',' )[ 0 ].replace( /'/g, '' ).toLowerCase() === option.model.toLowerCase();
		} );

		// Try to set a dropdown list item style.
		if ( option.view && option.view.styles ) {
			def.model.set( 'labelStyle', `font-family: ${ option.view.styles[ 'font-family' ] }` );
		}

		itemDefinitions.add( def );
	}
	return itemDefinitions;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontfamily.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontfamily
 */





/**
 * The font family plugin.
 *
 * For a detailed overview, check the {@glink features/font font feature} documentatiom
 * and the {@glink api/font package page}.
 *
 * This is a "glue" plugin which loads the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} and
 * {@link module:font/fontfamily/fontfamilyui~FontFamilyUI} features in the editor.
 *
 * @extends module:core/plugin~Plugin
 */
class FontFamily extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ FontFamilyEditing, FontFamilyUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontFamily';
	}
}

/**
 * The font family option descriptor.
 *
 * @typedef {Object} module:font/fontfamily~FontFamilyOption
 *
 * @property {String} title The user-readable title of the option.
 * @property {String} model The attribute's unique value in the model.
 * @property {module:engine/view/elementdefinition~ElementDefinition} view View element configuration.
 * @property {Array.<module:engine/view/elementdefinition~ElementDefinition>} [upcastAlso] An array with all matched elements that
 * the view-to-model conversion should also accept.
 */

/**
 * The configuration of the font family feature.
 * It is introduced by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature.
 *
 * Read more in {@link module:font/fontfamily~FontFamilyConfig}.
 *
 * @member {module:font/fontfamily~FontFamilyConfig} module:core/editor/editorconfig~EditorConfig#fontFamily
 */

/**
 * The configuration of the font family feature.
 * This option is used by the {@link module:font/fontfamily/fontfamilyediting~FontFamilyEditing} feature.
 *
 *		ClassicEditor
 *			.create( {
 * 				fontFamily: ... // Font family feature configuration.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface module:font/fontfamily~FontFamilyConfig
 */

/**
 * Available font family options defined as an array of strings. The default value is:
 *
 *		const fontFamilyConfig = {
 *			options: [
 *				'default',
 *				'Arial, Helvetica, sans-serif',
 *				'Courier New, Courier, monospace',
 *				'Georgia, serif',
 *				'Lucida Sans Unicode, Lucida Grande, sans-serif',
 *				'Tahoma, Geneva, sans-serif',
 *				'Times New Roman, Times, serif',
 *				'Trebuchet MS, Helvetica, sans-serif',
 *				'Verdana, Geneva, sans-serif'
 *			]
 *		};
 *
 * which configures 8 font family options. Each option consists of one or more comma–separated font family names. The first font name is
 * used as the dropdown item description in the UI.
 *
 * **Note:** The family names that consist of spaces should not have quotes (as opposed to the CSS standard). The necessary quotes
 * will be added automatically in the view. For example, the `"Lucida Sans Unicode"` will render as follows:
 *
 * 		<span style="font-family:'Lucida Sans Unicode', 'Lucida Grande', sans-serif">...</span>
 *
 * The "default" option removes the `fontFamily` attribute from the selection. In such case, the text will
 * be rendered in the view using the default font family defined in the styles of the web page.
 *
 * Font family can be applied using the command API. To do that, use the `fontFamily` command and pass the desired family as a `value`.
 * For example, the following code will apply the `fontFamily` attribute with the `'Arial'` `value` to the current selection:
 *
 *		editor.execute( 'fontFamily', { value: 'Arial' } );
 *
 * Executing the `'fontFamily'` command without any value will remove the `fontFamily` attribute from the current selection.
 *
 * @member {Array.<String|module:font/fontfamily~FontFamilyOption>} module:font/fontfamily~FontFamilyConfig#options
 */

/**
 * By default the plugin removes any `font-family` value that does not match the plugin's configuration. It means that if you paste content
 * with font families that the editor does not understand, the `font-family` attribute will be removed and the content will be displayed
 * with the default font.
 *
 * You can preserve pasted font family values by switching the `supportAllValues` option to `true`:
 *
 *		const fontFamilyConfig = {
 *			supportAllValues: true
 *		};
 *
 * With this configuration font families not specified in the editor configuration will not be removed when pasting the content.
 *
 * @member {Boolean} module:font/fontfamily~FontFamilyConfig#supportAllValues
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontsize/fontsizecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontsize/fontsizecommand
 */




/**
 * The font size command. It is used by {@link module:font/fontsize/fontsizeediting~FontSizeEditing}
 * to apply the font size.
 *
 *		editor.execute( 'fontSize', { value: 'small' } );
 *
 * **Note**: Executing the command without the value removes the attribute from the model.
 *
 * @extends module:font/fontcommand~FontCommand
 */
class FontSizeCommand extends FontCommand {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor, FONT_SIZE );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontsize/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontsize/utils
 */



/**
 * Normalizes and translates the {@link module:font/fontsize~FontSizeConfig#options configuration options}
 * to the {@link module:font/fontsize~FontSizeOption} format.
 *
 * @param {Array.<String|Number|Object>} configuredOptions An array of options taken from the configuration.
 * @returns {Array.<module:font/fontsize~FontSizeOption>}
 */
function utils_normalizeOptions( configuredOptions ) {
	// Convert options to objects.
	return configuredOptions
		.map( item => utils_getOptionDefinition( item ) )
		// Filter out undefined values that `getOptionDefinition` might return.
		.filter( option => !!option );
}

// Default named presets map. Always create a new instance of the preset object in order to avoid modifying references.
const namedPresets = {
	get tiny() {
		return {
			title: 'Tiny',
			model: 'tiny',
			view: {
				name: 'span',
				classes: 'text-tiny',
				priority: 7
			}
		};
	},
	get small() {
		return {
			title: 'Small',
			model: 'small',
			view: {
				name: 'span',
				classes: 'text-small',
				priority: 7
			}
		};
	},
	get big() {
		return {
			title: 'Big',
			model: 'big',
			view: {
				name: 'span',
				classes: 'text-big',
				priority: 7
			}
		};
	},
	get huge() {
		return {
			title: 'Huge',
			model: 'huge',
			view: {
				name: 'span',
				classes: 'text-huge',
				priority: 7
			}
		};
	}
};

// Returns an option definition either from preset or creates one from number shortcut.
// If object is passed then this method will return it without alternating it. Returns undefined for item than cannot be parsed.
//
// @param {String|Number|Object} item
// @returns {undefined|module:font/fontsize~FontSizeOption}
function utils_getOptionDefinition( option ) {
	// Check whether passed option is a full item definition provided by user in configuration.
	if ( isFullItemDefinition( option ) ) {
		return attachPriority( option );
	}

	const preset = findPreset( option );

	// Item is a named preset.
	if ( preset ) {
		return attachPriority( preset );
	}

	// 'Default' font size. It will be used to remove the fontSize attribute.
	if ( option === 'default' ) {
		return {
			model: undefined,
			title: 'Default'
		};
	}

	// At this stage we probably have numerical value to generate a preset so parse it's value.
	// Discard any faulty values.
	if ( isNumericalDefinition( option ) ) {
		return;
	}

	// Return font size definition from size value.
	return generatePixelPreset( option );
}

// Creates a predefined preset for pixel size.
//
// @param {Number} definition Font size in pixels.
// @returns {module:font/fontsize~FontSizeOption}
function generatePixelPreset( definition ) {
	// Extend a short (numeric value) definition.
	if ( typeof definition === 'number' || typeof definition === 'string' ) {
		definition = {
			title: String( definition ),
			model: `${ parseFloat( definition ) }px`
		};
	}

	definition.view = {
		name: 'span',
		styles: {
			'font-size': definition.model
		}
	};

	return attachPriority( definition );
}

// Adds the priority to the view element definition if missing. It's required due to ckeditor/ckeditor5#2291
//
// @param {Object} definition
// @param {Object} definition.title
// @param {Object} definition.model
// @param {Object} definition.view
// @returns {Object}
function attachPriority( definition ) {
	if ( !definition.view.priority ) {
		definition.view.priority = 7;
	}

	return definition;
}

// Returns a prepared preset definition. If passed an object, a name of preset should be defined as `model` value.
//
// @param {String|Object} definition
// @param {String} definition.model A preset name.
// @returns {Object|undefined}
function findPreset( definition ) {
	return namedPresets[ definition ] || namedPresets[ definition.model ];
}

// We treat `definition` as completed if it is an object that contains `title`, `model` and `view` values.
//
// @param {Object} definition
// @param {String} definition.title
// @param {String} definition.model
// @param {Object} definition.view
// @returns {Boolean}
function isFullItemDefinition( definition ) {
	return typeof definition === 'object' && definition.title && definition.model && definition.view;
}

// We treat `definition` as numerical if it is a number, number-like (string) or an object with the `title` key.
//
// @param {Object|Number|String} definition
// @param {Object} definition.title
// @returns {Boolean}
function isNumericalDefinition( definition ) {
	let numberValue;

	if ( typeof definition === 'object' ) {
		if ( !definition.model ) {
			/**
			 * Provided value as an option for {@link module:font/fontsize~FontSize} seems to invalid.
			 *
			 * See valid examples described in the {@link module:font/fontsize~FontSizeConfig#options plugin configuration}.
			 *
			 * @error font-size-invalid-definition
			 */
			throw new CKEditorError( 'font-size-invalid-definition', null, definition );
		} else {
			numberValue = parseFloat( definition.model );
		}
	} else {
		numberValue = parseFloat( definition );
	}

	return isNaN( numberValue );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontsize/fontsizeediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontsize/fontsizeediting
 */









// Mapping of `<font size="..">` styling to CSS's `font-size` values.
const styleFontSize = [
	'x-small', // Size "0" equal to "1".
	'x-small',
	'small',
	'medium',
	'large',
	'x-large',
	'xx-large',
	'xxx-large'
];

/**
 * The font size editing feature.
 *
 * It introduces the {@link module:font/fontsize/fontsizecommand~FontSizeCommand command} and the `fontSize`
 * attribute in the {@link module:engine/model/model~Model model} which renders in the {@link module:engine/view/view view}
 * as a `<span>` element with either:
 * * a style attribute (`<span style="font-size:12px">...</span>`),
 * * or a class attribute (`<span class="text-small">...</span>`)
 *
 * depending on the {@link module:font/fontsize~FontSizeConfig configuration}.
 *
 * @extends module:core/plugin~Plugin
 */
class FontSizeEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontSizeEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		// Define default configuration using named presets.
		editor.config.define( FONT_SIZE, {
			options: [
				'tiny',
				'small',
				'default',
				'big',
				'huge'
			],
			supportAllValues: false
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow fontSize attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: FONT_SIZE } );
		editor.model.schema.setAttributeProperties( FONT_SIZE, {
			isFormatting: true,
			copyOnEnter: true
		} );

		const supportAllValues = editor.config.get( 'fontSize.supportAllValues' );

		// Define view to model conversion.
		const options = utils_normalizeOptions( this.editor.config.get( 'fontSize.options' ) )
			.filter( item => item.model );
		const definition = buildDefinition( FONT_SIZE, options );

		// Set-up the two-way conversion.
		if ( supportAllValues ) {
			this._prepareAnyValueConverters( definition );
			this._prepareCompatibilityConverter();
		} else {
			editor.conversion.attributeToElement( definition );
		}

		// Add FontSize command.
		editor.commands.add( FONT_SIZE, new FontSizeCommand( editor ) );
	}

	/**
	 * These converters enable keeping any value found as `style="font-size: *"` as a value of an attribute on a text even
	 * if it is not defined in the plugin configuration.
	 *
	 * @param {Object} definition {@link module:engine/conversion/conversion~ConverterDefinition Converter definition} out of input data.
	 * @private
	 */
	_prepareAnyValueConverters( definition ) {
		const editor = this.editor;

		// If `fontSize.supportAllValues=true`, we do not allow to use named presets in the plugin's configuration.
		const presets = definition.model.values.filter( value => {
			return !utils_isLength( String( value ) ) && !isPercentage( String( value ) );
		} );

		if ( presets.length ) {
			/**
			 * If {@link module:font/fontsize~FontSizeConfig#supportAllValues `config.fontSize.supportAllValues`} is `true`,
			 * you need to use numerical values as font size options.
			 *
			 * See valid examples described in the {@link module:font/fontsize~FontSizeConfig#options plugin configuration}.
			 *
			 * @error font-size-invalid-use-of-named-presets
			 * @param {Array.<String>} presets Invalid values.
			 */
			throw new CKEditorError(
				'font-size-invalid-use-of-named-presets',
				null, { presets }
			);
		}

		editor.conversion.for( 'downcast' ).attributeToElement( {
			model: FONT_SIZE,
			view: ( attributeValue, { writer } ) => {
				if ( !attributeValue ) {
					return;
				}

				return writer.createAttributeElement( 'span', { style: 'font-size:' + attributeValue }, { priority: 7 } );
			}
		} );

		editor.conversion.for( 'upcast' ).elementToAttribute( {
			model: {
				key: FONT_SIZE,
				value: viewElement => viewElement.getStyle( 'font-size' )
			},
			view: {
				name: 'span',
				styles: {
					'font-size': /.*/
				}
			}
		} );
	}

	/**
	 * Adds support for legacy `<font size="..">` formatting.
	 *
	 * @private
	 */
	_prepareCompatibilityConverter() {
		const editor = this.editor;

		editor.conversion.for( 'upcast' ).elementToAttribute( {
			view: {
				name: 'font',
				attributes: {
					// Documentation mentions sizes from 1 to 7. To handle old content we support all values
					// up to 999 but clamp it to the valid range. Why 999? It should cover accidental values
					// similar to percentage, e.g. 100%, 200% which could be the usual mistake for font size.
					'size': /^[+-]?\d{1,3}$/
				}
			},
			model: {
				key: FONT_SIZE,
				value: viewElement => {
					const value = viewElement.getAttribute( 'size' );
					const isRelative = value[ 0 ] === '-' || value[ 0 ] === '+';

					let size = parseInt( value, 10 );

					if ( isRelative ) {
						// Add relative size (which can be negative) to the default size = 3.
						size = 3 + size;
					}

					const maxSize = styleFontSize.length - 1;
					const clampedSize = Math.min( Math.max( size, 0 ), maxSize );

					return styleFontSize[ clampedSize ];
				}
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/theme/icons/font-size.svg
/* harmony default export */ const font_size = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M9.816 11.5 7.038 4.785 4.261 11.5h5.555zm.62 1.5H3.641l-1.666 4.028H.312l5.789-14h1.875l5.789 14h-1.663L10.436 13zm7.55 2.279.779-.779.707.707-2.265 2.265-2.193-2.265.707-.707.765.765V4.825c0-.042 0-.083.002-.123l-.77.77-.707-.707L17.207 2.5l2.265 2.265-.707.707-.782-.782c.002.043.003.089.003.135v10.454z\"/></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-font/theme/fontsize.css
var fontsize = __webpack_require__(6203);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/theme/fontsize.css

            

var fontsize_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

fontsize_options.insert = "head";
fontsize_options.singleton = true;

var fontsize_update = injectStylesIntoStyleTag_default()(fontsize/* default */.Z, fontsize_options);



/* harmony default export */ const theme_fontsize = (fontsize/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontsize/fontsizeui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontsize/fontsizeui
 */











/**
 * The font size UI plugin. It introduces the `'fontSize'` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class FontSizeUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontSizeUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		const options = this._getLocalizedOptions();

		const command = editor.commands.get( FONT_SIZE );

		// Register UI component.
		editor.ui.componentFactory.add( FONT_SIZE, locale => {
			const dropdownView = createDropdown( locale );
			addListToDropdown( dropdownView, fontsizeui_prepareListOptions( options, command ) );

			// Create dropdown model.
			dropdownView.buttonView.set( {
				label: t( 'Font Size' ),
				icon: font_size,
				tooltip: true
			} );

			dropdownView.extendTemplate( {
				attributes: {
					class: [
						'ck-font-size-dropdown'
					]
				}
			} );

			dropdownView.bind( 'isEnabled' ).to( command );

			// Execute command when an item from the dropdown is selected.
			this.listenTo( dropdownView, 'execute', evt => {
				editor.execute( evt.source.commandName, { value: evt.source.commandParam } );
				editor.editing.view.focus();
			} );

			return dropdownView;
		} );
	}

	/**
	 * Returns options as defined in `config.fontSize.options` but processed to account for
	 * editor localization, i.e. to display {@link module:font/fontsize~FontSizeOption}
	 * in the correct language.
	 *
	 * Note: The reason behind this method is that 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.
	 *
	 * @private
	 * @returns {Array.<module:font/fontsize~FontSizeOption>}.
	 */
	_getLocalizedOptions() {
		const editor = this.editor;
		const t = editor.t;

		const localizedTitles = {
			Default: t( 'Default' ),
			Tiny: t( 'Tiny' ),
			Small: t( 'Small' ),
			Big: t( 'Big' ),
			Huge: t( 'Huge' )
		};

		const options = utils_normalizeOptions( editor.config.get( FONT_SIZE ).options );

		return options.map( option => {
			const title = localizedTitles[ option.title ];

			if ( title && title != option.title ) {
				// Clone the option to avoid altering the original `namedPresets` from `./utils.js`.
				option = Object.assign( {}, option, { title } );
			}

			return option;
		} );
	}
}

// Prepares FontSize dropdown items.
// @private
// @param {Array.<module:font/fontsize~FontSizeOption>} options
// @param {module:font/fontsize/fontsizecommand~FontSizeCommand} command
function fontsizeui_prepareListOptions( options, command ) {
	const itemDefinitions = new Collection();

	for ( const option of options ) {
		const def = {
			type: 'button',
			model: new model_Model( {
				commandName: FONT_SIZE,
				commandParam: option.model,
				label: option.title,
				class: 'ck-fontsize-option',
				withText: true
			} )
		};

		if ( option.view && option.view.styles ) {
			def.model.set( 'labelStyle', `font-size:${ option.view.styles[ 'font-size' ] }` );
		}

		if ( option.view && option.view.classes ) {
			def.model.set( 'class', `${ def.model.class } ${ option.view.classes }` );
		}

		def.model.bind( 'isOn' ).to( command, 'value', value => value === option.model );

		// Add the option to the collection.
		itemDefinitions.add( def );
	}

	return itemDefinitions;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-font/src/fontsize.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module font/fontsize
 */






/**
 * The font size plugin.
 *
 * For a detailed overview, check the {@glink features/font font feature} documentation
 * and the {@glink api/font package page}.
 *
 * This is a "glue" plugin which loads the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} and
 * {@link module:font/fontsize/fontsizeui~FontSizeUI} features in the editor.
 *
 * @extends module:core/plugin~Plugin
 */
class FontSize extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ FontSizeEditing, FontSizeUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'FontSize';
	}

	/**
	 * Normalizes and translates the {@link module:font/fontsize~FontSizeConfig#options configuration options}
	 * to the {@link module:font/fontsize~FontSizeOption} format.
	 *
	 * @param {Array.<String|Number|Object>} configuredOptions An array of options taken from the configuration.
	 * @returns {Array.<module:font/fontsize~FontSizeOption>}
	 */
	normalizeSizeOptions( options ) {
		return utils_normalizeOptions( options );
	}
}

/**
 * The font size option descriptor.
 *
 * @typedef {Object} module:font/fontsize~FontSizeOption
 *
 * @property {String} title The user-readable title of the option.
 * @property {String} model The attribute's unique value in the model.
 * @property {module:engine/view/elementdefinition~ElementDefinition} view View element configuration.
 * @property {Array.<module:engine/view/elementdefinition~ElementDefinition>} [upcastAlso] An array with all matched elements that
 * the view-to-model conversion should also accept.
 */

/**
 * The configuration of the font size feature.
 * It is introduced by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature.
 *
 * Read more in {@link module:font/fontsize~FontSizeConfig}.
 *
 * @member {module:font/fontsize~FontSizeConfig} module:core/editor/editorconfig~EditorConfig#fontSize
 */

/**
 * The configuration of the font size feature.
 * This option is used by the {@link module:font/fontsize/fontsizeediting~FontSizeEditing} feature.
 *
 * 		ClassicEditor
 * 			.create( {
 * 				fontSize: ... // Font size feature configuration.
 *			} )
 * 			.then( ... )
 * 			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface module:font/fontsize~FontSizeConfig
 */

/**
 * Available font size options. Expressed as predefined presets, numerical "pixel" values
 * or the {@link module:font/fontsize~FontSizeOption}.
 *
 * The default value is:
 *
 *		const fontSizeConfig = {
 *			options: [
 *				'tiny',
 * 				'small',
 * 				'default',
 * 				'big',
 * 				'huge'
 *			]
 *		};
 *
 * It defines 4 sizes: **tiny**, **small**, **big**, and **huge**. These values will be rendered as `<span>` elements in the view.
 * The **default** defines a text without the `fontSize` attribute.
 *
 * Each `<span>` has the the `class` attribute set to the corresponding size name. For instance, this is what the **small** size looks
 * like in the view:
 *
 * 		<span class="text-small">...</span>
 *
 * As an alternative, the font size might be defined using numerical values (either as a `Number` or as a `String`):
 *
 * 		const fontSizeConfig = {
 * 			options: [ 9, 10, 11, 12, 13, 14, 15 ]
 * 		};
 *
 * Also, you can define a label in the dropdown for numerical values:
 *
 *		const fontSizeConfig = {
 *			options: [
 *				{
 * 				 	title: 'Small',
 * 				 	model: '8px'
 * 				},
 * 				'default',
 * 				{
 * 				 	title: 'Big',
 * 				 	model: '14px'
 * 				}
 *			]
 *		};
 *
 * Font size can be applied using the command API. To do that, use the `'fontSize'` command and pass the desired font size as a `value`.
 * For example, the following code will apply the `fontSize` attribute with the **tiny** value to the current selection:
 *
 *		editor.execute( 'fontSize', { value: 'tiny' } );
 *
 * Executing the `fontSize` command without value will remove the `fontSize` attribute from the current selection.
 *
 * @member {Array.<String|Number|module:font/fontsize~FontSizeOption>} module:font/fontsize~FontSizeConfig#options
 */

/**
 * By default the plugin removes any `font-size` value that does not match the plugin's configuration. It means that if you paste content
 * with font sizes that the editor does not understand, the `font-size` attribute will be removed and the content will be displayed
 * with the default size.
 *
 * You can preserve pasted font size values by switching the `supportAllValues` option to `true`:
 *
 *		const fontSizeConfig = {
 *			options: [ 9, 10, 11, 12, 'default', 14, 15 ],
 *			supportAllValues: true
 *		};
 *
 * **Note:** This option can only be used with numerical values as font size options.
 *
 * With this configuration font sizes not specified in the editor configuration will not be removed when pasting the content.
 *
 * @member {Boolean} module:font/fontsize~FontSizeConfig#supportAllValues
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/codeblock.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/codeblock
 */






/**
 * Provides the General HTML Support integration with {@link module:code-block/codeblock~CodeBlock Code Block} feature.
 *
 * @extends module:core/plugin~Plugin
 */
class CodeBlockElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'CodeBlockElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		if ( !this.editor.plugins.has( 'CodeBlockEditing' ) ) {
			return;
		}

		const dataFilter = this.editor.plugins.get( DataFilter );

		dataFilter.on( 'register:pre', ( evt, definition ) => {
			if ( definition.model !== 'codeBlock' ) {
				return;
			}

			const editor = this.editor;
			const schema = editor.model.schema;
			const conversion = editor.conversion;

			// Extend codeBlock to allow attributes required by attribute filtration.
			schema.extend( 'codeBlock', {
				allowAttributes: [ 'htmlAttributes', 'htmlContentAttributes' ]
			} );

			conversion.for( 'upcast' ).add( viewToModelCodeBlockAttributeConverter( dataFilter ) );
			conversion.for( 'downcast' ).add( modelToViewCodeBlockAttributeConverter() );

			evt.stop();
		} );
	}
}

// View-to-model conversion helper preserving allowed attributes on {@link module:code-block/codeblock~CodeBlock Code Block}
// feature model element.
//
// Attributes are preserved as a value of `htmlAttributes` model attribute.
//
// @private
// @param {module:html-support/datafilter~DataFilter} dataFilter
// @returns {Function} Returns a conversion callback.
function viewToModelCodeBlockAttributeConverter( dataFilter ) {
	return dispatcher => {
		dispatcher.on( 'element:code', ( evt, data, conversionApi ) => {
			const viewCodeElement = data.viewItem;
			const viewPreElement = viewCodeElement.parent;

			if ( !viewPreElement || !viewPreElement.is( 'element', 'pre' ) ) {
				return;
			}

			preserveElementAttributes( viewPreElement, 'htmlAttributes' );
			preserveElementAttributes( viewCodeElement, 'htmlContentAttributes' );

			function preserveElementAttributes( viewElement, attributeName ) {
				const viewAttributes = dataFilter.processViewAttributes( viewElement, conversionApi );

				if ( viewAttributes ) {
					conversionApi.writer.setAttribute( attributeName, viewAttributes, data.modelRange );
				}
			}
		}, { priority: 'low' } );
	};
}

// Model-to-view conversion helper applying attributes from {@link module:code-block/codeblock~CodeBlock Code Block}
// feature model element.
//
// @private
// @returns {Function} Returns a conversion callback.
function modelToViewCodeBlockAttributeConverter() {
	return dispatcher => {
		dispatcher.on( 'attribute:htmlAttributes:codeBlock', ( evt, data, conversionApi ) => {
			if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
				return;
			}

			const { attributeOldValue, attributeNewValue } = data;
			const viewCodeElement = conversionApi.mapper.toViewElement( data.item );
			const viewPreElement = viewCodeElement.parent;

			updateViewAttributes( conversionApi.writer, attributeOldValue, attributeNewValue, viewPreElement );
		} );

		dispatcher.on( 'attribute:htmlContentAttributes:codeBlock', ( evt, data, conversionApi ) => {
			if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
				return;
			}

			const { attributeOldValue, attributeNewValue } = data;
			const viewCodeElement = conversionApi.mapper.toViewElement( data.item );

			updateViewAttributes( conversionApi.writer, attributeOldValue, attributeNewValue, viewCodeElement );
		} );
	};
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/dualcontent.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/dualcontent
 */







/**
 * Provides the General HTML Support integration for elements which can behave like sectioning element (e.g. article) or
 * element accepting only inline content (e.g. paragraph).
 *
 * The distinction between this two content models is important for choosing correct schema model and proper content conversion.
 * As an example, it ensures that:
 *
 * * children elements paragraphing is enabled for sectioning elements only,
 * * element and its content can be correctly handled by editing view (splitting and merging elements),
 * * model element HTML is semantically correct and easier to work with.
 *
 * If element contains any block element, it will be treated as a sectioning element and registered using
 * {@link module:html-support/dataschema~DataSchemaDefinition#model} and
 * {@link module:html-support/dataschema~DataSchemaDefinition#modelSchema} in editor schema.
 * Otherwise, it will be registered under {@link module:html-support/dataschema~DataSchemaBlockElementDefinition#paragraphLikeModel} model
 * name with model schema accepting only inline content (inheriting from `$block`).
 *
 * @extends module:core/plugin~Plugin
 */
class DualContentModelElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DualContentModelElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const dataFilter = this.editor.plugins.get( DataFilter );

		dataFilter.on( 'register', ( evt, definition ) => {
			const editor = this.editor;
			const schema = editor.model.schema;
			const conversion = editor.conversion;

			if ( !definition.paragraphLikeModel ) {
				return;
			}

			// Can only apply to newly registered features.
			if ( schema.isRegistered( definition.model ) || schema.isRegistered( definition.paragraphLikeModel ) ) {
				return;
			}

			const paragraphLikeModelDefinition = {
				model: definition.paragraphLikeModel,
				view: definition.view
			};

			schema.register( definition.model, definition.modelSchema );
			schema.register( paragraphLikeModelDefinition.model, {
				inheritAllFrom: '$block'
			} );

			conversion.for( 'upcast' ).elementToElement( {
				view: definition.view,
				model: ( viewElement, { writer } ) => {
					if ( this._hasBlockContent( viewElement ) ) {
						return writer.createElement( definition.model );
					}

					return writer.createElement( paragraphLikeModelDefinition.model );
				},
				// With a `low` priority, `paragraph` plugin auto-paragraphing mechanism is executed. Make sure
				// this listener is called before it. If not, some elements will be transformed into a paragraph.
				converterPriority: src_priorities.get( 'low' ) + 1
			} );

			conversion.for( 'downcast' ).elementToElement( {
				view: definition.view,
				model: definition.model
			} );
			this._addAttributeConversion( definition );

			conversion.for( 'downcast' ).elementToElement( {
				view: paragraphLikeModelDefinition.view,
				model: paragraphLikeModelDefinition.model
			} );
			this._addAttributeConversion( paragraphLikeModelDefinition );

			evt.stop();
		} );
	}

	/**
	 * Checks whether the given view element includes any other block element.
	 *
	 * @private
	 * @param {module:engine/view/element~Element} viewElement
	 * @returns {Boolean}
	 */
	_hasBlockContent( viewElement ) {
		const view = this.editor.editing.view;
		const blockElements = view.domConverter.blockElements;

		// Traversing the viewElement subtree looking for block elements.
		// Especially for the cases like <div><a href="#"><p>foo</p></a></div>.
		// https://github.com/ckeditor/ckeditor5/issues/11513
		for ( const viewItem of view.createRangeIn( viewElement ).getItems() ) {
			if ( viewItem.is( 'element' ) && blockElements.includes( viewItem.name ) ) {
				return true;
			}
		}

		return false;
	}

	/**
	 * Adds attribute filtering conversion for the given data schema.
	 *
	 * @private
	 * @param {module:html-support/dataschema~DataSchemaBlockElementDefinition} definition
	 */
	_addAttributeConversion( definition ) {
		const editor = this.editor;
		const conversion = editor.conversion;
		const dataFilter = editor.plugins.get( DataFilter );

		editor.model.schema.extend( definition.model, {
			allowAttributes: 'htmlAttributes'
		} );

		conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, dataFilter ) );
		conversion.for( 'downcast' ).add( modelToViewBlockAttributeConverter( definition ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/heading.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/heading
 */





/**
 * Provides the General HTML Support integration with {@link module:heading/heading~Heading Heading} feature.
 *
 * @extends module:core/plugin~Plugin
 */
class HeadingElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataSchema ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HeadingElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		if ( !editor.plugins.has( 'HeadingEditing' ) ) {
			return;
		}

		const dataSchema = editor.plugins.get( DataSchema );
		const options = editor.config.get( 'heading.options' );
		const headerModels = [];

		// We are registering all elements supported by HeadingEditing
		// to enable custom attributes for those elements.
		for ( const option of options ) {
			if ( 'model' in option && 'view' in option ) {
				dataSchema.registerBlockElement( {
					view: option.view,
					model: option.model
				} );

				headerModels.push( option.model );
			}
		}

		dataSchema.extendBlockElement( {
			model: 'htmlHgroup',
			modelSchema: {
				allowChildren: headerModels
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/image.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/image
 */






/**
 * Provides the General HTML Support integration with the {@link module:image/image~Image Image} feature.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// At least one image plugin should be loaded for the integration to work properly.
		if ( !editor.plugins.has( 'ImageInlineEditing' ) && !editor.plugins.has( 'ImageBlockEditing' ) ) {
			return;
		}

		const schema = editor.model.schema;
		const conversion = editor.conversion;
		const dataFilter = editor.plugins.get( DataFilter );

		dataFilter.on( 'register:figure', () => {
			conversion.for( 'upcast' ).add( viewToModelFigureAttributeConverter( dataFilter ) );
		} );

		dataFilter.on( 'register:img', ( evt, definition ) => {
			if ( definition.model !== 'imageBlock' && definition.model !== 'imageInline' ) {
				return;
			}

			if ( schema.isRegistered( 'imageBlock' ) ) {
				schema.extend( 'imageBlock', {
					allowAttributes: [
						'htmlAttributes',
						// Figure and Link don't have model counterpart.
						// We will preserve attributes on image model element using these attribute keys.
						'htmlFigureAttributes',
						'htmlLinkAttributes'
					]
				} );
			}

			if ( schema.isRegistered( 'imageInline' ) ) {
				schema.extend( 'imageInline', {
					allowAttributes: [
						// `htmlA` is needed for standard GHS link integration.
						'htmlA',
						'htmlAttributes'
					]
				} );
			}

			conversion.for( 'upcast' ).add( viewToModelImageAttributeConverter( dataFilter ) );
			conversion.for( 'downcast' ).add( modelToViewImageAttributeConverter() );

			evt.stop();
		} );
	}
}

// View-to-model conversion helper preserving allowed attributes on the {@link module:image/image~Image Image}
// feature model element.
//
// @private
// @param {module:html-support/datafilter~DataFilter} dataFilter
// @returns {Function} Returns a conversion callback.
function viewToModelImageAttributeConverter( dataFilter ) {
	return dispatcher => {
		dispatcher.on( 'element:img', ( evt, data, conversionApi ) => {
			if ( !data.modelRange ) {
				return;
			}

			const viewImageElement = data.viewItem;
			const viewContainerElement = viewImageElement.parent;

			preserveElementAttributes( viewImageElement, 'htmlAttributes' );

			if ( viewContainerElement.is( 'element', 'a' ) ) {
				preserveLinkAttributes( viewContainerElement );
			}

			function preserveElementAttributes( viewElement, attributeName ) {
				const viewAttributes = dataFilter.processViewAttributes( viewElement, conversionApi );

				if ( viewAttributes ) {
					conversionApi.writer.setAttribute( attributeName, viewAttributes, data.modelRange );
				}
			}

			function preserveLinkAttributes( viewContainerElement ) {
				if ( data.modelRange && data.modelRange.getContainedElement().is( 'element', 'imageBlock' ) ) {
					preserveElementAttributes( viewContainerElement, 'htmlLinkAttributes' );
				}
			}
		}, { priority: 'low' } );
	};
}

// View-to-model conversion helper preserving allowed attributes on {@link module:image/image~Image Image}
// feature model element from figure view element.
//
// @private
// @param {module:html-support/datafilter~DataFilter} dataFilter
// @returns {Function} Returns a conversion callback.
function viewToModelFigureAttributeConverter( dataFilter ) {
	return dispatcher => {
		dispatcher.on( 'element:figure', ( evt, data, conversionApi ) => {
			const viewFigureElement = data.viewItem;

			if ( !data.modelRange || !viewFigureElement.hasClass( 'image' ) ) {
				return;
			}

			const viewAttributes = dataFilter.processViewAttributes( viewFigureElement, conversionApi );

			if ( viewAttributes ) {
				conversionApi.writer.setAttribute( 'htmlFigureAttributes', viewAttributes, data.modelRange );
			}
		}, { priority: 'low' } );
	};
}

// A model-to-view conversion helper applying attributes from the {@link module:image/image~Image Image}
// feature.
//
// @private
// @returns {Function} Returns a conversion callback.
function modelToViewImageAttributeConverter() {
	return dispatcher => {
		addInlineAttributeConversion( 'htmlAttributes' );

		addBlockAttributeConversion( 'img', 'htmlAttributes' );
		addBlockAttributeConversion( 'figure', 'htmlFigureAttributes' );
		addBlockAttributeConversion( 'a', 'htmlLinkAttributes' );

		function addInlineAttributeConversion( attributeName ) {
			dispatcher.on( `attribute:${ attributeName }:imageInline`, ( evt, data, conversionApi ) => {
				if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
					return;
				}

				const { attributeOldValue, attributeNewValue } = data;
				const viewElement = conversionApi.mapper.toViewElement( data.item );

				updateViewAttributes( conversionApi.writer, attributeOldValue, attributeNewValue, viewElement );
			}, { priority: 'low' } );
		}

		function addBlockAttributeConversion( elementName, attributeName ) {
			dispatcher.on( `attribute:${ attributeName }:imageBlock`, ( evt, data, conversionApi ) => {
				if ( !conversionApi.consumable.test( data.item, evt.name ) ) {
					return;
				}

				const { attributeOldValue, attributeNewValue } = data;
				const containerElement = conversionApi.mapper.toViewElement( data.item );
				const viewElement = getDescendantElement( conversionApi.writer, containerElement, elementName );

				if ( viewElement ) {
					updateViewAttributes( conversionApi.writer, attributeOldValue, attributeNewValue, viewElement );
					conversionApi.consumable.consume( data.item, evt.name );
				}
			}, { priority: 'low' } );

			if ( elementName === 'a' ) {
				// To have a link element in the view, we need to attach a converter to the `linkHref` attribute as well.
				dispatcher.on( 'attribute:linkHref:imageBlock', ( evt, data, conversionApi ) => {
					if ( !conversionApi.consumable.consume( data.item, 'attribute:htmlLinkAttributes:imageBlock' ) ) {
						return;
					}

					const containerElement = conversionApi.mapper.toViewElement( data.item );
					const viewElement = getDescendantElement( conversionApi.writer, containerElement, 'a' );

					setViewAttributes( conversionApi.writer, data.item.getAttribute( 'htmlLinkAttributes' ), viewElement );
				}, { priority: 'low' } );
			}
		}
	};
}

// Returns the first view element descendant matching the given view name.
// Includes view element itself.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {module:engine/view/element~Element} containerElement
// @param {String} elementName
// @returns {module:engine/view/element~Element|null}
function getDescendantElement( writer, containerElement, elementName ) {
	const range = writer.createRangeOn( containerElement );

	for ( const { item } of range.getWalker() ) {
		if ( item.is( 'element', elementName ) ) {
			return item;
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/mediaembed.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/mediaembed
 */







/**
 * Provides the General HTML Support integration with {@link module:media-embed/mediaembed~MediaEmbed Media Embed} feature.
 *
 * @extends module:core/plugin~Plugin
 */
class MediaEmbedElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'MediaEmbedElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Stop here if MediaEmbed plugin is not provided or the integrator wants to output markup with previews as
		// we do not support filtering previews.
		if ( !editor.plugins.has( 'MediaEmbed' ) || editor.config.get( 'mediaEmbed.previewsInData' ) ) {
			return;
		}

		const schema = editor.model.schema;
		const conversion = editor.conversion;
		const dataFilter = this.editor.plugins.get( DataFilter );
		const dataSchema = this.editor.plugins.get( DataSchema );
		const mediaElementName = editor.config.get( 'mediaEmbed.elementName' );

		// Overwrite GHS schema definition for a given elementName.
		dataSchema.registerBlockElement( {
			model: 'media',
			view: mediaElementName
		} );

		dataFilter.on( 'register:figure', ( ) => {
			conversion.for( 'upcast' ).add( viewToModelFigureAttributesConverter( dataFilter ) );
		} );

		dataFilter.on( `register:${ mediaElementName }`, ( evt, definition ) => {
			if ( definition.model !== 'media' ) {
				return;
			}

			schema.extend( 'media', {
				allowAttributes: [
					'htmlAttributes',
					'htmlFigureAttributes'
				]
			} );

			conversion.for( 'upcast' ).add( viewToModelMediaAttributesConverter( dataFilter, mediaElementName ) );
			conversion.for( 'dataDowncast' ).add( modelToViewMediaAttributeConverter( mediaElementName ) );

			evt.stop();
		} );
	}
}

function viewToModelMediaAttributesConverter( dataFilter, mediaElementName ) {
	return dispatcher => {
		dispatcher.on( `element:${ mediaElementName }`, upcastMedia );
	};

	function upcastMedia( evt, data, conversionApi ) {
		const viewMediaElement = data.viewItem;

		preserveElementAttributes( viewMediaElement, 'htmlAttributes' );

		function preserveElementAttributes( viewElement, attributeName ) {
			const viewAttributes = dataFilter.processViewAttributes( viewElement, conversionApi );

			if ( viewAttributes ) {
				conversionApi.writer.setAttribute( attributeName, viewAttributes, data.modelRange );
			}
		}
	}
}

// View-to-model conversion helper preserving allowed attributes on {@link module:media-embed/mediaembed~MediaEmbed MediaEmbed}
// feature model element from figure view element.
//
// @private
// @param {module:html-support/datafilter~DataFilter} dataFilter
// @returns {Function} Returns a conversion callback.
function viewToModelFigureAttributesConverter( dataFilter ) {
	return dispatcher => {
		dispatcher.on( 'element:figure', ( evt, data, conversionApi ) => {
			const viewFigureElement = data.viewItem;

			if ( !data.modelRange || !viewFigureElement.hasClass( 'media' ) ) {
				return;
			}

			const viewAttributes = dataFilter.processViewAttributes( viewFigureElement, conversionApi );

			if ( viewAttributes ) {
				conversionApi.writer.setAttribute( 'htmlFigureAttributes', viewAttributes, data.modelRange );
			}
		}, { priority: 'low' } );
	};
}

function modelToViewMediaAttributeConverter( mediaElementName ) {
	return dispatcher => {
		addAttributeConversionDispatcherHandler( mediaElementName, 'htmlAttributes' );
		addAttributeConversionDispatcherHandler( 'figure', 'htmlFigureAttributes' );

		function addAttributeConversionDispatcherHandler( elementName, attributeName ) {
			dispatcher.on( `attribute:${ attributeName }:media`, ( evt, data, conversionApi ) => {
				if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
					return;
				}

				const { attributeOldValue, attributeNewValue } = data;
				const containerElement = conversionApi.mapper.toViewElement( data.item );
				const viewElement = mediaembed_getDescendantElement( conversionApi.writer, containerElement, elementName );

				updateViewAttributes( conversionApi.writer, attributeOldValue, attributeNewValue, viewElement );
			} );
		}
	};
}

// Returns the first view element descendant matching the given view name.
// Includes view element itself.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {module:engine/view/element~Element} containerElement
// @param {String} elementName
// @returns {module:engine/view/element~Element|null}
function mediaembed_getDescendantElement( writer, containerElement, elementName ) {
	const range = writer.createRangeOn( containerElement );

	for ( const { item } of range.getWalker() ) {
		if ( item.is( 'element', elementName ) ) {
			return item;
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/script.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/script
 */






/**
 * Provides the General HTML Support for `script` elements.
 *
 * @extends module:core/plugin~Plugin
 */
class ScriptElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ScriptElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const dataFilter = this.editor.plugins.get( DataFilter );

		dataFilter.on( 'register:script', ( evt, definition ) => {
			const editor = this.editor;
			const schema = editor.model.schema;
			const conversion = editor.conversion;

			schema.register( 'htmlScript', definition.modelSchema );

			schema.extend( 'htmlScript', {
				allowAttributes: [ 'htmlAttributes', 'htmlContent' ],
				isContent: true
			} );

			editor.data.registerRawContentMatcher( {
				name: 'script'
			} );

			conversion.for( 'upcast' ).elementToElement( {
				view: 'script',
				model: viewToModelObjectConverter( definition )
			} );

			conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, dataFilter ) );

			conversion.for( 'downcast' ).elementToElement( {
				model: 'htmlScript',
				view: ( modelElement, { writer } ) => {
					return createObjectView( 'script', modelElement, writer );
				}
			} );

			conversion.for( 'downcast' ).add( modelToViewBlockAttributeConverter( definition ) );

			evt.stop();
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/table.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/table
 */





/**
 * Provides the General HTML Support integration with {@link module:table/table~Table Table} feature.
 *
 * @extends module:core/plugin~Plugin
 */
class TableElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		if ( !editor.plugins.has( 'TableEditing' ) ) {
			return;
		}

		const schema = editor.model.schema;
		const conversion = editor.conversion;
		const dataFilter = editor.plugins.get( DataFilter );

		dataFilter.on( 'register:figure', ( ) => {
			conversion.for( 'upcast' ).add( table_viewToModelFigureAttributeConverter( dataFilter ) );
		} );

		dataFilter.on( 'register:table', ( evt, definition ) => {
			if ( definition.model !== 'table' ) {
				return;
			}

			schema.extend( 'table', {
				allowAttributes: [
					'htmlAttributes',
					// Figure, thead and tbody elements don't have model counterparts.
					// We will be preserving attributes on table element using these attribute keys.
					'htmlFigureAttributes', 'htmlTheadAttributes', 'htmlTbodyAttributes'
				]
			} );

			conversion.for( 'upcast' ).add( viewToModelTableAttributeConverter( dataFilter ) );
			conversion.for( 'downcast' ).add( modelToViewTableAttributeConverter() );

			evt.stop();
		} );
	}
}

// View-to-model conversion helper preserving allowed attributes on {@link module:table/table~Table Table}
// feature model element.
//
// @private
// @param {module:html-support/datafilter~DataFilter} dataFilter
// @returns {Function} Returns a conversion callback.
function viewToModelTableAttributeConverter( dataFilter ) {
	return dispatcher => {
		dispatcher.on( 'element:table', ( evt, data, conversionApi ) => {
			const viewTableElement = data.viewItem;

			preserveElementAttributes( viewTableElement, 'htmlAttributes' );

			for ( const childNode of viewTableElement.getChildren() ) {
				if ( childNode.is( 'element', 'thead' ) ) {
					preserveElementAttributes( childNode, 'htmlTheadAttributes' );
				}

				if ( childNode.is( 'element', 'tbody' ) ) {
					preserveElementAttributes( childNode, 'htmlTbodyAttributes' );
				}
			}

			function preserveElementAttributes( viewElement, attributeName ) {
				const viewAttributes = dataFilter.processViewAttributes( viewElement, conversionApi );

				if ( viewAttributes ) {
					conversionApi.writer.setAttribute( attributeName, viewAttributes, data.modelRange );
				}
			}
		} );
	};
}

// View-to-model conversion helper preserving allowed attributes on {@link module:table/table~Table Table}
// feature model element from figure view element.
//
// @private
// @param {module:html-support/datafilter~DataFilter} dataFilter
// @returns {Function} Returns a conversion callback.
function table_viewToModelFigureAttributeConverter( dataFilter ) {
	return dispatcher => {
		dispatcher.on( 'element:figure', ( evt, data, conversionApi ) => {
			const viewFigureElement = data.viewItem;

			if ( !data.modelRange || !viewFigureElement.hasClass( 'table' ) ) {
				return;
			}

			const viewAttributes = dataFilter.processViewAttributes( viewFigureElement, conversionApi );

			if ( viewAttributes ) {
				conversionApi.writer.setAttribute( 'htmlFigureAttributes', viewAttributes, data.modelRange );
			}
		}, { priority: 'low' } );
	};
}

// Model-to-view conversion helper applying attributes from {@link module:table/table~Table Table}
// feature.
//
// @private
// @returns {Function} Returns a conversion callback.
function modelToViewTableAttributeConverter() {
	return dispatcher => {
		addAttributeConversionDispatcherHandler( 'table', 'htmlAttributes' );
		addAttributeConversionDispatcherHandler( 'figure', 'htmlFigureAttributes' );
		addAttributeConversionDispatcherHandler( 'thead', 'htmlTheadAttributes' );
		addAttributeConversionDispatcherHandler( 'tbody', 'htmlTbodyAttributes' );

		function addAttributeConversionDispatcherHandler( elementName, attributeName ) {
			dispatcher.on( `attribute:${ attributeName }:table`, ( evt, data, conversionApi ) => {
				if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
					return;
				}

				const containerElement = conversionApi.mapper.toViewElement( data.item );
				const viewElement = table_getDescendantElement( conversionApi.writer, containerElement, elementName );

				setViewAttributes( conversionApi.writer, data.attributeNewValue, viewElement );
			} );
		}
	};
}

// Returns the first view element descendant matching the given view name.
// Includes view element itself.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {module:engine/view/element~Element} containerElement
// @param {String} elementName
// @returns {module:engine/view/element~Element|null}
function table_getDescendantElement( writer, containerElement, elementName ) {
	const range = writer.createRangeOn( containerElement );

	for ( const { item } of range.getWalker() ) {
		if ( item.is( 'element', elementName ) ) {
			return item;
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/style.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/style
 */






/**
 * Provides the General HTML Support for `style` elements.
 *
 * @extends module:core/plugin~Plugin
 */
class StyleElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'StyleElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const dataFilter = this.editor.plugins.get( DataFilter );

		dataFilter.on( 'register:style', ( evt, definition ) => {
			const editor = this.editor;
			const schema = editor.model.schema;
			const conversion = editor.conversion;

			schema.register( 'htmlStyle', definition.modelSchema );

			schema.extend( 'htmlStyle', {
				allowAttributes: [ 'htmlAttributes', 'htmlContent' ],
				isContent: true
			} );

			editor.data.registerRawContentMatcher( {
				name: 'style'
			} );

			conversion.for( 'upcast' ).elementToElement( {
				view: 'style',
				model: viewToModelObjectConverter( definition )
			} );

			conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, dataFilter ) );

			conversion.for( 'downcast' ).elementToElement( {
				model: 'htmlStyle',
				view: ( modelElement, { writer } ) => {
					return createObjectView( 'style', modelElement, writer );
				}
			} );

			conversion.for( 'downcast' ).add( modelToViewBlockAttributeConverter( definition ) );

			evt.stop();
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/documentlist.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/documentlist
 */







/**
 * Provides the General HTML Support integration with the {@link module:list/documentlist~DocumentList Document List} feature.
 *
 * @extends module:core/plugin~Plugin
 */
class DocumentListElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DocumentListElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		if ( !editor.plugins.has( 'DocumentListEditing' ) ) {
			return;
		}

		const schema = editor.model.schema;
		const conversion = editor.conversion;
		const dataFilter = editor.plugins.get( DataFilter );
		const documentListEditing = editor.plugins.get( 'DocumentListEditing' );

		// Register downcast strategy.
		// Note that this must be done before document list editing registers conversion in afterInit.
		documentListEditing.registerDowncastStrategy( {
			scope: 'item',
			attributeName: 'htmlLiAttributes',

			setAttributeOnDowncast( writer, attributeValue, viewElement ) {
				setViewAttributes( writer, attributeValue, viewElement );
			}
		} );

		documentListEditing.registerDowncastStrategy( {
			scope: 'list',
			attributeName: 'htmlListAttributes',

			setAttributeOnDowncast( writer, viewAttributes, viewElement ) {
				setViewAttributes( writer, viewAttributes, viewElement );
			}
		} );

		dataFilter.on( 'register', ( evt, definition ) => {
			if ( ![ 'ul', 'ol', 'li' ].includes( definition.view ) ) {
				return;
			}

			evt.stop();

			// Do not register same converters twice.
			if ( schema.checkAttribute( '$block', 'htmlListAttributes' ) ) {
				return;
			}

			schema.extend( '$block', { allowAttributes: [ 'htmlListAttributes', 'htmlLiAttributes' ] } );
			schema.extend( '$blockObject', { allowAttributes: [ 'htmlListAttributes', 'htmlLiAttributes' ] } );
			schema.extend( '$container', { allowAttributes: [ 'htmlListAttributes', 'htmlLiAttributes' ] } );

			conversion.for( 'upcast' ).add( dispatcher => {
				dispatcher.on( 'element:ul', viewToModelListAttributeConverter( 'htmlListAttributes', dataFilter ), { priority: 'low' } );
				dispatcher.on( 'element:ol', viewToModelListAttributeConverter( 'htmlListAttributes', dataFilter ), { priority: 'low' } );
				dispatcher.on( 'element:li', viewToModelListAttributeConverter( 'htmlLiAttributes', dataFilter ), { priority: 'low' } );
			} );
		} );

		// Make sure that all items in a single list (items at the same level & listType) have the same properties.
		// Note: This is almost an exact copy from DocumentListPropertiesEditing.
		documentListEditing.on( 'postFixer', ( evt, { listNodes, writer } ) => {
			const previousNodesByIndent = []; // Last seen nodes of lower indented lists.

			for ( const { node, previous } of listNodes ) {
				// For the first list block there is nothing to compare with.
				if ( !previous ) {
					continue;
				}

				const nodeIndent = node.getAttribute( 'listIndent' );
				const previousNodeIndent = previous.getAttribute( 'listIndent' );

				let previousNodeInList = null; // It's like `previous` but has the same indent as current node.

				// Let's find previous node for the same indent.
				// We're going to need that when we get back to previous indent.
				if ( nodeIndent > previousNodeIndent ) {
					previousNodesByIndent[ previousNodeIndent ] = previous;
				}
				// Restore the one for given indent.
				else if ( nodeIndent < previousNodeIndent ) {
					previousNodeInList = previousNodesByIndent[ nodeIndent ];
					previousNodesByIndent.length = nodeIndent;
				}
				// Same indent.
				else {
					previousNodeInList = previous;
				}

				// This is a first item of a nested list.
				if ( !previousNodeInList ) {
					continue;
				}

				if ( previousNodeInList.getAttribute( 'listType' ) == node.getAttribute( 'listType' ) ) {
					const value = previousNodeInList.getAttribute( 'htmlListAttributes' );

					if ( !lodash_es_isEqual( node.getAttribute( 'htmlListAttributes' ), value ) ) {
						writer.setAttribute( 'htmlListAttributes', value, node );
						evt.return = true;
					}
				}

				if ( previousNodeInList.getAttribute( 'listItemId' ) == node.getAttribute( 'listItemId' ) ) {
					const value = previousNodeInList.getAttribute( 'htmlLiAttributes' );

					if ( !lodash_es_isEqual( node.getAttribute( 'htmlLiAttributes' ), value ) ) {
						writer.setAttribute( 'htmlLiAttributes', value, node );
						evt.return = true;
					}
				}
			}
		} );
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const editor = this.editor;

		if ( !editor.commands.get( 'indentList' ) ) {
			return;
		}

		// Reset list attributes after indenting list items.
		this.listenTo( editor.commands.get( 'indentList' ), 'afterExecute', ( evt, changedBlocks ) => {
			editor.model.change( writer => {
				for ( const node of changedBlocks ) {
					// Just reset the attribute.
					// If there is a previous indented list that this node should be merged into,
					// the postfixer will unify all the attributes of both sub-lists.
					writer.setAttribute( 'htmlListAttributes', {}, node );
				}
			} );
		} );
	}
}

// View-to-model conversion helper preserving allowed attributes on {@link TODO}
// feature model element.
//
// @private
// @param {String} attributeName
// @param {module:html-support/datafilter~DataFilter} dataFilter
// @returns {Function} Returns a conversion callback.
function viewToModelListAttributeConverter( attributeName, dataFilter ) {
	return ( evt, data, conversionApi ) => {
		const viewElement = data.viewItem;

		if ( !data.modelRange ) {
			Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
		}

		const viewAttributes = dataFilter.processViewAttributes( viewElement, conversionApi );

		for ( const item of data.modelRange.getItems( { shallow: true } ) ) {
			// Apply only to list item blocks.
			if ( !item.hasAttribute( 'listItemId' ) ) {
				continue;
			}

			// Set list attributes only on same level items, those nested deeper are already handled
			// by the recursive conversion.
			if ( item.hasAttribute( attributeName ) ) {
				continue;
			}

			conversionApi.writer.setAttribute( attributeName, viewAttributes || {}, item );
		}
	};
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/integrations/customelement.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/integrations/customelement
 */

/* globals document */








/**
 * Provides the General HTML Support for custom elements (not registered in the {@link module:html-support/dataschema~DataSchema}).
 *
 * @extends module:core/plugin~Plugin
 */
class CustomElementSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DataFilter, DataSchema ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'CustomElementSupport';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const dataFilter = this.editor.plugins.get( DataFilter );
		const dataSchema = this.editor.plugins.get( DataSchema );

		dataFilter.on( 'register:$customElement', ( evt, definition ) => {
			evt.stop();

			const editor = this.editor;
			const schema = editor.model.schema;
			const conversion = editor.conversion;
			const unsafeElements = editor.editing.view.domConverter.unsafeElements;
			const preLikeElements = editor.data.htmlProcessor.domConverter.preElements;

			schema.register( definition.model, definition.modelSchema );
			schema.extend( definition.model, {
				allowAttributes: [ 'htmlElementName', 'htmlAttributes', 'htmlContent' ],
				isContent: true
			} );

			// Being executed on the low priority, it will catch all elements that were not caught by other converters.
			conversion.for( 'upcast' ).elementToElement( {
				view: /.*/,
				model: ( viewElement, conversionApi ) => {
					// Do not try to convert $comment fake element.
					if ( viewElement.name == '$comment' ) {
						return;
					}

					if ( !isValidElementName( viewElement.name ) ) {
						return;
					}

					// Allow for fallback only if this element is not defined in data schema to make sure
					// that this will handle only custom elements not registered in the data schema.
					if ( dataSchema.getDefinitionsForView( viewElement.name ).size ) {
						return;
					}

					// Make sure that this element will not render in the editing view.
					if ( !unsafeElements.includes( viewElement.name ) ) {
						unsafeElements.push( viewElement.name );
					}

					// Make sure that whitespaces will not be trimmed or replaced by nbsps while stringify content.
					if ( !preLikeElements.includes( viewElement.name ) ) {
						preLikeElements.push( viewElement.name );
					}

					const modelElement = conversionApi.writer.createElement( definition.model, {
						htmlElementName: viewElement.name
					} );

					const htmlAttributes = dataFilter.processViewAttributes( viewElement, conversionApi );

					if ( htmlAttributes ) {
						conversionApi.writer.setAttribute( 'htmlAttributes', htmlAttributes, modelElement );
					}

					// Store the whole element in the attribute so that DomConverter will be able to use the pre like element context.
					const viewWriter = new UpcastWriter( viewElement.document );
					const documentFragment = viewWriter.createDocumentFragment( viewElement );
					const htmlContent = editor.data.processor.toData( documentFragment );

					conversionApi.writer.setAttribute( 'htmlContent', htmlContent, modelElement );

					// Consume the content of the element.
					for ( const { item } of editor.editing.view.createRangeIn( viewElement ) ) {
						conversionApi.consumable.consume( item, { name: true } );
					}

					return modelElement;
				},
				converterPriority: 'low'
			} );

			// Because this element is unsafe (DomConverter#unsafeElements), it will render as a transparent <span> but it must
			// be rendered anyway for the mapping between the model and the view to exist.
			conversion.for( 'editingDowncast' ).elementToElement( {
				model: {
					name: definition.model,
					attributes: [ 'htmlElementName', 'htmlAttributes', 'htmlContent' ]
				},
				view: ( modelElement, { writer } ) => {
					const viewName = modelElement.getAttribute( 'htmlElementName' );
					const viewElement = writer.createRawElement( viewName );

					if ( modelElement.hasAttribute( 'htmlAttributes' ) ) {
						setViewAttributes( writer, modelElement.getAttribute( 'htmlAttributes' ), viewElement );
					}

					return viewElement;
				}
			} );

			conversion.for( 'dataDowncast' ).elementToElement( {
				model: {
					name: definition.model,
					attributes: [ 'htmlElementName', 'htmlAttributes', 'htmlContent' ]
				},
				view: ( modelElement, { writer } ) => {
					const viewName = modelElement.getAttribute( 'htmlElementName' );
					const htmlContent = modelElement.getAttribute( 'htmlContent' );

					const viewElement = writer.createRawElement( viewName, null, ( domElement, domConverter ) => {
						domConverter.setContentOf( domElement, htmlContent );

						// Unwrap the custom element content (it was stored in the attribute as the whole custom element).
						// See the upcast conversion for the "htmlContent" attribute to learn more.
						const customElement = domElement.firstChild;

						customElement.remove();

						while ( customElement.firstChild ) {
							domElement.appendChild( customElement.firstChild );
						}
					} );

					if ( modelElement.hasAttribute( 'htmlAttributes' ) ) {
						setViewAttributes( writer, modelElement.getAttribute( 'htmlAttributes' ), viewElement );
					}

					return viewElement;
				}
			} );
		} );
	}
}

// Returns true if name is valid for a DOM element name.
function isValidElementName( name ) {
	try {
		document.createElement( name );
	} catch ( error ) {
		return false;
	}

	return true;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/generalhtmlsupport.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/generalhtmlsupport
 */
















/**
 * The General HTML Support feature.
 *
 * This is a "glue" plugin which initializes the {@link module:html-support/datafilter~DataFilter data filter} configuration
 * and features integration with the General HTML Support.
 *
 * @extends module:core/plugin~Plugin
 */
class GeneralHtmlSupport extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'GeneralHtmlSupport';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [
			DataFilter,
			CodeBlockElementSupport,
			DualContentModelElementSupport,
			HeadingElementSupport,
			ImageElementSupport,
			MediaEmbedElementSupport,
			ScriptElementSupport,
			TableElementSupport,
			StyleElementSupport,
			DocumentListElementSupport,
			CustomElementSupport
		];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const dataFilter = editor.plugins.get( DataFilter );

		// Load the filtering configuration.
		dataFilter.loadAllowedConfig( editor.config.get( 'htmlSupport.allow' ) || [] );
		dataFilter.loadDisallowedConfig( editor.config.get( 'htmlSupport.disallow' ) || [] );
	}

	/**
	 * Returns a GHS model attribute name related to a given view element name.
	 *
	 * @protected
	 * @param {String} viewElementName A view element name.
	 * @returns {String}
	 */
	getGhsAttributeNameForElement( viewElementName ) {
		const dataSchema = this.editor.plugins.get( 'DataSchema' );
		const definitions = Array.from( dataSchema.getDefinitionsForView( viewElementName, false ) );

		if ( definitions && definitions.length && definitions[ 0 ].isInline && !definitions[ 0 ].isObject ) {
			return definitions[ 0 ].model;
		}

		return 'htmlAttributes';
	}

	/**
	 * Updates GHS model attribute for a specified view element name, so it includes the given class name.
	 *
	 * @protected
	 * @param {String} viewElementName A view element name.
	 * @param {String|Array.<String>} className The css class to add.
	 * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
	 */
	addModelHtmlClass( viewElementName, className, selectable ) {
		const model = this.editor.model;
		const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

		model.change( writer => {
			for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
				modifyGhsAttribute( writer, item, ghsAttributeName, 'classes', classes => {
					for ( const value of toArray( className ) ) {
						classes.add( value );
					}
				} );
			}
		} );
	}

	/**
	 * Updates GHS model attribute for a specified view element name, so it does not include the given class name.
	 *
	 * @protected
	 * @param {String} viewElementName A view element name.
	 * @param {String|Array.<String>} className The css class to remove.
	 * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
	 */
	removeModelHtmlClass( viewElementName, className, selectable ) {
		const model = this.editor.model;
		const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

		model.change( writer => {
			for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
				modifyGhsAttribute( writer, item, ghsAttributeName, 'classes', classes => {
					for ( const value of toArray( className ) ) {
						classes.delete( value );
					}
				} );
			}
		} );
	}

	/**
	 * Updates GHS model attribute for a specified view element name, so it includes the given attribute.
	 *
	 * @protected
	 * @param {String} viewElementName A view element name.
	 * @param {Object} attributes The object with attributes to set.
	 * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
	 */
	setModelHtmlAttributes( viewElementName, attributes, selectable ) {
		const model = this.editor.model;
		const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

		model.change( writer => {
			for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
				modifyGhsAttribute( writer, item, ghsAttributeName, 'attributes', attributesMap => {
					for ( const [ key, value ] of Object.entries( attributes ) ) {
						attributesMap.set( key, value );
					}
				} );
			}
		} );
	}

	/**
	 * Updates GHS model attribute for a specified view element name, so it does not include the given attribute.
	 *
	 * @protected
	 * @param {String} viewElementName A view element name.
	 * @param {String|Array.<String>} attributeName The attribute name (or names) to remove.
	 * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
	 */
	removeModelHtmlAttributes( viewElementName, attributeName, selectable ) {
		const model = this.editor.model;
		const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

		model.change( writer => {
			for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
				modifyGhsAttribute( writer, item, ghsAttributeName, 'attributes', attributesMap => {
					for ( const key of toArray( attributeName ) ) {
						attributesMap.delete( key );
					}
				} );
			}
		} );
	}

	/**
	 * Updates GHS model attribute for a specified view element name, so it includes a given style.
	 *
	 * @protected
	 * @param {String} viewElementName A view element name.
	 * @param {Object} styles The object with styles to set.
	 * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
	 */
	setModelHtmlStyles( viewElementName, styles, selectable ) {
		const model = this.editor.model;
		const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

		model.change( writer => {
			for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
				modifyGhsAttribute( writer, item, ghsAttributeName, 'styles', stylesMap => {
					for ( const [ key, value ] of Object.entries( styles ) ) {
						stylesMap.set( key, value );
					}
				} );
			}
		} );
	}

	/**
	 * Updates GHS model attribute for a specified view element name, so it does not include a given style.
	 *
	 * @protected
	 * @param {String} viewElementName A view element name.
	 * @param {String|Array.<String>} properties The style (or styles list) to remove.
	 * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
	 */
	removeModelHtmlStyles( viewElementName, properties, selectable ) {
		const model = this.editor.model;
		const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );

		model.change( writer => {
			for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
				modifyGhsAttribute( writer, item, ghsAttributeName, 'styles', stylesMap => {
					for ( const key of toArray( properties ) ) {
						stylesMap.delete( key );
					}
				} );
			}
		} );
	}
}

// Returns an iterator over an items in the selectable that accept given GHS attribute.
function* getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) {
	if ( selectable.is( 'documentSelection' ) && selectable.isCollapsed ) {
		if ( model.schema.checkAttributeInSelection( selectable, ghsAttributeName ) ) {
			yield selectable;
		}
	} else {
		for ( const range of getValidRangesForSelectable( model, selectable, ghsAttributeName ) ) {
			yield* range.getItems( { shallow: true } );
		}
	}
}

// Translates a given selectable to an iterable of ranges.
function getValidRangesForSelectable( model, selectable, ghsAttributeName ) {
	if ( selectable.is( 'node' ) || selectable.is( '$text' ) || selectable.is( '$textProxy' ) ) {
		if ( model.schema.checkAttribute( selectable, ghsAttributeName ) ) {
			return [ model.createRangeOn( selectable ) ];
		} else {
			return [];
		}
	} else {
		return model.schema.getValidRanges( model.createSelection( selectable ).getRanges(), ghsAttributeName );
	}
}

// Updates a GHS attribute on a specified item.
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/item~Item|module:engine/model/documentselection~DocumentSelection} item
// @param {String} ghsAttributeName
// @param {'classes'|'attributes'|'styles'} subject
// @param {Function} callback That receives a map or set as an argument and should modify it (add or remove entries).
function modifyGhsAttribute( writer, item, ghsAttributeName, subject, callback ) {
	const oldValue = item.getAttribute( ghsAttributeName );
	const newValue = {};

	for ( const kind of [ 'attributes', 'styles', 'classes' ] ) {
		if ( kind != subject ) {
			if ( oldValue && oldValue[ kind ] ) {
				newValue[ kind ] = oldValue[ kind ];
			}
		} else {
			const values = kind == 'classes' ?
				new Set( oldValue && oldValue[ kind ] || [] ) :
				new Map( Object.entries( oldValue && oldValue[ kind ] || {} ) );

			callback( values );

			if ( values.size ) {
				newValue[ kind ] = kind == 'classes' ? Array.from( values ) : Object.fromEntries( values );
			}
		}
	}

	if ( Object.keys( newValue ).length ) {
		if ( item.is( 'documentSelection' ) ) {
			writer.setSelectionAttribute( ghsAttributeName, newValue );
		} else {
			writer.setAttribute( ghsAttributeName, newValue, item );
		}
	} else if ( oldValue ) {
		if ( item.is( 'documentSelection' ) ) {
			writer.removeSelectionAttribute( ghsAttributeName );
		} else {
			writer.removeAttribute( ghsAttributeName, item );
		}
	}
}

/**
 * The configuration of the General HTML Support feature.
 * Introduced by the {@link module:html-support/generalhtmlsupport~GeneralHtmlSupport} feature.
 *
 * Read more in {@link module:html-support/generalhtmlsupport~GeneralHtmlSupportConfig}.
 *
 * @member {module:htmlsupport/generalhtmlsupport~GeneralHtmlSupportConfig} module:core/editor/editorconfig~EditorConfig#htmlSupport
 */

/**
 * The configuration of the General HTML Support feature.
 * The option is used by the {@link module:html-support/generalhtmlsupport~GeneralHtmlSupport} feature.
 *
 *		ClassicEditor
 *			.create( {
 * 				htmlSupport: ... // General HTML Support feature config.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface GeneralHtmlSupportConfig
 */

/**
 * The configuration of allowed content rules used by General HTML Support.
 *
 * Setting this configuration option will enable HTML features that are not explicitly supported by any other dedicated CKEditor 5 features.
 *
 * 		const htmlSupportConfig.allow = [
 * 			{
 * 				name: 'div',                      // Enable 'div' element support,
 * 				classes: [ 'special-container' ], // allow 'special-container' class,
 * 				styles: 'background',             // allow 'background' style,
 * 				attributes: true                  // allow any attribute (can be empty).
 * 			},
 * 			{
 * 				name: 'p',                                   // Extend existing Paragraph feature,
 * 				classes: 'highlighted'                       // with 'highlighted' class,
 * 				attributes: [
 * 					{ key: 'data-i18n-context, value: true } // and i18n attribute.
 * 				]
 * 			}
 * 		];
 *
 * @member {Array.<module:engine/view/matcher~MatcherPattern>} module:html-support/generalhtmlsupport~GeneralHtmlSupportConfig#allow
 */

/**
 * The configuration of disallowed content rules used by General HTML Support.
 *
 * Setting this configuration option will disable listed HTML features.
 *
 * 		const htmlSupportConfig.disallow = [
 * 			{
 * 				name: /[\s\S]+/    // For every HTML feature,
 * 				attributes: {
 * 					key: /^on.*$/ // disable 'on*' attributes, like 'onClick', 'onError' etc.
 * 				}
 * 			}
 * 		];
 * @member {Array.<module:engine/view/matcher~MatcherPattern>} module:html-support/generalhtmlsupport~GeneralHtmlSupportConfig#disallow
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paragraph/src/paragraphcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module paragraph/paragraphcommand
 */


/**
 * The paragraph command.
 *
 * @extends module:core/command~Command
 */
class ParagraphCommand extends command_Command {
    /**
     * @inheritDoc
     */
    refresh() {
        const model = this.editor.model;
        const document = model.document;
        const block = first_first(document.selection.getSelectedBlocks());
        this.value = !!block && block.is('element', 'paragraph');
        this.isEnabled = !!block && checkCanBecomeParagraph(block, model.schema);
    }
    /**
     * Executes the command. All the blocks (see {@link module:engine/model/schema~Schema}) in the selection
     * will be turned to paragraphs.
     *
     * @fires execute
     * @param {Object} [options] Options for the executed command.
     * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} [options.selection]
     * The selection that the command should be applied to.
     * By default, if not provided, the command is applied to the {@link module:engine/model/document~Document#selection}.
     */
    execute(options = {}) {
        const model = this.editor.model;
        const document = model.document;
        model.change(writer => {
            const blocks = (options.selection || document.selection).getSelectedBlocks();
            for (const block of blocks) {
                if (!block.is('element', 'paragraph') && checkCanBecomeParagraph(block, model.schema)) {
                    writer.rename(block, 'paragraph');
                }
            }
        });
    }
}
// Checks whether the given block can be replaced by a paragraph.
//
// @private
// @param {module:engine/model/element~Element} block A block to be tested.
// @param {module:engine/model/schema~Schema} schema The schema of the document.
// @returns {Boolean}
function checkCanBecomeParagraph(block, schema) {
    return schema.checkChild(block.parent, 'paragraph') && !schema.isObject(block);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paragraph/src/insertparagraphcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module paragraph/insertparagraphcommand
 */

/**
 * The insert paragraph command. It inserts a new paragraph at a specific
 * {@link module:engine/model/position~Position document position}.
 *
 *		// Insert a new paragraph before an element in the document.
 *		editor.execute( 'insertParagraph', {
 *			position: editor.model.createPositionBefore( element )
 *		} );
 *
 * If a paragraph is disallowed in the context of the specific position, the command
 * will attempt to split position ancestors to find a place where it is possible
 * to insert a paragraph.
 *
 * **Note**: This command moves the selection to the inserted paragraph.
 *
 * @extends module:core/command~Command
 */
class InsertParagraphCommand extends command_Command {
    /**
     * Executes the command.
     *
     * @param {Object} options Options for the executed command.
     * @param {module:engine/model/position~Position} options.position The model position at which
     * the new paragraph will be inserted.
     * @param {Object} attributes Attributes keys and values to set on a inserted paragraph
     * @fires execute
     */
    execute(options) {
        const model = this.editor.model;
        const attributes = options.attributes;
        let position = options.position;
        model.change(writer => {
            const paragraph = writer.createElement('paragraph');
            if (attributes) {
                model.schema.setAllowedAttributes(paragraph, attributes, writer);
            }
            if (!model.schema.checkChild(position.parent, paragraph)) {
                const allowedParent = model.schema.findAllowedParent(position, paragraph);
                // It could be there's no ancestor limit that would allow paragraph.
                // In theory, "paragraph" could be disallowed even in the "$root".
                if (!allowedParent) {
                    return;
                }
                position = writer.split(position, allowedParent).position;
            }
            model.insertContent(paragraph, position);
            writer.setSelection(paragraph, 'in');
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paragraph/src/paragraph.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module paragraph/paragraph
 */



/**
 * The paragraph feature for the editor.
 *
 * It introduces the `<paragraph>` element in the model which renders as a `<p>` element in the DOM and data.
 *
 * It also brings two editors commands:
 *
 * * The {@link module:paragraph/paragraphcommand~ParagraphCommand `'paragraph'`} command that converts all
 * blocks in the model selection into paragraphs.
 * * The {@link module:paragraph/insertparagraphcommand~InsertParagraphCommand `'insertParagraph'`} command
 * that inserts a new paragraph at a specified location in the model.
 *
 * @extends module:core/plugin~Plugin
 */
class Paragraph extends plugin_Plugin {
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Paragraph';
    }
    /**
     * @inheritDoc
     */
    init() {
        const editor = this.editor;
        const model = editor.model;
        editor.commands.add('paragraph', new ParagraphCommand(editor));
        editor.commands.add('insertParagraph', new InsertParagraphCommand(editor));
        // Schema.
        model.schema.register('paragraph', { inheritAllFrom: '$block' });
        editor.conversion.elementToElement({ model: 'paragraph', view: 'p' });
        // Conversion for paragraph-like elements which has not been converted by any plugin.
        editor.conversion.for('upcast').elementToElement({
            model: (viewElement, { writer }) => {
                if (!Paragraph.paragraphLikeElements.has(viewElement.name)) {
                    return null;
                }
                // Do not auto-paragraph empty elements.
                if (viewElement.isEmpty) {
                    return null;
                }
                return writer.createElement('paragraph');
            },
            view: /.+/,
            converterPriority: 'low'
        });
    }
}
/**
 * A list of element names which should be treated by the autoparagraphing algorithms as
 * paragraph-like. This means that e.g. the following content:
 *
 *		<h1>Foo</h1>
*		<table>
*			<tr>
*				<td>X</td>
*				<td>
*					<ul>
*						<li>Y</li>
*						<li>Z</li>
*					</ul>
*				</td>
*			</tr>
*		</table>
*
* contains five paragraph-like elements: `<h1>`, two `<td>`s and two `<li>`s.
* Hence, if none of the features is going to convert those elements the above content will be automatically handled
* by the paragraph feature and converted to:
*
*		<p>Foo</p>
*		<p>X</p>
*		<p>Y</p>
*		<p>Z</p>
*
* Note: The `<td>` containing two `<li>` elements was ignored as the innermost paragraph-like elements
* have a priority upon conversion.
*
* @member {Set.<String>} module:paragraph/paragraph~Paragraph.paragraphLikeElements
*/
Paragraph.paragraphLikeElements = new Set([
    'blockquote',
    'dd',
    'div',
    'dt',
    'h1',
    'h2',
    'h3',
    'h4',
    'h5',
    'h6',
    'li',
    'p',
    'td',
    'th'
]);

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paragraph/src/paragraphbuttonui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module paragraph/paragraphbuttonui
 */



/**
 * This plugin defines the `'paragraph'` button. It can be used together with
 * {@link module:heading/headingbuttonsui~HeadingButtonsUI} to replace the standard heading dropdown.
 *
 * This plugin is not loaded automatically by the {@link module:paragraph/paragraph~Paragraph} plugin. It must
 * be added manually.
 *
 *		ClassicEditor
 *			.create( {
 *				plugins: [ ..., Heading, Paragraph, HeadingButtonsUI, ParagraphButtonUI ]
 *				toolbar: [ 'paragraph', 'heading1', 'heading2', 'heading3' ]
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @extends module:core/plugin~Plugin
 */
class ParagraphButtonUI extends (/* unused pure expression or super */ null && (Plugin)) {
    init() {
        const editor = this.editor;
        const t = editor.t;
        editor.ui.componentFactory.add('paragraph', locale => {
            const view = new ButtonView(locale);
            const command = editor.commands.get('paragraph');
            view.label = t('Paragraph');
            view.icon = icon;
            view.tooltip = true;
            view.isToggleable = true;
            view.bind('isEnabled').to(command);
            view.bind('isOn').to(command, 'value');
            view.on('execute', () => {
                editor.execute('paragraph');
            });
            return view;
        });
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paragraph/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module paragraph
 */



;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/paragraph.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/paragraph
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-heading/src/headingcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module heading/headingcommand
 */




/**
 * The heading command. It is used by the {@link module:heading/heading~Heading heading feature} to apply headings.
 *
 * @extends module:core/command~Command
 */
class HeadingCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor Editor instance.
	 * @param {Array.<String>} modelElements Names of the element which this command can apply in the model.
	 */
	constructor( editor, modelElements ) {
		super( editor );

		/**
		 * If the selection starts in a heading (which {@link #modelElements is supported by this command})
		 * the value is set to the name of that heading model element.
		 * It is  set to `false` otherwise.
		 *
		 * @observable
		 * @readonly
		 * @member {Boolean|String} #value
		 */

		/**
		 * Set of defined model's elements names that this command support.
		 * See {@link module:heading/heading~HeadingOption}.
		 *
		 * @readonly
		 * @member {Array.<String>}
		 */
		this.modelElements = modelElements;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const block = first_first( this.editor.model.document.selection.getSelectedBlocks() );

		this.value = !!block && this.modelElements.includes( block.name ) && block.name;
		this.isEnabled = !!block && this.modelElements.some( heading => checkCanBecomeHeading( block, heading, this.editor.model.schema ) );
	}

	/**
	 * Executes the command. Applies the heading to the selected blocks or, if the first selected
	 * block is a heading already, turns selected headings (of this level only) to paragraphs.
	 *
	 * @param {Object} options
	 * @param {String} options.value Name of the element which this command will apply in the model.
	 * @fires execute
	 */
	execute( options ) {
		const model = this.editor.model;
		const document = model.document;

		const modelElement = options.value;

		model.change( writer => {
			const blocks = Array.from( document.selection.getSelectedBlocks() )
				.filter( block => {
					return checkCanBecomeHeading( block, modelElement, model.schema );
				} );

			for ( const block of blocks ) {
				if ( !block.is( 'element', modelElement ) ) {
					writer.rename( block, modelElement );
				}
			}
		} );
	}
}

// Checks whether the given block can be replaced by a specific heading.
//
// @private
// @param {module:engine/model/element~Element} block A block to be tested.
// @param {module:heading/headingcommand~HeadingCommand#modelElement} heading Command element name in the model.
// @param {module:engine/model/schema~Schema} schema The schema of the document.
// @returns {Boolean}
function checkCanBecomeHeading( block, heading, schema ) {
	return schema.checkChild( block.parent, heading ) && !schema.isObject( block );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-heading/src/headingediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module heading/headingediting
 */







const defaultModelElement = 'paragraph';

/**
 * The headings engine feature. It handles switching between block formats &ndash; headings and paragraph.
 * This class represents the engine part of the heading feature. See also {@link module:heading/heading~Heading}.
 * It introduces `heading1`-`headingN` commands which allow to convert paragraphs into headings.
 *
 * @extends module:core/plugin~Plugin
 */
class HeadingEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HeadingEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'heading', {
			options: [
				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
				{ model: 'heading1', view: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
				{ model: 'heading2', view: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
				{ model: 'heading3', view: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
			]
		} );
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ Paragraph ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const options = editor.config.get( 'heading.options' );

		const modelElements = [];

		for ( const option of options ) {
			// Skip paragraph - it is defined in required Paragraph feature.
			if ( option.model !== defaultModelElement ) {
				// Schema.
				editor.model.schema.register( option.model, {
					inheritAllFrom: '$block'
				} );

				editor.conversion.elementToElement( option );

				modelElements.push( option.model );
			}
		}

		this._addDefaultH1Conversion( editor );

		// Register the heading command for this option.
		editor.commands.add( 'heading', new HeadingCommand( editor, modelElements ) );
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		// If the enter command is added to the editor, alter its behavior.
		// Enter at the end of a heading element should create a paragraph.
		const editor = this.editor;
		const enterCommand = editor.commands.get( 'enter' );
		const options = editor.config.get( 'heading.options' );

		if ( enterCommand ) {
			this.listenTo( enterCommand, 'afterExecute', ( evt, data ) => {
				const positionParent = editor.model.document.selection.getFirstPosition().parent;
				const isHeading = options.some( option => positionParent.is( 'element', option.model ) );

				if ( isHeading && !positionParent.is( 'element', defaultModelElement ) && positionParent.childCount === 0 ) {
					data.writer.rename( positionParent, defaultModelElement );
				}
			} );
		}
	}

	/**
	 * Adds default conversion for `h1` -> `heading1` with a low priority.
	 *
	 * @private
	 * @param {module:core/editor/editor~Editor} editor Editor instance on which to add the `h1` conversion.
	 */
	_addDefaultH1Conversion( editor ) {
		editor.conversion.for( 'upcast' ).elementToElement( {
			model: 'heading1',
			view: 'h1',
			// With a `low` priority, `paragraph` plugin autoparagraphing mechanism is executed. Make sure
			// this listener is called before it. If not, `h1` will be transformed into a paragraph.
			converterPriority: src_priorities.get( 'low' ) + 1
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-heading/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module heading/utils
 */

/**
 * Returns heading options as defined in `config.heading.options` but processed to consider
 * the editor localization, i.e. to display {@link module:heading/heading~HeadingOption}
 * in the correct language.
 *
 * Note: The reason behind this method is that 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.
 *
 * @param {module:core/editor/editor~Editor} editor
 * @returns {Array.<module:heading/heading~HeadingOption>}.
 */
function getLocalizedOptions( editor ) {
	const t = editor.t;
	const localizedTitles = {
		Paragraph: t( 'Paragraph' ),
		'Heading 1': t( 'Heading 1' ),
		'Heading 2': t( 'Heading 2' ),
		'Heading 3': t( 'Heading 3' ),
		'Heading 4': t( 'Heading 4' ),
		'Heading 5': t( 'Heading 5' ),
		'Heading 6': t( 'Heading 6' )
	};

	return editor.config.get( 'heading.options' ).map( option => {
		const title = localizedTitles[ option.title ];

		if ( title && title != option.title ) {
			option.title = title;
		}

		return option;
	} );
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-heading/theme/heading.css
var heading = __webpack_require__(3230);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-heading/theme/heading.css

            

var heading_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

heading_options.insert = "head";
heading_options.singleton = true;

var heading_update = injectStylesIntoStyleTag_default()(heading/* default */.Z, heading_options);



/* harmony default export */ const theme_heading = (heading/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-heading/src/headingui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module heading/headingui
 */









/**
 * The headings UI feature. It introduces the `headings` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class HeadingUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HeadingUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;
		const options = getLocalizedOptions( editor );
		const defaultTitle = t( 'Choose heading' );
		const dropdownTooltip = t( 'Heading' );

		// Register UI component.
		editor.ui.componentFactory.add( 'heading', locale => {
			const titles = {};
			const itemDefinitions = new Collection();

			const headingCommand = editor.commands.get( 'heading' );
			const paragraphCommand = editor.commands.get( 'paragraph' );

			const commands = [ headingCommand ];

			for ( const option of options ) {
				const def = {
					type: 'button',
					model: new model_Model( {
						label: option.title,
						class: option.class,
						withText: true
					} )
				};

				if ( option.model === 'paragraph' ) {
					def.model.bind( 'isOn' ).to( paragraphCommand, 'value' );
					def.model.set( 'commandName', 'paragraph' );
					commands.push( paragraphCommand );
				} else {
					def.model.bind( 'isOn' ).to( headingCommand, 'value', value => value === option.model );
					def.model.set( {
						commandName: 'heading',
						commandValue: option.model
					} );
				}

				// Add the option to the collection.
				itemDefinitions.add( def );

				titles[ option.model ] = option.title;
			}

			const dropdownView = createDropdown( locale );
			addListToDropdown( dropdownView, itemDefinitions );

			dropdownView.buttonView.set( {
				isOn: false,
				withText: true,
				tooltip: dropdownTooltip
			} );

			dropdownView.extendTemplate( {
				attributes: {
					class: [
						'ck-heading-dropdown'
					]
				}
			} );

			dropdownView.bind( 'isEnabled' ).toMany( commands, 'isEnabled', ( ...areEnabled ) => {
				return areEnabled.some( isEnabled => isEnabled );
			} );

			dropdownView.buttonView.bind( 'label' ).to( headingCommand, 'value', paragraphCommand, 'value', ( value, para ) => {
				const whichModel = value || para && 'paragraph';
				// If none of the commands is active, display default title.
				return titles[ whichModel ] ? titles[ whichModel ] : defaultTitle;
			} );

			// Execute command when an item from the dropdown is selected.
			this.listenTo( dropdownView, 'execute', evt => {
				editor.execute( evt.source.commandName, evt.source.commandValue ? { value: evt.source.commandValue } : undefined );
				editor.editing.view.focus();
			} );

			return dropdownView;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-heading/src/heading.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module heading/heading
 */








/**
 * The headings feature.
 *
 * For a detailed overview, check the {@glink features/headings Headings feature documentation}
 * and the {@glink api/heading package page}.
 *
 * This is a "glue" plugin which loads the {@link module:heading/headingediting~HeadingEditing heading editing feature}
 * and {@link module:heading/headingui~HeadingUI heading UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class Heading extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ HeadingEditing, HeadingUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Heading';
	}
}

/**
 * The configuration of the heading feature. Introduced by the {@link module:heading/headingediting~HeadingEditing} feature.
 *
 * Read more in {@link module:heading/heading~HeadingConfig}.
 *
 * @member {module:heading/heading~HeadingConfig} module:core/editor/editorconfig~EditorConfig#heading
 */

/**
 * The configuration of the heading feature.
 * The option is used by the {@link module:heading/headingediting~HeadingEditing} feature.
 *
 *		ClassicEditor
 *			.create( {
 * 				heading: ... // Heading feature config.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface HeadingConfig
 */

/**
 * The available heading options.
 *
 * The default value is:
 *
 *		const headingConfig = {
 *			options: [
 *				{ model: 'paragraph', title: 'Paragraph', class: 'ck-heading_paragraph' },
 *				{ model: 'heading1', view: 'h2', title: 'Heading 1', class: 'ck-heading_heading1' },
 *				{ model: 'heading2', view: 'h3', title: 'Heading 2', class: 'ck-heading_heading2' },
 *				{ model: 'heading3', view: 'h4', title: 'Heading 3', class: 'ck-heading_heading3' }
 *			]
 *		};
 *
 * It defines 3 levels of headings. In the editor model they will use `heading1`, `heading2`, and `heading3` elements.
 * Their respective view elements (so the elements output by the editor) will be: `h2`, `h3`, and `h4`. This means that
 * if you choose "Heading 1" in the headings dropdown the editor will turn the current block to `<heading1>` in the model
 * which will result in rendering (and outputting to data) the `<h2>` element.
 *
 * The `title` and `class` properties will be used by the `headings` dropdown to render available options.
 * Usually, the first option in the headings dropdown is the "Paragraph" option, hence it's also defined on the list.
 * However, you don't need to define its view representation because it's handled by
 * the {@link module:paragraph/paragraph~Paragraph} feature (which is required by
 * the {@link module:heading/headingediting~HeadingEditing} feature).
 *
 * You can **read more** about configuring heading levels and **see more examples** in
 * the {@glink features/headings Headings} guide.
 *
 * Note: In the model you should always start from `heading1`, regardless of how the headings are represented in the view.
 * That's assumption is used by features like {@link module:autoformat/autoformat~Autoformat} to know which element
 * they should use when applying the first level heading.
 *
 * The defined headings are also available as values passed to the `'heading'` command under their model names.
 * For example, the below code will apply `<heading1>` to the current selection:
 *
 *		editor.execute( 'heading', { value: 'heading1' } );
 *
 * @member {Array.<module:heading/heading~HeadingOption>} module:heading/heading~HeadingConfig#options
 */

/**
 * Heading option descriptor.
 *
 * @typedef {Object} module:heading/heading~HeadingOption
 * @property {String} model Name of the model element to convert.
 * @property {module:engine/view/elementdefinition~ElementDefinition} view Definition of a view element to convert from/to.
 * @property {String} title The user-readable title of the option.
 * @property {String} class The class which will be added to the dropdown item representing this option.
 * @property {String} [icon] Icon used by {@link module:heading/headingbuttonsui~HeadingButtonsUI}. It can be omitted when using
 * the default configuration.
 * @extends module:engine/conversion/conversion~ConverterDefinition
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-highlight/src/highlightcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module highlight/highlightcommand
 */



/**
 * The highlight command. It is used by the {@link module:highlight/highlightediting~HighlightEditing highlight feature}
 * to apply the text highlighting.
 *
 *		editor.execute( 'highlight', { value: 'greenMarker' } );
 *
 * **Note**: Executing the command without a value removes the attribute from the model. If the selection is collapsed
 * inside a text with the highlight attribute, the command will remove the attribute from the entire range
 * of that text.
 *
 * @extends module:core/command~Command
 */
class HighlightCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const doc = model.document;

		/**
		 * A value indicating whether the command is active. If the selection has some highlight attribute,
		 * it corresponds to the value of that attribute.
		 *
		 * @observable
		 * @readonly
		 * @member {undefined|String} module:highlight/highlightcommand~HighlightCommand#value
		 */
		this.value = doc.selection.getAttribute( 'highlight' );
		this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'highlight' );
	}

	/**
	 * Executes the command.
	 *
	 * @param {Object} [options] Options for the executed command.
	 * @param {String} [options.value] The value to apply.
	 *
	 * @fires execute
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;
		const selection = document.selection;

		const highlighter = options.value;

		model.change( writer => {
			if ( selection.isCollapsed ) {
				const position = selection.getFirstPosition();

				// When selection is inside text with `highlight` attribute.
				if ( selection.hasAttribute( 'highlight' ) ) {
					// Find the full highlighted range.
					const isSameHighlight = value => {
						return value.item.hasAttribute( 'highlight' ) && value.item.getAttribute( 'highlight' ) === this.value;
					};

					const highlightStart = position.getLastMatchingPosition( isSameHighlight, { direction: 'backward' } );
					const highlightEnd = position.getLastMatchingPosition( isSameHighlight );

					const highlightRange = writer.createRange( highlightStart, highlightEnd );

					// Then depending on current value...
					if ( !highlighter || this.value === highlighter ) {
						// ...remove attribute when passing highlighter different then current or executing "eraser".

						// If we're at the end of the highlighted range, we don't want to remove highlight of the range.
						if ( !position.isEqual( highlightEnd ) ) {
							writer.removeAttribute( 'highlight', highlightRange );
						}

						writer.removeSelectionAttribute( 'highlight' );
					} else {
						// ...update `highlight` value.

						// If we're at the end of the highlighted range, we don't want to change the highlight of the range.
						if ( !position.isEqual( highlightEnd ) ) {
							writer.setAttribute( 'highlight', highlighter, highlightRange );
						}

						writer.setSelectionAttribute( 'highlight', highlighter );
					}
				} else if ( highlighter ) {
					writer.setSelectionAttribute( 'highlight', highlighter );
				}
			} else {
				const ranges = model.schema.getValidRanges( selection.getRanges(), 'highlight' );

				for ( const range of ranges ) {
					if ( highlighter ) {
						writer.setAttribute( 'highlight', highlighter, range );
					} else {
						writer.removeAttribute( 'highlight', range );
					}
				}
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-highlight/src/highlightediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module highlight/highlightediting
 */





/**
 * The highlight editing feature. It introduces the {@link module:highlight/highlightcommand~HighlightCommand command} and the `highlight`
 * attribute in the {@link module:engine/model/model~Model model} which renders in the {@link module:engine/view/view view}
 * as a `<mark>` element with a `class` attribute (`<mark class="marker-green">...</mark>`) depending
 * on the {@link module:highlight/highlight~HighlightConfig configuration}.
 *
 * @extends module:core/plugin~Plugin
 */
class HighlightEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HighlightEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'highlight', {
			options: [
				{
					model: 'yellowMarker',
					class: 'marker-yellow',
					title: 'Yellow marker',
					color: 'var(--ck-highlight-marker-yellow)',
					type: 'marker'
				},
				{
					model: 'greenMarker',
					class: 'marker-green',
					title: 'Green marker',
					color: 'var(--ck-highlight-marker-green)',
					type: 'marker'
				},
				{
					model: 'pinkMarker',
					class: 'marker-pink',
					title: 'Pink marker',
					color: 'var(--ck-highlight-marker-pink)',
					type: 'marker'
				},
				{
					model: 'blueMarker',
					class: 'marker-blue',
					title: 'Blue marker',
					color: 'var(--ck-highlight-marker-blue)',
					type: 'marker'
				},
				{
					model: 'redPen',
					class: 'pen-red',
					title: 'Red pen',
					color: 'var(--ck-highlight-pen-red)',
					type: 'pen'
				},
				{
					model: 'greenPen',
					class: 'pen-green',
					title: 'Green pen',
					color: 'var(--ck-highlight-pen-green)',
					type: 'pen'
				}
			]
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow highlight attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: 'highlight' } );

		const options = editor.config.get( 'highlight.options' );

		// Set-up the two-way conversion.
		editor.conversion.attributeToElement( _buildDefinition( options ) );

		editor.commands.add( 'highlight', new HighlightCommand( editor ) );
	}
}

// Converts the options array to a converter definition.
//
// @param {Array.<module:highlight/highlight~HighlightOption>} options An array with configured options.
// @returns {module:engine/conversion/conversion~ConverterDefinition}
function _buildDefinition( options ) {
	const definition = {
		model: {
			key: 'highlight',
			values: []
		},
		view: {}
	};

	for ( const option of options ) {
		definition.model.values.push( option.model );
		definition.view[ option.model ] = {
			name: 'mark',
			classes: option.class
		};
	}

	return definition;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-highlight/theme/icons/marker.svg
/* harmony default export */ const marker = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path class=\"ck-icon__fill\" d=\"M10.798 1.59 3.002 12.875l1.895 1.852 2.521 1.402 6.997-12.194z\"/><path d=\"m2.556 16.727.234-.348c-.297-.151-.462-.293-.498-.426-.036-.137.002-.416.115-.837.094-.25.15-.449.169-.595a4.495 4.495 0 0 0 0-.725c-.209-.621-.303-1.041-.284-1.26.02-.218.178-.506.475-.862l6.77-9.414c.539-.91 1.605-.85 3.199.18 1.594 1.032 2.188 1.928 1.784 2.686l-5.877 10.36c-.158.412-.333.673-.526.782-.193.108-.604.179-1.232.21-.362.131-.608.237-.738.318-.13.081-.305.238-.526.47-.293.265-.504.397-.632.397-.096 0-.27-.075-.524-.226l-.31.41-1.6-1.12zm-.279.415 1.575 1.103-.392.515H1.19l1.087-1.618zm8.1-13.656-4.953 6.9L8.75 12.57l4.247-7.574c.175-.25-.188-.647-1.092-1.192-.903-.546-1.412-.652-1.528-.32zM8.244 18.5 9.59 17h9.406v1.5H8.245z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-highlight/theme/icons/pen.svg
/* harmony default export */ const pen = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path class=\"ck-icon__fill\" d=\"M10.126 2.268 2.002 13.874l1.895 1.852 2.521 1.402L14.47 5.481l-1.543-2.568-2.801-.645z\"/><path d=\"m4.5 18.088-2.645-1.852-.04-2.95-.006-.005.006-.008v-.025l.011.008L8.73 2.97c.165-.233.356-.417.567-.557l-1.212.308L4.604 7.9l-.83-.558 3.694-5.495 2.708-.69 1.65 1.145.046.018.85-1.216 2.16 1.512-.856 1.222c.828.967 1.144 2.141.432 3.158L7.55 17.286l.006.005-3.055.797H4.5zm-.634.166-1.976.516-.026-1.918 2.002 1.402zM9.968 3.817l-.006-.004-6.123 9.184 3.277 2.294 6.108-9.162.005.003c.317-.452-.16-1.332-1.064-1.966-.891-.624-1.865-.776-2.197-.349zM8.245 18.5 9.59 17h9.406v1.5H8.245z\"/></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-highlight/theme/highlight.css
var highlight = __webpack_require__(713);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-highlight/theme/highlight.css

            

var highlight_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

highlight_options.insert = "head";
highlight_options.singleton = true;

var highlight_update = injectStylesIntoStyleTag_default()(highlight/* default */.Z, highlight_options);



/* harmony default export */ const theme_highlight = (highlight/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-highlight/src/highlightui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module highlight/highlightui
 */









/**
 * The default highlight UI plugin. It introduces:
 *
 * * The `'highlight'` dropdown,
 * * The `'removeHighlight'` and `'highlight:*'` buttons.
 *
 * The default configuration includes the following buttons:
 *
 * * `'highlight:yellowMarker'`
 * * `'highlight:greenMarker'`
 * * `'highlight:pinkMarker'`
 * * `'highlight:blueMarker'`
 * * `'highlight:redPen'`
 * * `'highlight:greenPen'`
 *
 * See the {@link module:highlight/highlight~HighlightConfig#options configuration} to learn more
 * about the defaults.
 *
 * @extends module:core/plugin~Plugin
 */
class HighlightUI extends plugin_Plugin {
	/**
	 * Returns the localized option titles provided by the plugin.
	 *
	 * The following localized titles corresponding with default
	 * {@link module:highlight/highlight~HighlightConfig#options} are available:
	 *
	 * * `'Yellow marker'`,
	 * * `'Green marker'`,
	 * * `'Pink marker'`,
	 * * `'Blue marker'`,
	 * * `'Red pen'`,
	 * * `'Green pen'`.
	 *
	 * @readonly
	 * @type {Object.<String,String>}
	 */
	get localizedOptionTitles() {
		const t = this.editor.t;

		return {
			'Yellow marker': t( 'Yellow marker' ),
			'Green marker': t( 'Green marker' ),
			'Pink marker': t( 'Pink marker' ),
			'Blue marker': t( 'Blue marker' ),
			'Red pen': t( 'Red pen' ),
			'Green pen': t( 'Green pen' )
		};
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HighlightUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const options = this.editor.config.get( 'highlight.options' );

		for ( const option of options ) {
			this._addHighlighterButton( option );
		}

		this._addRemoveHighlightButton();

		this._addDropdown( options );
	}

	/**
	 * Creates the "Remove highlight" button.
	 *
	 * @private
	 */
	_addRemoveHighlightButton() {
		const t = this.editor.t;
		const command = this.editor.commands.get( 'highlight' );

		this._addButton( 'removeHighlight', t( 'Remove highlight' ), icons.eraser, null, button => {
			button.bind( 'isEnabled' ).to( command, 'isEnabled' );
		} );
	}

	/**
	 * Creates a toolbar button from the provided highlight option.
	 *
	 * @param {module:highlight/highlight~HighlightOption} option
	 * @private
	 */
	_addHighlighterButton( option ) {
		const command = this.editor.commands.get( 'highlight' );

		// TODO: change naming
		this._addButton( 'highlight:' + option.model, option.title, getIconForType( option.type ), option.model, decorateHighlightButton );

		function decorateHighlightButton( button ) {
			button.bind( 'isEnabled' ).to( command, 'isEnabled' );
			button.bind( 'isOn' ).to( command, 'value', value => value === option.model );
			button.iconView.fillColor = option.color;
			button.isToggleable = true;
		}
	}

	/**
	 * Internal method for creating highlight buttons.
	 *
	 * @param {String} name The name of the button.
	 * @param {String} label The label for the button.
	 * @param {String} icon The button icon.
	 * @param {*} value The `value` property passed to the executed command.
	 * @param {Function} decorateButton A callback getting ButtonView instance so that it can be further customized.
	 * @private
	 */
	_addButton( name, label, icon, value, decorateButton ) {
		const editor = this.editor;

		editor.ui.componentFactory.add( name, locale => {
			const buttonView = new buttonview_ButtonView( locale );

			const localized = this.localizedOptionTitles[ label ] ? this.localizedOptionTitles[ label ] : label;

			buttonView.set( {
				label: localized,
				icon,
				tooltip: true
			} );

			buttonView.on( 'execute', () => {
				editor.execute( 'highlight', { value } );
				editor.editing.view.focus();
			} );

			// Add additional behavior for buttonView.
			decorateButton( buttonView );

			return buttonView;
		} );
	}

	/**
	 * Creates the split button dropdown UI from the provided highlight options.
	 *
	 * @param {Array.<module:highlight/highlight~HighlightOption>} options
	 * @private
	 */
	_addDropdown( options ) {
		const editor = this.editor;
		const t = editor.t;
		const componentFactory = editor.ui.componentFactory;

		const startingHighlighter = options[ 0 ];

		const optionsMap = options.reduce( ( retVal, option ) => {
			retVal[ option.model ] = option;

			return retVal;
		}, {} );

		componentFactory.add( 'highlight', locale => {
			const command = editor.commands.get( 'highlight' );
			const dropdownView = createDropdown( locale, SplitButtonView );
			const splitButtonView = dropdownView.buttonView;

			splitButtonView.set( {
				label: t( 'Highlight' ),
				tooltip: true,
				// Holds last executed highlighter.
				lastExecuted: startingHighlighter.model,
				// Holds current highlighter to execute (might be different then last used).
				commandValue: startingHighlighter.model,
				isToggleable: true
			} );

			// Dropdown button changes to selection (command.value):
			// - If selection is in highlight it get active highlight appearance (icon, color) and is activated.
			// - Otherwise it gets appearance (icon, color) of last executed highlight.
			splitButtonView.bind( 'icon' ).to( command, 'value', value => getIconForType( getActiveOption( value, 'type' ) ) );
			splitButtonView.bind( 'color' ).to( command, 'value', value => getActiveOption( value, 'color' ) );
			splitButtonView.bind( 'commandValue' ).to( command, 'value', value => getActiveOption( value, 'model' ) );
			splitButtonView.bind( 'isOn' ).to( command, 'value', value => !!value );

			splitButtonView.delegate( 'execute' ).to( dropdownView );

			// Create buttons array.
			const buttons = options.map( option => {
				// Get existing highlighter button.
				const buttonView = componentFactory.create( 'highlight:' + option.model );

				// Update lastExecutedHighlight on execute.
				this.listenTo( buttonView, 'execute', () => {
					dropdownView.buttonView.set( { lastExecuted: option.model } );
				} );

				return buttonView;
			} );

			// Make toolbar button enabled when any button in dropdown is enabled before adding separator and eraser.
			dropdownView.bind( 'isEnabled' ).toMany( buttons, 'isEnabled', ( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled ) );

			// Add separator and eraser buttons to dropdown.
			buttons.push( new ToolbarSeparatorView() );
			buttons.push( componentFactory.create( 'removeHighlight' ) );

			addToolbarToDropdown( dropdownView, buttons, { enableActiveItemFocusOnDropdownOpen: true } );
			bindToolbarIconStyleToActiveColor( dropdownView );

			dropdownView.toolbarView.ariaLabel = t( 'Text highlight toolbar' );

			// Execute current action from dropdown's split button action button.
			splitButtonView.on( 'execute', () => {
				editor.execute( 'highlight', { value: splitButtonView.commandValue } );
			} );

			// Focus the editable after executing the command.
			// It overrides a default behaviour where the focus is moved to the dropdown button (#12125).
			this.listenTo( dropdownView, 'execute', () => {
				editor.editing.view.focus();
			} );

			// Returns active highlighter option depending on current command value.
			// If current is not set or it is the same as last execute this method will return the option key (like icon or color)
			// of last executed highlighter. Otherwise it will return option key for current one.
			function getActiveOption( current, key ) {
				const whichHighlighter = !current ||
				current === splitButtonView.lastExecuted ? splitButtonView.lastExecuted : current;

				return optionsMap[ whichHighlighter ][ key ];
			}

			return dropdownView;
		} );
	}
}

// Extends split button icon style to reflect last used button style.
function bindToolbarIconStyleToActiveColor( dropdownView ) {
	const actionView = dropdownView.buttonView.actionView;

	actionView.iconView.bind( 'fillColor' ).to( dropdownView.buttonView, 'color' );
}

// Returns icon for given highlighter type.
function getIconForType( type ) {
	return type === 'marker' ? marker : pen;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-highlight/src/highlight.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module highlight/highlight
 */






/**
 * The highlight plugin.
 *
 * For a detailed overview, check the {@glink features/highlight Highlight feature} documentation.
 *
 * This is a "glue" plugin which loads the {@link module:highlight/highlightediting~HighlightEditing} and
 * {@link module:highlight/highlightui~HighlightUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Highlight extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ HighlightEditing, HighlightUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Highlight';
	}
}

/**
 * The highlight option descriptor. See {@link module:highlight/highlight~HighlightConfig} to learn more.
 *
 *		{
 *			model: 'pinkMarker',
 *			class: 'marker-pink',
 *			title: 'Pink Marker',
 *			color: 'var(--ck-highlight-marker-pink)',
 *			type: 'marker'
 *		}
 *
 * @typedef {Object} module:highlight/highlight~HighlightOption
 * @property {String} title The user-readable title of the option.
 * @property {String} model The unique attribute value in the model.
 * @property {String} color The CSS `var()` used for the highlighter. The color is used in the user interface to represent the highlighter.
 * There is a possibility to use the default color format like rgb, hex or hsl, but you need to care about the color of `<mark>`
 * by adding CSS classes definition.
 * @property {String} class The CSS class used on the `<mark>` element in the view. It should match the `color` setting.
 * @property {'marker'|'pen'} type The type of highlighter:
 *
 * * `'marker'` &ndash; Uses the `color` as the `background-color` style,
 * * `'pen'` &ndash; Uses the `color` as the font `color` style.
 */

/**
 * The configuration of the {@link module:highlight/highlight~Highlight} feature.
 *
 * Read more in {@link module:highlight/highlight~HighlightConfig}.
 *
 * @member {module:highlight/highlight~HighlightConfig} module:core/editor/editorconfig~EditorConfig#highlight
 */

/**
 * The configuration of the {@link module:highlight/highlight~Highlight highlight feature}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				highlight:  ... // Highlight feature configuration.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface HighlightConfig
 */

/**
 * The available highlight options. The default value is:
 *
 *		options: [
 *			{
 *				model: 'yellowMarker',
 *				class: 'marker-yellow',
 *				title: 'Yellow marker',
 *				color: 'var(--ck-highlight-marker-yellow)',
 *				type: 'marker'
 *			},
 *			{
 *				model: 'greenMarker',
 *				class: 'marker-green',
 *				title: 'Green marker',
 *				color: 'var(--ck-highlight-marker-green)',
 *				type: 'marker'
 *			},
 *			{
 *				model: 'pinkMarker',
 *				class: 'marker-pink',
 *				title: 'Pink marker',
 *				color: 'var(--ck-highlight-marker-pink)',
 *				type: 'marker'
 *			},
 *			{
 *				model: 'blueMarker',
 *				class: 'marker-blue',
 *				title: 'Blue marker',
 *				color: 'var(--ck-highlight-marker-blue)',
 *				type: 'marker'
 *			},
 *			{
 *				model: 'redPen',
 *				class: 'pen-red',
 *				title: 'Red pen',
 *				color: 'var(--ck-highlight-pen-red)',
 *				type: 'pen'
 *			},
 *			{
 *				model: 'greenPen',
 *				class: 'pen-green',
 *				title: 'Green pen',
 *				color: 'var(--ck-highlight-pen-green)',
 *				type: 'pen'
 *			}
 *		]
 *
 * There are two types of highlighters available:
 *
 * * `'marker'` &ndash; Rendered as a `<mark>` element, styled with the `background-color`.
 * * `'pen'` &ndash; Rendered as a `<mark>` element, styled with the font `color`.
 *
 * **Note**: The highlight feature provides a stylesheet with the CSS classes and corresponding colors defined
 * as CSS variables.
 *
 *		:root {
 *			--ck-highlight-marker-yellow: #fdfd77;
 *			--ck-highlight-marker-green: #63f963;
 *			--ck-highlight-marker-pink: #fc7999;
 *			--ck-highlight-marker-blue: #72cdfd;
 *			--ck-highlight-pen-red: #e91313;
 *			--ck-highlight-pen-green: #118800;
 *		}
 *
 *		.marker-yellow { ... }
 *		.marker-green { ... }
 *		.marker-pink { ... }
 *		.marker-blue { ... }
 *		.pen-red { ... }
 *		.pen-green { ... }
 *
 * It is possible to define the `color` property directly as `rgba(R, G, B, A)`,
 * `#RRGGBB[AA]` or `hsla(H, S, L, A)`. In such situation, the color will **only** apply to the UI of
 * the editor and the `<mark>` elements in the content must be styled by custom classes provided by
 * a dedicated stylesheet.
 *
 * **Note**: It is recommended for the `color` property to correspond to the class in the content
 * stylesheet because it represents the highlighter in the user interface of the editor.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				highlight: {
 *					options: [
 *						{
 *							model: 'pinkMarker',
 *							class: 'marker-pink',
 *							title: 'Pink Marker',
 *							color: 'var(--ck-highlight-marker-pink)',
 *							type: 'marker'
 *						},
 *						{
 *							model: 'redPen',
 *							class: 'pen-red',
 *							title: 'Red Pen',
 *							color: 'var(--ck-highlight-pen-red)',
 *							type: 'pen'
 *						},
 *					]
 *				}
 *		} )
 *		.then( ... )
 *		.catch( ... );
 *
 * @member {Array.<module:highlight/highlight~HighlightOption>} module:highlight/highlight~HighlightConfig#options
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-horizontal-line/src/horizontallinecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module horizontal-line/horizontallinecommand
 */




/**
 * The horizontal line command.
 *
 * The command is registered by {@link module:horizontal-line/horizontallineediting~HorizontalLineEditing} as `'horizontalLine'`.
 *
 * To insert a horizontal line at the current selection, execute the command:
 *
 *		editor.execute( 'horizontalLine' );
 *
 * @extends module:core/command~Command
 */
class HorizontalLineCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const schema = model.schema;
		const selection = model.document.selection;

		this.isEnabled = isHorizontalLineAllowedInParent( selection, schema, model );
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 */
	execute() {
		const model = this.editor.model;

		model.change( writer => {
			const horizontalElement = writer.createElement( 'horizontalLine' );

			model.insertObject( horizontalElement, null, null, { setSelection: 'after' } );
		} );
	}
}

// Checks if a horizontal line is allowed by the schema in the optimal insertion parent.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/model/model~Model} model Model instance.
// @returns {Boolean}
function isHorizontalLineAllowedInParent( selection, schema, model ) {
	const parent = getInsertHorizontalLineParent( selection, model );

	return schema.checkChild( parent, 'horizontalLine' );
}

// Returns a node that will be used to insert a horizontal line with `model.insertContent` to check if the horizontal line can be
// placed there.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/model~Model} model Model instance.
// @returns {module:engine/model/element~Element}
function getInsertHorizontalLineParent( selection, model ) {
	const insertionRange = utils_findOptimalInsertionRange( selection, model );
	const parent = insertionRange.start.parent;

	if ( parent.isEmpty && !parent.is( 'element', '$root' ) ) {
		return parent.parent;
	}

	return parent;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css
var horizontalline = __webpack_require__(2536);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-horizontal-line/theme/horizontalline.css

            

var horizontalline_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

horizontalline_options.insert = "head";
horizontalline_options.singleton = true;

var horizontalline_update = injectStylesIntoStyleTag_default()(horizontalline/* default */.Z, horizontalline_options);



/* harmony default export */ const theme_horizontalline = (horizontalline/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-horizontal-line/src/horizontallineediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module horizontal-line/horizontallineediting
 */








/**
 * The horizontal line editing feature.
 *
 * @extends module:core/plugin~Plugin
 */
class HorizontalLineEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HorizontalLineEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;
		const t = editor.t;
		const conversion = editor.conversion;

		schema.register( 'horizontalLine', {
			inheritAllFrom: '$blockObject'
		} );

		conversion.for( 'dataDowncast' ).elementToElement( {
			model: 'horizontalLine',
			view: ( modelElement, { writer } ) => {
				return writer.createEmptyElement( 'hr' );
			}
		} );

		conversion.for( 'editingDowncast' ).elementToStructure( {
			model: 'horizontalLine',
			view: ( modelElement, { writer } ) => {
				const label = t( 'Horizontal line' );

				const viewWrapper = writer.createContainerElement( 'div', null,
					writer.createEmptyElement( 'hr' )
				);

				writer.addClass( 'ck-horizontal-line', viewWrapper );
				writer.setCustomProperty( 'hr', true, viewWrapper );

				return toHorizontalLineWidget( viewWrapper, writer, label );
			}
		} );

		conversion.for( 'upcast' ).elementToElement( { view: 'hr', model: 'horizontalLine' } );

		editor.commands.add( 'horizontalLine', new HorizontalLineCommand( editor ) );
	}
}

// Converts a given {@link module:engine/view/element~Element} to a horizontal line widget:
// * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to
//   recognize the horizontal line widget element.
// * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
//
//  @param {module:engine/view/element~Element} viewElement
//  @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
//  @param {String} label The element's label.
//  @returns {module:engine/view/element~Element}
function toHorizontalLineWidget( viewElement, writer, label ) {
	writer.setCustomProperty( 'horizontalLine', true, viewElement );

	return toWidget( viewElement, writer, { label } );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-horizontal-line/theme/icons/horizontalline.svg
/* harmony default export */ const icons_horizontalline = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 9h16v2H2z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-horizontal-line/src/horizontallineui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module horizontal-line/horizontallineui
 */






/**
 * The horizontal line UI plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class HorizontalLineUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HorizontalLineUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add the `horizontalLine` button to feature components.
		editor.ui.componentFactory.add( 'horizontalLine', locale => {
			const command = editor.commands.get( 'horizontalLine' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Horizontal line' ),
				icon: icons_horizontalline,
				tooltip: true
			} );

			view.bind( 'isEnabled' ).to( command, 'isEnabled' );

			// Execute the command.
			this.listenTo( view, 'execute', () => {
				editor.execute( 'horizontalLine' );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-horizontal-line/src/horizontalline.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module horizontal-line/horizontalline
 */






/**
 * The horizontal line feature.
 *
 * It provides the possibility to insert a horizontal line into the rich-text editor.
 *
 * For a detailed overview, check the {@glink features/horizontal-line Horizontal line feature} documentation.
 *
 * @extends module:core/plugin~Plugin
 */
class HorizontalLine extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ HorizontalLineEditing, HorizontalLineUI, Widget ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HorizontalLine';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-support/src/htmlcomment.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-support/htmlcomment
 */




/**
 * The HTML comment feature. It preserves the HTML comments (`<!-- -->`) in the editor data.
 *
 * For a detailed overview, check the {@glink features/general-html-support#html-comments HTML comment feature documentation}.
 *
 * @extends module:core/plugin~Plugin
 */
class HtmlComment extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HtmlComment';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow storing comment's content as the $root attribute with the name `$comment:<unique id>`.
		editor.model.schema.addAttributeCheck( ( context, attributeName ) => {
			if ( context.endsWith( '$root' ) && attributeName.startsWith( '$comment' ) ) {
				return true;
			}
		} );

		// Convert the `$comment` view element to `$comment:<unique id>` marker and store its content (the comment itself) as a $root
		// attribute. The comment content is needed in the `dataDowncast` pipeline to re-create the comment node.
		editor.conversion.for( 'upcast' ).elementToMarker( {
			view: '$comment',
			model: ( viewElement, { writer } ) => {
				const root = this.editor.model.document.getRoot();
				const commentContent = viewElement.getCustomProperty( '$rawContent' );
				const markerName = `$comment:${ uid() }`;

				writer.setAttribute( markerName, commentContent, root );

				return markerName;
			}
		} );

		// Convert the `$comment` marker to `$comment` UI element with `$rawContent` custom property containing the comment content.
		editor.conversion.for( 'dataDowncast' ).markerToElement( {
			model: '$comment',
			view: ( modelElement, { writer } ) => {
				const root = this.editor.model.document.getRoot();
				const markerName = modelElement.markerName;
				const commentContent = root.getAttribute( markerName );
				const comment = writer.createUIElement( '$comment' );

				writer.setCustomProperty( '$rawContent', commentContent, comment );

				return comment;
			}
		} );

		// Remove comments' markers and their corresponding $root attributes, which are no longer present.
		editor.model.document.registerPostFixer( writer => {
			const root = editor.model.document.getRoot();

			const changedMarkers = editor.model.document.differ.getChangedMarkers();

			const changedCommentMarkers = changedMarkers.filter( marker => {
				return marker.name.startsWith( '$comment' );
			} );

			const removedCommentMarkers = changedCommentMarkers.filter( marker => {
				const newRange = marker.data.newRange;

				return newRange && newRange.root.rootName === '$graveyard';
			} );

			if ( removedCommentMarkers.length === 0 ) {
				return false;
			}

			for ( const marker of removedCommentMarkers ) {
				writer.removeMarker( marker.name );
				writer.removeAttribute( marker.name, root );
			}

			return true;
		} );

		// Delete all comment markers from the document before setting new data.
		editor.data.on( 'set', () => {
			for ( const commentMarker of editor.model.markers.getMarkersGroup( '$comment' ) ) {
				this.removeHtmlComment( commentMarker.name );
			}
		}, { priority: 'high' } );

		// Delete all comment markers that are within a removed range.
		// Delete all comment markers at the limit element boundaries if the whole content of the limit element is removed.
		editor.model.on( 'deleteContent', ( evt, [ selection ] ) => {
			for ( const range of selection.getRanges() ) {
				const limitElement = editor.model.schema.getLimitElement( range );
				const firstPosition = editor.model.createPositionAt( limitElement, 0 );
				const lastPosition = editor.model.createPositionAt( limitElement, 'end' );

				let affectedCommentIDs;

				if ( firstPosition.isTouching( range.start ) && lastPosition.isTouching( range.end ) ) {
					affectedCommentIDs = this.getHtmlCommentsInRange( editor.model.createRange( firstPosition, lastPosition ) );
				} else {
					affectedCommentIDs = this.getHtmlCommentsInRange( range, { skipBoundaries: true } );
				}

				for ( const commentMarkerID of affectedCommentIDs ) {
					this.removeHtmlComment( commentMarkerID );
				}
			}
		}, { priority: 'high' } );
	}

	/**
	 * Creates an HTML comment on the specified position and returns its ID.
	 *
	 * *Note*: If two comments are created at the same position, the second comment will be inserted before the first one.
	 *
	 * @param {module:engine/model/position~Position} position
	 * @param {String} content
	 * @returns {String} Comment ID. This ID can be later used to e.g. remove the comment from the content.
	 */
	createHtmlComment( position, content ) {
		const id = uid();
		const editor = this.editor;
		const model = editor.model;
		const root = model.document.getRoot();
		const markerName = `$comment:${ id }`;

		return model.change( writer => {
			const range = writer.createRange( position );

			writer.addMarker( markerName, {
				usingOperation: true,
				affectsData: true,
				range
			} );

			writer.setAttribute( markerName, content, root );

			return markerName;
		} );
	}

	/**
	 * Removes an HTML comment with the given comment ID.
	 *
	 * It does nothing and returns `false` if the comment with the given ID does not exist.
	 * Otherwise it removes the comment and returns `true`.
	 *
	 * Note that a comment can be removed also by removing the content around the comment.
	 *
	 * @param {String} commentID The ID of the comment to be removed.
	 * @returns {Boolean} `true` when the comment with the given ID was removed, `false` otherwise.
	 */
	removeHtmlComment( commentID ) {
		const editor = this.editor;
		const root = editor.model.document.getRoot();

		const marker = editor.model.markers.get( commentID );

		if ( !marker ) {
			return false;
		}

		editor.model.change( writer => {
			writer.removeMarker( marker );
			writer.removeAttribute( commentID, root );
		} );

		return true;
	}

	/**
	 * Gets the HTML comment data for the comment with a given ID.
	 *
	 * Returns `null` if the comment does not exist.
	 *
	 * @param {String} commentID
	 * @returns {module:html-support/htmlcomment~HtmlCommentData}
	 */
	getHtmlCommentData( commentID ) {
		const editor = this.editor;
		const marker = editor.model.markers.get( commentID );
		const root = editor.model.document.getRoot();

		if ( !marker ) {
			return null;
		}

		return {
			content: root.getAttribute( commentID ),
			position: marker.getStart()
		};
	}

	/**
	 * Gets all HTML comments in the given range.
	 *
	 * By default it includes comments at the range boundaries.
	 *
	 * @param {module:engine/model/range~Range} range
	 * @param {Object} [options]
	 * @param {Boolean} [options.skipBoundaries=false] When set to `true` the range boundaries will be skipped.
	 * @returns {Array.<String>} HTML comment IDs
	 */
	getHtmlCommentsInRange( range, { skipBoundaries = false } = {} ) {
		const includeBoundaries = !skipBoundaries;

		// Unfortunately, MarkerCollection#getMarkersAtPosition() filters out collapsed markers.
		return Array.from( this.editor.model.markers.getMarkersGroup( '$comment' ) )
			.filter( marker => isCommentMarkerInRange( marker, range ) )
			.map( marker => marker.name );

		function isCommentMarkerInRange( commentMarker, range ) {
			const position = commentMarker.getRange().start;

			return (
				( position.isAfter( range.start ) || ( includeBoundaries && position.isEqual( range.start ) ) ) &&
				( position.isBefore( range.end ) || ( includeBoundaries && position.isEqual( range.end ) ) )
			);
		}
	}
}

/**
 * An interface for the HTML comments data.
 *
 * It consists of the {@link module:engine/model/position~Position `position`} and `content`.
 *
 * @typedef {Object} module:html-support/htmlcomment~HtmlCommentData
 *
 * @property {module:engine/model/position~Position} position
 * @property {String} content
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-embed/src/htmlembedcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-embed/htmlembedcommand
 */




/**
 * The insert HTML embed element command.
 *
 * The command is registered by {@link module:html-embed/htmlembedediting~HtmlEmbedEditing} as `'htmlEmbed'`.
 *
 * To insert an empty HTML embed element at the current selection, execute the command:
 *
 *		editor.execute( 'htmlEmbed' );
 *
 * You can specify the initial content of a new HTML embed in the argument:
 *
 *		editor.execute( 'htmlEmbed', '<b>Initial content.</b>' );
 *
 * To update the content of the HTML embed, select it in the model and pass the content in the argument:
 *
 *		editor.execute( 'htmlEmbed', '<b>New content of an existing embed.</b>' );
 *
 * @extends module:core/command~Command
 */
class HtmlEmbedCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const schema = model.schema;
		const selection = model.document.selection;
		const selectedRawHtmlElement = getSelectedRawHtmlModelWidget( selection );

		this.isEnabled = isHtmlEmbedAllowedInParent( selection, schema, model );
		this.value = selectedRawHtmlElement ? selectedRawHtmlElement.getAttribute( 'value' ) || '' : null;
	}

	/**
	 * Executes the command, which either:
	 *
	 * * creates and inserts a new HTML embed element if none was selected,
	 * * updates the content of the HTML embed if one was selected.
	 *
	 * @fires execute
	 * @param {String} [value] When passed, the value (content) will be set on a new embed or a selected one.
	 */
	execute( value ) {
		const model = this.editor.model;
		const selection = model.document.selection;

		model.change( writer => {
			let htmlEmbedElement;

			// If the command has a non-null value, there must be some HTML embed selected in the model.
			if ( this.value !== null ) {
				htmlEmbedElement = getSelectedRawHtmlModelWidget( selection );
			} else {
				htmlEmbedElement = writer.createElement( 'rawHtml' );

				model.insertObject( htmlEmbedElement, null, null, { setSelection: 'on' } );
			}

			writer.setAttribute( 'value', value, htmlEmbedElement );
		} );
	}
}

// Checks if an HTML embed is allowed by the schema in the optimal insertion parent.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/model/model~Model} model
// @returns {Boolean}
function isHtmlEmbedAllowedInParent( selection, schema, model ) {
	const parent = getInsertHtmlEmbedParent( selection, model );

	return schema.checkChild( parent, 'rawHtml' );
}

// Returns a node that will be used to insert a html embed with `model.insertContent` to check if a html embed element can be placed there.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/model~Model} model
// @returns {module:engine/model/element~Element}
function getInsertHtmlEmbedParent( selection, model ) {
	const insertionRange = utils_findOptimalInsertionRange( selection, model );
	const parent = insertionRange.start.parent;

	if ( parent.isEmpty && !parent.is( 'element', '$root' ) ) {
		return parent.parent;
	}

	return parent;
}

// Returns the selected HTML embed element in the model, if any.
//
// @param {module:engine/model/selection~Selection} selection
// @returns {module:engine/model/element~Element|null}
function getSelectedRawHtmlModelWidget( selection ) {
	const selectedElement = selection.getSelectedElement();

	if ( selectedElement && selectedElement.is( 'element', 'rawHtml' ) ) {
		return selectedElement;
	}

	return null;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-html-embed/theme/htmlembed.css
var htmlembed = __webpack_require__(3403);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-embed/theme/htmlembed.css

            

var htmlembed_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

htmlembed_options.insert = "head";
htmlembed_options.singleton = true;

var htmlembed_update = injectStylesIntoStyleTag_default()(htmlembed/* default */.Z, htmlembed_options);



/* harmony default export */ const theme_htmlembed = (htmlembed/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-embed/src/htmlembedediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-embed/htmlembedediting
 */










/**
 * The HTML embed editing feature.
 *
 * @extends module:core/plugin~Plugin
 */
class HtmlEmbedEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HtmlEmbedEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'htmlEmbed', {
			showPreviews: false,
			sanitizeHtml: rawHtml => {
				/**
				 * When using the HTML embed feature with the `htmlEmbed.showPreviews=true` option, it is strongly recommended to
				 * define a sanitize function that will clean up the input HTML in order to avoid XSS vulnerability.
				 *
				 * For a detailed overview, check the {@glink features/html-embed HTML embed feature} documentation.
				 *
				 * @error html-embed-provide-sanitize-function
				 */
				logWarning( 'html-embed-provide-sanitize-function' );

				return {
					html: rawHtml,
					hasChanged: false
				};
			}
		} );

		/**
		 * Keeps references to {@link module:ui/button/buttonview~ButtonView edit, save, and cancel} button instances created for
		 * each widget so they can be destroyed if they are no longer in DOM after the editing view was re-rendered.
		 *
		 * @private
		 * @member {Set.<module:ui/button/buttonview~ButtonView>} #_widgetButtonViewReferences
		 */
		this._widgetButtonViewReferences = new Set();
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;

		schema.register( 'rawHtml', {
			inheritAllFrom: '$blockObject',
			allowAttributes: [ 'value' ]
		} );

		editor.commands.add( 'htmlEmbed', new HtmlEmbedCommand( editor ) );

		this._setupConversion();
	}

	/**
	 * Prepares converters for the feature.
	 *
	 * @private
	 */
	_setupConversion() {
		const editor = this.editor;
		const t = editor.t;
		const view = editor.editing.view;
		const widgetButtonViewReferences = this._widgetButtonViewReferences;

		const htmlEmbedConfig = editor.config.get( 'htmlEmbed' );

		// Destroy UI buttons created for widgets that have been removed from the view document (e.g. in the previous conversion).
		// This prevents unexpected memory leaks from UI views.
		this.editor.editing.view.on( 'render', () => {
			for ( const buttonView of widgetButtonViewReferences ) {
				if ( buttonView.element.isConnected ) {
					return;
				}

				buttonView.destroy();
				widgetButtonViewReferences.delete( buttonView );
			}
		}, { priority: 'lowest' } );

		// Register div.raw-html-embed as a raw content element so all of it's content will be provided
		// as a view element's custom property while data upcasting.
		editor.data.registerRawContentMatcher( {
			name: 'div',
			classes: 'raw-html-embed'
		} );

		editor.conversion.for( 'upcast' ).elementToElement( {
			view: {
				name: 'div',
				classes: 'raw-html-embed'
			},
			model: ( viewElement, { writer } ) => {
				// The div.raw-html-embed is registered as a raw content element,
				// so all it's content is available in a custom property.
				return writer.createElement( 'rawHtml', {
					value: viewElement.getCustomProperty( '$rawContent' )
				} );
			}
		} );

		editor.conversion.for( 'dataDowncast' ).elementToElement( {
			model: 'rawHtml',
			view: ( modelElement, { writer } ) => {
				return writer.createRawElement( 'div', { class: 'raw-html-embed' }, function( domElement ) {
					domElement.innerHTML = modelElement.getAttribute( 'value' ) || '';
				} );
			}
		} );

		editor.conversion.for( 'editingDowncast' ).elementToStructure( {
			model: { name: 'rawHtml', attributes: [ 'value' ] },
			view: ( modelElement, { writer } ) => {
				let domContentWrapper, state, props;

				const viewContentWrapper = writer.createRawElement( 'div', {
					class: 'raw-html-embed__content-wrapper'
				}, function( domElement ) {
					domContentWrapper = domElement;

					renderContent( { domElement, editor, state, props } );

					// Since there is a `data-cke-ignore-events` attribute set on the wrapper element in the editable mode,
					// the explicit `mousedown` handler on the `capture` phase is needed to move the selection onto the whole
					// HTML embed widget.
					domContentWrapper.addEventListener( 'mousedown', () => {
						if ( state.isEditable ) {
							const model = editor.model;
							const selectedElement = model.document.selection.getSelectedElement();

							// Move the selection onto the whole HTML embed widget if it's currently not selected.
							if ( selectedElement !== modelElement ) {
								model.change( writer => writer.setSelection( modelElement, 'on' ) );
							}
						}
					}, true );
				} );

				// API exposed on each raw HTML embed widget so other features can control a particular widget.
				const rawHtmlApi = {
					makeEditable() {
						state = Object.assign( {}, state, {
							isEditable: true
						} );

						renderContent( { domElement: domContentWrapper, editor, state, props } );

						view.change( writer => {
							writer.setAttribute( 'data-cke-ignore-events', 'true', viewContentWrapper );
						} );

						// This could be potentially pulled to a separate method called focusTextarea().
						domContentWrapper.querySelector( 'textarea' ).focus();
					},
					save( newValue ) {
						// If the value didn't change, we just cancel. If it changed,
						// it's enough to update the model – the entire widget will be reconverted.
						if ( newValue !== state.getRawHtmlValue() ) {
							editor.execute( 'htmlEmbed', newValue );
							editor.editing.view.focus();
						} else {
							this.cancel();
						}
					},
					cancel() {
						state = Object.assign( {}, state, {
							isEditable: false
						} );

						renderContent( { domElement: domContentWrapper, editor, state, props } );
						editor.editing.view.focus();

						view.change( writer => {
							writer.removeAttribute( 'data-cke-ignore-events', viewContentWrapper );
						} );
					}
				};

				state = {
					showPreviews: htmlEmbedConfig.showPreviews,
					isEditable: false,
					getRawHtmlValue: () => modelElement.getAttribute( 'value' ) || ''
				};

				props = {
					sanitizeHtml: htmlEmbedConfig.sanitizeHtml,
					textareaPlaceholder: t( 'Paste raw HTML here...' ),

					onEditClick() {
						rawHtmlApi.makeEditable();
					},
					onSaveClick( newValue ) {
						rawHtmlApi.save( newValue );
					},
					onCancelClick() {
						rawHtmlApi.cancel();
					}
				};

				const viewContainer = writer.createContainerElement( 'div', {
					class: 'raw-html-embed',
					'data-html-embed-label': t( 'HTML snippet' ),
					dir: editor.locale.uiLanguageDirection
				}, viewContentWrapper );

				writer.setCustomProperty( 'rawHtmlApi', rawHtmlApi, viewContainer );
				writer.setCustomProperty( 'rawHtml', true, viewContainer );

				return toWidget( viewContainer, writer, {
					widgetLabel: t( 'HTML snippet' ),
					hasSelectionHandle: true
				} );
			}
		} );

		function renderContent( { domElement, editor, state, props } ) {
			// Remove all children;
			domElement.textContent = '';

			const domDocument = domElement.ownerDocument;
			let domTextarea;

			if ( state.isEditable ) {
				const textareaProps = {
					isDisabled: false,
					placeholder: props.textareaPlaceholder
				};

				domTextarea = createDomTextarea( { domDocument, state, props: textareaProps } );

				domElement.append( domTextarea );
			} else if ( state.showPreviews ) {
				const previewContainerProps = {
					sanitizeHtml: props.sanitizeHtml
				};

				domElement.append( createPreviewContainer( { domDocument, state, props: previewContainerProps, editor } ) );
			} else {
				const textareaProps = {
					isDisabled: true,
					placeholder: props.textareaPlaceholder
				};

				domElement.append( createDomTextarea( { domDocument, state, props: textareaProps } ) );
			}

			const buttonsWrapperProps = {
				onEditClick: props.onEditClick,
				onSaveClick: () => {
					props.onSaveClick( domTextarea.value );
				},
				onCancelClick: props.onCancelClick
			};

			domElement.prepend( createDomButtonsWrapper( { editor, domDocument, state, props: buttonsWrapperProps } ) );
		}

		function createDomButtonsWrapper( { editor, domDocument, state, props } ) {
			const domButtonsWrapper = createElement( domDocument, 'div', {
				class: 'raw-html-embed__buttons-wrapper'
			} );

			if ( state.isEditable ) {
				const saveButtonView = createUIButton( editor, 'save', props.onSaveClick );
				const cancelButtonView = createUIButton( editor, 'cancel', props.onCancelClick );

				domButtonsWrapper.append( saveButtonView.element, cancelButtonView.element );
				widgetButtonViewReferences.add( saveButtonView ).add( cancelButtonView );
			} else {
				const editButtonView = createUIButton( editor, 'edit', props.onEditClick );

				domButtonsWrapper.append( editButtonView.element );
				widgetButtonViewReferences.add( editButtonView );
			}

			return domButtonsWrapper;
		}

		function createDomTextarea( { domDocument, state, props } ) {
			const domTextarea = createElement( domDocument, 'textarea', {
				placeholder: props.placeholder,
				class: 'ck ck-reset ck-input ck-input-text raw-html-embed__source'
			} );

			domTextarea.disabled = props.isDisabled;
			domTextarea.value = state.getRawHtmlValue();

			return domTextarea;
		}

		function createPreviewContainer( { domDocument, state, props, editor } ) {
			const sanitizedOutput = props.sanitizeHtml( state.getRawHtmlValue() );
			const placeholderText = state.getRawHtmlValue().length > 0 ?
				t( 'No preview available' ) :
				t( 'Empty snippet content' );

			const domPreviewPlaceholder = createElement( domDocument, 'div', {
				class: 'ck ck-reset_all raw-html-embed__preview-placeholder'
			}, placeholderText );

			const domPreviewContent = createElement( domDocument, 'div', {
				class: 'raw-html-embed__preview-content',
				dir: editor.locale.contentLanguageDirection
			} );

			// Creating a contextual document fragment allows executing scripts when inserting into the preview element.
			// See: #8326.
			const domRange = domDocument.createRange();
			const domDocumentFragment = domRange.createContextualFragment( sanitizedOutput.html );

			domPreviewContent.appendChild( domDocumentFragment );

			const domPreviewContainer = createElement( domDocument, 'div', {
				class: 'raw-html-embed__preview'
			}, [
				domPreviewPlaceholder, domPreviewContent
			] );

			return domPreviewContainer;
		}
	}
}

// Returns a UI button view that can be used in conversion.
//
//  @param {module:utils/locale~Locale} locale Editor locale.
//  @param {'edit'|'save'|'cancel'} type Type of button to create.
//  @param {Function} onClick The callback executed on button click.
//  @returns {module:ui/button/buttonview~ButtonView}
function createUIButton( editor, type, onClick ) {
	const t = editor.locale.t;
	const buttonView = new buttonview_ButtonView( editor.locale );
	const command = editor.commands.get( 'htmlEmbed' );

	buttonView.set( {
		class: `raw-html-embed__${ type }-button`,
		icon: icons.pencil,
		tooltip: true,
		tooltipPosition: editor.locale.uiLanguageDirection === 'rtl' ? 'e' : 'w'
	} );

	buttonView.render();

	if ( type === 'edit' ) {
		buttonView.set( {
			icon: icons.pencil,
			label: t( 'Edit source' )
		} );

		buttonView.bind( 'isEnabled' ).to( command );
	} else if ( type === 'save' ) {
		buttonView.set( {
			icon: icons.check,
			label: t( 'Save changes' )
		} );

		buttonView.bind( 'isEnabled' ).to( command );
	} else {
		buttonView.set( {
			icon: icons.cancel,
			label: t( 'Cancel' )
		} );
	}

	buttonView.on( 'execute', onClick );

	return buttonView;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-embed/theme/icons/html.svg
/* harmony default export */ const html = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M17 0a2 2 0 0 1 2 2v7a1 1 0 0 1 1 1v5a1 1 0 0 1-.883.993l-.118.006L19 17a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2l-.001-1.001-.116-.006A1 1 0 0 1 0 15v-5a1 1 0 0 1 .999-1L1 2a2 2 0 0 1 2-2h14zm.499 15.999h-15L2.5 17a.5.5 0 0 0 .5.5h14a.5.5 0 0 0 .5-.5l-.001-1.001zm-3.478-6.013-.014.014H14v.007l-1.525 1.525-1.46-1.46-.015.013V10h-1v5h1v-3.53l1.428 1.43.048.043.131-.129L14 11.421V15h1v-5h-.965l-.014-.014zM2 10H1v5h1v-2h2v2h1v-5H4v2H2v-2zm7 0H6v1h1v4h1v-4h1v-1zm8 0h-1v5h3v-1h-2v-4zm0-8.5H3a.5.5 0 0 0-.5.5l-.001 6.999h15L17.5 2a.5.5 0 0 0-.5-.5zM10 7v1H4V7h6zm3-2v1H4V5h9zm-3-2v1H4V3h6z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-embed/src/htmlembedui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-embed/htmlembedui
 */






/**
 * The HTML embed UI plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class HtmlEmbedUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HtmlEmbedUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add the `htmlEmbed` button to feature components.
		editor.ui.componentFactory.add( 'htmlEmbed', locale => {
			const command = editor.commands.get( 'htmlEmbed' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Insert HTML' ),
				icon: html,
				tooltip: true
			} );

			view.bind( 'isEnabled' ).to( command, 'isEnabled' );

			// Execute the command.
			this.listenTo( view, 'execute', () => {
				editor.execute( 'htmlEmbed' );
				editor.editing.view.focus();

				const widgetWrapper = editor.editing.view.document.selection.getSelectedElement();

				widgetWrapper.getCustomProperty( 'rawHtmlApi' ).makeEditable();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-html-embed/src/htmlembed.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module html-embed/htmlembed
 */







/**
 * The HTML embed feature.
 *
 * It allows inserting HTML snippets directly into the editor.
 *
 * For a detailed overview, check the {@glink features/html-embed HTML embed feature} documentation.
 *
 * @extends module:core/plugin~Plugin
 */
class HtmlEmbed extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ HtmlEmbedEditing, HtmlEmbedUI, Widget ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'HtmlEmbed';
	}
}

/**
 * The configuration of the HTML embed feature.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				htmlEmbed: ... // HTML embed feature options.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface HtmlEmbedConfig
 */

/**
 * Whether the feature should render previews of the embedded HTML.
 *
 * When set to `true`, the feature will produce a preview of the inserted HTML based on a sanitized
 * version of the HTML provided by the user.
 *
 * The function responsible for sanitizing the HTML needs to be specified in
 * {@link module:html-embed/htmlembed~HtmlEmbedConfig#sanitizeHtml `config.htmlEmbed.sanitizeHtml()`}.
 *
 * Read more about the security aspect of this feature in the {@glink features/html-embed#security "Security"} section of
 * the {@glink features/html-embed HTML embed} feature guide.
 *
 * @member {Boolean} [module:html-embed/htmlembed~HtmlEmbedConfig#showPreviews=false]
 */

/**
 * Callback used to sanitize the HTML provided by the user when generating previews of it in the editor.
 *
 * We strongly recommend overwriting the default function to avoid XSS vulnerabilities.
 *
 * Read more about the security aspect of this feature in the {@glink features/html-embed#security "Security"} section of
 * the {@glink features/html-embed HTML embed} feature guide.
 *
 * The function receives the input HTML (as a string), and should return an object
 * that matches the {@link module:html-embed/htmlembed~HtmlEmbedSanitizeOutput} interface.
 *
 *  	ClassicEditor
 * 			.create( editorElement, {
 * 				htmlEmbed: {
 * 					showPreviews: true,
 * 					sanitizeHtml( inputHtml ) {
 * 						// Strip unsafe elements and attributes, e.g.:
 * 						// the `<script>` elements and `on*` attributes.
 * 						const outputHtml = sanitize( inputHtml );
 *
 * 						return {
 * 							html: outputHtml,
 *							// true or false depending on whether the sanitizer stripped anything.
 * 							hasChanged: ...
 * 						};
 * 					},
 * 				}
 * 			} )
 * 			.then( ... )
 * 			.catch( ... );
 *
 * **Note:** The function is used only when the feature
 * {@link module:html-embed/htmlembed~HtmlEmbedConfig#showPreviews is configured to render previews}.
 *
 * @member {Function} [module:html-embed/htmlembed~HtmlEmbedConfig#sanitizeHtml]
 */

/**
 * An object returned by the {@link module:html-embed/htmlembed~HtmlEmbedConfig#sanitizeHtml} function.
 *
 * @interface HtmlEmbedSanitizeOutput
 */

/**
 * An output (safe) HTML that will be inserted into the {@glink framework/guides/architecture/editing-engine editing view}.
 *
 * @member {String} module:html-embed/htmlembed~HtmlEmbedSanitizeOutput#html
 */

/**
 * A flag that indicates whether the output HTML is different than the input value.
 *
 * @member {Boolean} [module:html-embed/htmlembed~HtmlEmbedSanitizeOutput#hasChanged]
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagetextalternative/imagetextalternativecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagetextalternative/imagetextalternativecommand
 */



/**
 * The image text alternative command. It is used to change the `alt` attribute of `<imageBlock>` and `<imageInline>` model elements.
 *
 * @extends module:core/command~Command
 */
class ImageTextAlternativeCommand extends command_Command {
	/**
	 * The command value: `false` if there is no `alt` attribute, otherwise the value of the `alt` attribute.
	 *
	 * @readonly
	 * @observable
	 * @member {String|Boolean} #value
	 */

	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const element = imageUtils.getClosestSelectedImageElement( this.editor.model.document.selection );

		this.isEnabled = !!element;

		if ( this.isEnabled && element.hasAttribute( 'alt' ) ) {
			this.value = element.getAttribute( 'alt' );
		} else {
			this.value = false;
		}
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} options
	 * @param {String} options.newValue The new value of the `alt` attribute to set.
	 */
	execute( options ) {
		const editor = this.editor;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const model = editor.model;
		const imageElement = imageUtils.getClosestSelectedImageElement( model.document.selection );

		model.change( writer => {
			writer.setAttribute( 'alt', options.newValue, imageElement );
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagetextalternative/imagetextalternativeediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagetextalternative/imagetextalternativeediting
 */





/**
 * The image text alternative editing plugin.
 *
 * Registers the `'imageTextAlternative'` command.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageTextAlternativeEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUtils ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageTextAlternativeEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		this.editor.commands.add( 'imageTextAlternative', new ImageTextAlternativeCommand( this.editor ) );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/textalternativeform.css
var textalternativeform = __webpack_require__(6831);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/textalternativeform.css

            

var textalternativeform_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

textalternativeform_options.insert = "head";
textalternativeform_options.singleton = true;

var textalternativeform_update = injectStylesIntoStyleTag_default()(textalternativeform/* default */.Z, textalternativeform_options);



/* harmony default export */ const theme_textalternativeform = (textalternativeform/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagetextalternative/ui/textalternativeformview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagetextalternative/ui/textalternativeformview
 */







// See: #8833.
// eslint-disable-next-line ckeditor5-rules/ckeditor-imports


/**
 * The TextAlternativeFormView class.
 *
 * @extends module:ui/view~View
 */
class TextAlternativeFormView extends src_view_View {
	/**
	 * @inheritDoc
	 */
	constructor( locale ) {
		super( locale );

		const t = this.locale.t;

		/**
		 * Tracks information about the DOM focus in the form.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * An input with a label.
		 *
		 * @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView} #labeledInput
		 */
		this.labeledInput = this._createLabeledInputView();

		/**
		 * A button used to submit the form.
		 *
		 * @member {module:ui/button/buttonview~ButtonView} #saveButtonView
		 */
		this.saveButtonView = this._createButton( t( 'Save' ), icons.check, 'ck-button-save' );
		this.saveButtonView.type = 'submit';

		/**
		 * A button used to cancel the form.
		 *
		 * @member {module:ui/button/buttonview~ButtonView} #cancelButtonView
		 */
		this.cancelButtonView = this._createButton( t( 'Cancel' ), icons.cancel, 'ck-button-cancel', 'cancel' );

		/**
		 * A collection of views which can be focused in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate form fields backwards using the Shift + Tab keystroke.
				focusPrevious: 'shift + tab',

				// Navigate form fields forwards using the Tab key.
				focusNext: 'tab'
			}
		} );

		this.setTemplate( {
			tag: 'form',

			attributes: {
				class: [
					'ck',
					'ck-text-alternative-form',
					'ck-responsive-form'
				],

				// https://github.com/ckeditor/ckeditor5-image/issues/40
				tabindex: '-1'
			},

			children: [
				this.labeledInput,
				this.saveButtonView,
				this.cancelButtonView
			]
		} );

		injectCssTransitionDisabler( this );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		this.keystrokes.listenTo( this.element );

		submitHandler( { view: this } );

		[ this.labeledInput, this.saveButtonView, this.cancelButtonView ]
			.forEach( v => {
				// Register the view as focusable.
				this._focusables.add( v );

				// Register the view in the focus tracker.
				this.focusTracker.add( v.element );
			} );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Creates the button view.
	 *
	 * @private
	 * @param {String} label The button label
	 * @param {String} icon The button's icon.
	 * @param {String} className The additional button CSS class name.
	 * @param {String} [eventName] The event name that the ButtonView#execute event will be delegated to.
	 * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
	 */
	_createButton( label, icon, className, eventName ) {
		const button = new buttonview_ButtonView( this.locale );

		button.set( {
			label,
			icon,
			tooltip: true
		} );

		button.extendTemplate( {
			attributes: {
				class: className
			}
		} );

		if ( eventName ) {
			button.delegate( 'execute' ).to( this, eventName );
		}

		return button;
	}

	/**
	 * Creates an input with a label.
	 *
	 * @private
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView} Labeled field view instance.
	 */
	_createLabeledInputView() {
		const t = this.locale.t;
		const labeledInput = new LabeledFieldView( this.locale, createLabeledInputText );

		labeledInput.label = t( 'Text alternative' );

		return labeledInput;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/ui/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/ui/utils
 */



/**
 * A helper utility that positions the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} instance
 * with respect to the image in the editor content, if one is selected.
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 */
function utils_repositionContextualBalloon( editor ) {
	const balloon = editor.plugins.get( 'ContextualBalloon' );

	if ( editor.plugins.get( 'ImageUtils' ).getClosestSelectedImageWidget( editor.editing.view.document.selection ) ) {
		const position = utils_getBalloonPositionData( editor );

		balloon.updatePosition( position );
	}
}

/**
 * Returns the positioning options that control the geometry of the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} with respect
 * to the selected element in the editor content.
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @returns {module:utils/dom/position~Options}
 */
function utils_getBalloonPositionData( editor ) {
	const editingView = editor.editing.view;
	const defaultPositions = balloonpanelview_BalloonPanelView.defaultPositions;
	const imageUtils = editor.plugins.get( 'ImageUtils' );

	return {
		target: editingView.domConverter.mapViewToDom( imageUtils.getClosestSelectedImageWidget( editingView.document.selection ) ),
		positions: [
			defaultPositions.northArrowSouth,
			defaultPositions.northArrowSouthWest,
			defaultPositions.northArrowSouthEast,
			defaultPositions.southArrowNorth,
			defaultPositions.southArrowNorthWest,
			defaultPositions.southArrowNorthEast,
			defaultPositions.viewportStickyNorth
		]
	};
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagetextalternative/imagetextalternativeui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagetextalternative/imagetextalternativeui
 */







/**
 * The image text alternative UI plugin.
 *
 * The plugin uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon}.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageTextAlternativeUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ContextualBalloon ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageTextAlternativeUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		this._createButton();
		this._createForm();
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		// Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
		this._form.destroy();
	}

	/**
	 * Creates a button showing the balloon panel for changing the image text alternative and
	 * registers it in the editor {@link module:ui/componentfactory~ComponentFactory ComponentFactory}.
	 *
	 * @private
	 */
	_createButton() {
		const editor = this.editor;
		const t = editor.t;

		editor.ui.componentFactory.add( 'imageTextAlternative', locale => {
			const command = editor.commands.get( 'imageTextAlternative' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Change image text alternative' ),
				icon: icons.lowVision,
				tooltip: true
			} );

			view.bind( 'isEnabled' ).to( command, 'isEnabled' );
			view.bind( 'isOn' ).to( command, 'value', value => !!value );

			this.listenTo( view, 'execute', () => {
				this._showForm();
			} );

			return view;
		} );
	}

	/**
	 * Creates the {@link module:image/imagetextalternative/ui/textalternativeformview~TextAlternativeFormView}
	 * form.
	 *
	 * @private
	 */
	_createForm() {
		const editor = this.editor;
		const view = editor.editing.view;
		const viewDocument = view.document;
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		/**
		 * The contextual balloon plugin instance.
		 *
		 * @private
		 * @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
		 */
		this._balloon = this.editor.plugins.get( 'ContextualBalloon' );

		/**
		 * A form containing a textarea and buttons, used to change the `alt` text value.
		 *
		 * @member {module:image/imagetextalternative/ui/textalternativeformview~TextAlternativeFormView}
		 */
		this._form = new TextAlternativeFormView( editor.locale );

		// Render the form so its #element is available for clickOutsideHandler.
		this._form.render();

		this.listenTo( this._form, 'submit', () => {
			editor.execute( 'imageTextAlternative', {
				newValue: this._form.labeledInput.fieldView.element.value
			} );

			this._hideForm( true );
		} );

		this.listenTo( this._form, 'cancel', () => {
			this._hideForm( true );
		} );

		// Close the form on Esc key press.
		this._form.keystrokes.set( 'Esc', ( data, cancel ) => {
			this._hideForm( true );
			cancel();
		} );

		// Reposition the balloon or hide the form if an image widget is no longer selected.
		this.listenTo( editor.ui, 'update', () => {
			if ( !imageUtils.getClosestSelectedImageWidget( viewDocument.selection ) ) {
				this._hideForm( true );
			} else if ( this._isVisible ) {
				utils_repositionContextualBalloon( editor );
			}
		} );

		// Close on click outside of balloon panel element.
		clickoutsidehandler_clickOutsideHandler( {
			emitter: this._form,
			activator: () => this._isVisible,
			contextElements: [ this._balloon.view.element ],
			callback: () => this._hideForm()
		} );
	}

	/**
	 * Shows the {@link #_form} in the {@link #_balloon}.
	 *
	 * @private
	 */
	_showForm() {
		if ( this._isVisible ) {
			return;
		}

		const editor = this.editor;
		const command = editor.commands.get( 'imageTextAlternative' );
		const labeledInput = this._form.labeledInput;

		this._form.disableCssTransitions();

		if ( !this._isInBalloon ) {
			this._balloon.add( {
				view: this._form,
				position: utils_getBalloonPositionData( editor )
			} );
		}

		// Make sure that each time the panel shows up, the field remains in sync with the value of
		// the command. If the user typed in the input, then canceled the balloon (`labeledInput#value`
		// stays unaltered) and re-opened it without changing the value of the command, they would see the
		// old value instead of the actual value of the command.
		// https://github.com/ckeditor/ckeditor5-image/issues/114
		labeledInput.fieldView.value = labeledInput.fieldView.element.value = command.value || '';

		this._form.labeledInput.fieldView.select();

		this._form.enableCssTransitions();
	}

	/**
	 * Removes the {@link #_form} from the {@link #_balloon}.
	 *
	 * @param {Boolean} [focusEditable=false] Controls whether the editing view is focused afterwards.
	 * @private
	 */
	_hideForm( focusEditable ) {
		if ( !this._isInBalloon ) {
			return;
		}

		// Blur the input element before removing it from DOM to prevent issues in some browsers.
		// See https://github.com/ckeditor/ckeditor5/issues/1501.
		if ( this._form.focusTracker.isFocused ) {
			this._form.saveButtonView.focus();
		}

		this._balloon.remove( this._form );

		if ( focusEditable ) {
			this.editor.editing.view.focus();
		}
	}

	/**
	 * Returns `true` when the {@link #_form} is the visible view in the {@link #_balloon}.
	 *
	 * @private
	 * @type {Boolean}
	 */
	get _isVisible() {
		return this._balloon.visibleView === this._form;
	}

	/**
	 * Returns `true` when the {@link #_form} is in the {@link #_balloon}.
	 *
	 * @private
	 * @type {Boolean}
	 */
	get _isInBalloon() {
		return this._balloon.hasView( this._form );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagetextalternative.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagetextalternative
 */





/**
 * The image text alternative plugin.
 *
 * For a detailed overview, check the {@glink features/images/images-styles image styles} documentation.
 *
 * This is a "glue" plugin which loads the
 *  {@link module:image/imagetextalternative/imagetextalternativeediting~ImageTextAlternativeEditing}
 * and {@link module:image/imagetextalternative/imagetextalternativeui~ImageTextAlternativeUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageTextAlternative extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageTextAlternativeEditing, ImageTextAlternativeUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageTextAlternative';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/converters
 */



/**
 * Returns a function that converts the image view representation:
 *
 *		<figure class="image"><img src="..." alt="..."></img></figure>
 *
 * to the model representation:
 *
 *		<imageBlock src="..." alt="..."></imageBlock>
 *
 * The entire content of the `<figure>` element except the first `<img>` is being converted as children
 * of the `<imageBlock>` model element.
 *
 * @protected
 * @param {module:image/imageutils~ImageUtils} imageUtils
 * @returns {Function}
 */
function upcastImageFigure( imageUtils ) {
	return dispatcher => {
		dispatcher.on( 'element:figure', converter );
	};

	function converter( evt, data, conversionApi ) {
		// Do not convert if this is not an "image figure".
		if ( !conversionApi.consumable.test( data.viewItem, { name: true, classes: 'image' } ) ) {
			return;
		}

		// Find an image element inside the figure element.
		const viewImage = imageUtils.findViewImgElement( data.viewItem );

		// Do not convert if image element is absent or was already converted.
		if ( !viewImage || !conversionApi.consumable.test( viewImage, { name: true } ) ) {
			return;
		}

		// Consume the figure to prevent other converters from processing it again.
		conversionApi.consumable.consume( data.viewItem, { name: true, classes: 'image' } );

		// Convert view image to model image.
		const conversionResult = conversionApi.convertItem( viewImage, data.modelCursor );

		// Get image element from conversion result.
		const modelImage = first_first( conversionResult.modelRange.getItems() );

		// When image wasn't successfully converted then finish conversion.
		if ( !modelImage ) {
			// Revert consumed figure so other features can convert it.
			conversionApi.consumable.revert( data.viewItem, { name: true, classes: 'image' } );

			return;
		}

		// Convert rest of the figure element's children as an image children.
		conversionApi.convertChildren( data.viewItem, modelImage );

		conversionApi.updateConversionResult( modelImage, data );
	}
}

/**
 * Returns a function that converts the image view representation:
 *
 *		<picture><source ... /><source ... />...<img ... /></picture>
 *
 * to the model representation as the `sources` attribute:
 *
 *		<image[Block|Inline] ... sources="..."></image[Block|Inline]>
 *
 * @protected
 * @param {module:image/imageutils~ImageUtils} imageUtils
 * @returns {Function}
 */
function upcastPicture( imageUtils ) {
	const sourceAttributeNames = [ 'srcset', 'media', 'type', 'sizes' ];

	return dispatcher => {
		dispatcher.on( 'element:picture', converter );
	};

	function converter( evt, data, conversionApi ) {
		const pictureViewElement = data.viewItem;

		// Do not convert <picture> if already consumed.
		if ( !conversionApi.consumable.test( pictureViewElement, { name: true } ) ) {
			return;
		}

		const sources = new Map();

		// Collect all <source /> elements attribute values.
		for ( const childSourceElement of pictureViewElement.getChildren() ) {
			if ( childSourceElement.is( 'element', 'source' ) ) {
				const attributes = {};

				for ( const name of sourceAttributeNames ) {
					if ( childSourceElement.hasAttribute( name ) ) {
						// Don't collect <source /> attribute if already consumed somewhere else.
						if ( conversionApi.consumable.test( childSourceElement, { attributes: name } ) ) {
							attributes[ name ] = childSourceElement.getAttribute( name );
						}
					}
				}

				if ( Object.keys( attributes ).length ) {
					sources.set( childSourceElement, attributes );
				}
			}
		}

		const imgViewElement = imageUtils.findViewImgElement( pictureViewElement );

		// Don't convert when a picture has no <img/> inside (it is broken).
		if ( !imgViewElement ) {
			return;
		}

		let modelImage = data.modelCursor.parent;

		// - In case of an inline image (cursor parent in a <paragraph>), the <img/> must be converted right away
		// because no converter handled it yet and otherwise there would be no model element to set the sources attribute on.
		// - In case of a block image, the <figure class="image"> converter (in ImageBlockEditing) converts the
		// <img/> right away on its own and the modelCursor is already inside an imageBlock and there's nothing special
		// to do here.
		if ( !modelImage.is( 'element', 'imageBlock' ) ) {
			const conversionResult = conversionApi.convertItem( imgViewElement, data.modelCursor );

			// Set image range as conversion result.
			data.modelRange = conversionResult.modelRange;

			// Continue conversion where image conversion ends.
			data.modelCursor = conversionResult.modelCursor;

			modelImage = first( conversionResult.modelRange.getItems() );
		}

		conversionApi.consumable.consume( pictureViewElement, { name: true } );

		// Consume only these <source/> attributes that were actually collected and will be passed on
		// to the image model element.
		for ( const [ sourceElement, attributes ] of sources ) {
			conversionApi.consumable.consume( sourceElement, { attributes: Object.keys( attributes ) } );
		}

		if ( sources.size ) {
			conversionApi.writer.setAttribute( 'sources', Array.from( sources.values() ), modelImage );
		}

		// Convert rest of the <picture> children as an image children. Other converters may want to consume them.
		conversionApi.convertChildren( pictureViewElement, modelImage );
	}
}

/**
 * Converter used to convert the `srcset` model image attribute to the `srcset`, `sizes` and `width` attributes in the view.
 *
 * @protected
 * @param {module:image/imageutils~ImageUtils} imageUtils
 * @param {'imageBlock'|'imageInline'} imageType The type of the image.
 * @returns {Function}
 */
function downcastSrcsetAttribute( imageUtils, imageType ) {
	return dispatcher => {
		dispatcher.on( `attribute:srcset:${ imageType }`, converter );
	};

	function converter( evt, data, conversionApi ) {
		if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
			return;
		}

		const writer = conversionApi.writer;
		const element = conversionApi.mapper.toViewElement( data.item );
		const img = imageUtils.findViewImgElement( element );

		if ( data.attributeNewValue === null ) {
			const srcset = data.attributeOldValue;

			if ( srcset.data ) {
				writer.removeAttribute( 'srcset', img );
				writer.removeAttribute( 'sizes', img );

				if ( srcset.width ) {
					writer.removeAttribute( 'width', img );
				}
			}
		} else {
			const srcset = data.attributeNewValue;

			if ( srcset.data ) {
				writer.setAttribute( 'srcset', srcset.data, img );
				// Always outputting `100vw`. See https://github.com/ckeditor/ckeditor5-image/issues/2.
				writer.setAttribute( 'sizes', '100vw', img );

				if ( srcset.width ) {
					writer.setAttribute( 'width', srcset.width, img );
				}
			}
		}
	}
}

/**
 * Converts the `source` model attribute to the `<picture><source /><source />...<img /></picture>`
 * view structure.
 *
 * @protected
 * @param {module:image/imageutils~ImageUtils} imageUtils
 * @returns {Function}
 */
function downcastSourcesAttribute( imageUtils ) {
	return dispatcher => {
		dispatcher.on( 'attribute:sources:imageBlock', converter );
		dispatcher.on( 'attribute:sources:imageInline', converter );
	};

	function converter( evt, data, conversionApi ) {
		if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
			return;
		}

		const viewWriter = conversionApi.writer;
		const element = conversionApi.mapper.toViewElement( data.item );
		const imgElement = imageUtils.findViewImgElement( element );

		if ( data.attributeNewValue && data.attributeNewValue.length ) {
			// Make sure <picture> does not break attribute elements, for instance <a> in linked images.
			const pictureElement = viewWriter.createContainerElement( 'picture', null,
				data.attributeNewValue.map( sourceAttributes => {
					return viewWriter.createEmptyElement( 'source', sourceAttributes );
				} )
			);

			// Collect all wrapping attribute elements.
			const attributeElements = [];
			let viewElement = imgElement.parent;

			while ( viewElement && viewElement.is( 'attributeElement' ) ) {
				const parentElement = viewElement.parent;

				viewWriter.unwrap( viewWriter.createRangeOn( imgElement ), viewElement );

				attributeElements.unshift( viewElement );
				viewElement = parentElement;
			}

			// Insert the picture and move img into it.
			viewWriter.insert( viewWriter.createPositionBefore( imgElement ), pictureElement );
			viewWriter.move( viewWriter.createRangeOn( imgElement ), viewWriter.createPositionAt( pictureElement, 'end' ) );

			// Apply collected attribute elements over the new picture element.
			for ( const attributeElement of attributeElements ) {
				viewWriter.wrap( viewWriter.createRangeOn( pictureElement ), attributeElement );
			}
		}
		// Both setting "sources" to an empty array and removing the attribute should unwrap the <img />.
		// Unwrap once if the latter followed the former, though.
		else if ( imgElement.parent.is( 'element', 'picture' ) ) {
			const pictureElement = imgElement.parent;

			viewWriter.move( viewWriter.createRangeOn( imgElement ), viewWriter.createPositionBefore( pictureElement ) );
			viewWriter.remove( pictureElement );
		}
	}
}

/**
 * Converter used to convert a given image attribute from the model to the view.
 *
 * @protected
 * @param {module:image/imageutils~ImageUtils} imageUtils
 * @param {'imageBlock'|'imageInline'} imageType The type of the image.
 * @param {String} attributeKey The name of the attribute to convert.
 * @returns {Function}
 */
function downcastImageAttribute( imageUtils, imageType, attributeKey ) {
	return dispatcher => {
		dispatcher.on( `attribute:${ attributeKey }:${ imageType }`, converter );
	};

	function converter( evt, data, conversionApi ) {
		if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
			return;
		}

		const viewWriter = conversionApi.writer;
		const element = conversionApi.mapper.toViewElement( data.item );
		const img = imageUtils.findViewImgElement( element );

		viewWriter.setAttribute( data.attributeKey, data.attributeNewValue || '', img );
	}
}


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/imageloadobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/imageloadobserver
 */



/**
 * Observes all new images added to the {@link module:engine/view/document~Document},
 * fires {@link module:engine/view/document~Document#event:imageLoaded} and
 * {@link module:engine/view/document~Document#event:layoutChanged} event every time when the new image
 * has been loaded.
 *
 * **Note:** This event is not fired for images that has been added to the document and rendered as `complete` (already loaded).
 *
 * @extends module:engine/view/observer/observer~Observer
 */
class ImageLoadObserver extends Observer {
	/**
	 * @inheritDoc
	 */
	observe( domRoot ) {
		this.listenTo( domRoot, 'load', ( event, domEvent ) => {
			const domElement = domEvent.target;

			if ( this.checkShouldIgnoreEventFromTarget( domElement ) ) {
				return;
			}

			if ( domElement.tagName == 'IMG' ) {
				this._fireEvents( domEvent );
			}
			// Use capture phase for better performance (#4504).
		}, { useCapture: true } );
	}

	/**
	 * Fires {@link module:engine/view/document~Document#event:layoutChanged} and
	 * {@link module:engine/view/document~Document#event:imageLoaded}
	 * if observer {@link #isEnabled is enabled}.
	 *
	 * @protected
	 * @param {Event} domEvent The DOM event.
	 */
	_fireEvents( domEvent ) {
		if ( this.isEnabled ) {
			this.document.fire( 'layoutChanged' );
			this.document.fire( 'imageLoaded', domEvent );
		}
	}
}

/**
 * Fired when an <img/> DOM element has been loaded in the DOM root.
 *
 * Introduced by {@link module:image/image/imageloadobserver~ImageLoadObserver}.
 *
 * @see module:image/image/imageloadobserver~ImageLoadObserver
 * @event module:engine/view/document~Document#event:imageLoaded
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/insertimagecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */




/**
 * @module image/image/insertimagecommand
 */

/**
 * Insert image command.
 *
 * The command is registered by the {@link module:image/image/imageediting~ImageEditing} plugin as `insertImage`
 * and it is also available via aliased `imageInsert` name.
 *
 * In order to insert an image at the current selection position
 * (according to the {@link module:widget/utils~findOptimalInsertionRange} algorithm),
 * execute the command and specify the image source:
 *
 *		editor.execute( 'insertImage', { source: 'http://url.to.the/image' } );
 *
 * It is also possible to insert multiple images at once:
 *
 *		editor.execute( 'insertImage', {
 *			source:  [
 *				'path/to/image.jpg',
 *				'path/to/other-image.jpg'
 *			]
 *		} );
 *
 * If you want to take the full control over the process, you can specify individual model attributes:
 *
 *		editor.execute( 'insertImage', {
 *			source:  [
 *				{ src: 'path/to/image.jpg', alt: 'First alt text' },
 *				{ src: 'path/to/other-image.jpg', alt: 'Second alt text', customAttribute: 'My attribute value' }
 *			]
 *		} );
 *
 * @extends module:core/command~Command
 */
class InsertImageCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		const configImageInsertType = editor.config.get( 'image.insert.type' );

		if ( !editor.plugins.has( 'ImageBlockEditing' ) ) {
			if ( configImageInsertType === 'block' ) {
				/**
				 * The {@link module:image/imageblock~ImageBlock} plugin must be enabled to allow inserting block images. See
				 * {@link module:image/imageinsert~ImageInsertConfig#type} to learn more.
				 *
				 * @error image-block-plugin-required
				 */
				logWarning( 'image-block-plugin-required' );
			}
		}

		if ( !editor.plugins.has( 'ImageInlineEditing' ) ) {
			if ( configImageInsertType === 'inline' ) {
				/**
				 * The {@link module:image/imageinline~ImageInline} plugin must be enabled to allow inserting inline images. See
				 * {@link module:image/imageinsert~ImageInsertConfig#type} to learn more.
				 *
				 * @error image-inline-plugin-required
				 */
				logWarning( 'image-inline-plugin-required' );
			}
		}
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.isEnabled = this.editor.plugins.get( 'ImageUtils' ).isImageAllowed();
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} options Options for the executed command.
	 * @param {String|Array.<String>|Array.<Object>} options.source The image source or an array of image sources to insert.
	 * See the documentation of the command to learn more about accepted formats.
	 */
	execute( options ) {
		const sourceDefinitions = toArray( options.source );
		const selection = this.editor.model.document.selection;
		const imageUtils = this.editor.plugins.get( 'ImageUtils' );

		// In case of multiple images, each image (starting from the 2nd) will be inserted at a position that
		// follows the previous one. That will move the selection and, to stay on the safe side and make sure
		// all images inherit the same selection attributes, they are collected beforehand.
		//
		// Applying these attributes ensures, for instance, that inserting an (inline) image into a link does
		// not split that link but preserves its continuity.
		//
		// Note: Selection attributes that do not make sense for images will be filtered out by insertImage() anyway.
		const selectionAttributes = Object.fromEntries( selection.getAttributes() );

		sourceDefinitions.forEach( ( sourceDefinition, index ) => {
			const selectedElement = selection.getSelectedElement();

			if ( typeof sourceDefinition === 'string' ) {
				sourceDefinition = { src: sourceDefinition };
			}

			// Inserting of an inline image replace the selected element and make a selection on the inserted image.
			// Therefore inserting multiple inline images requires creating position after each element.
			if ( index && selectedElement && imageUtils.isImage( selectedElement ) ) {
				const position = this.editor.model.createPositionAfter( selectedElement );

				imageUtils.insertImage( { ...sourceDefinition, ...selectionAttributes }, position );
			} else {
				imageUtils.insertImage( { ...sourceDefinition, ...selectionAttributes } );
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/imageediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/imageediting
 */






/**
 * The image engine plugin. This module loads common code shared between
 * {@link module:image/image/imageinlineediting~ImageInlineEditing} and
 * {@link module:image/image/imageblockediting~ImageBlockEditing} plugins.
 *
 * This plugin registers the {@link module:image/image/insertimagecommand~InsertImageCommand 'insertImage'} command.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUtils ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const conversion = editor.conversion;

		// See https://github.com/ckeditor/ckeditor5-image/issues/142.
		editor.editing.view.addObserver( ImageLoadObserver );

		conversion.for( 'upcast' )
			.attributeToAttribute( {
				view: {
					name: 'img',
					key: 'alt'
				},
				model: 'alt'
			} )
			.attributeToAttribute( {
				view: {
					name: 'img',
					key: 'srcset'
				},
				model: {
					key: 'srcset',
					value: viewImage => {
						const value = {
							data: viewImage.getAttribute( 'srcset' )
						};

						if ( viewImage.hasAttribute( 'width' ) ) {
							value.width = viewImage.getAttribute( 'width' );
						}

						return value;
					}
				}
			} );

		const insertImageCommand = new InsertImageCommand( editor );

		// Register `insertImage` command and add `imageInsert` command as an alias for backward compatibility.
		editor.commands.add( 'insertImage', insertImageCommand );
		editor.commands.add( 'imageInsert', insertImageCommand );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/imagetypecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/imagetypecommand
 */



/**
 * The image type command. It changes the type of a selected image, depending on the configuration.
 *
 * @extends module:core/command~Command
 */
class ImageTypeCommand extends command_Command {
	/**
	 * @inheritDoc
	 *
	 * @param {module:core/editor/editor~Editor} editor
	 * @param {'imageBlock'|'imageInline'} modelElementName Model element name the command converts to.
	 */
	constructor( editor, modelElementName ) {
		super( editor );

		/**
		 * Model element name the command converts to.
		 *
		 * @readonly
		 * @private
		 * @member {'imageBlock'|'imageInline'}
		 */
		this._modelElementName = modelElementName;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const element = imageUtils.getClosestSelectedImageElement( this.editor.model.document.selection );

		if ( this._modelElementName === 'imageBlock' ) {
			this.isEnabled = imageUtils.isInlineImage( element );
		} else {
			this.isEnabled = imageUtils.isBlockImage( element );
		}
	}

	/**
	 * Executes the command and changes the type of a selected image.
	 *
	 * @fires execute
	 * @returns {Object|null} An object containing references to old and new model image elements
	 * (for before and after the change) so external integrations can hook into the decorated
	 * `execute` event and handle this change. `null` if the type change failed.
	 */
	execute() {
		const editor = this.editor;
		const model = this.editor.model;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const oldElement = imageUtils.getClosestSelectedImageElement( model.document.selection );
		const attributes = Object.fromEntries( oldElement.getAttributes() );

		// Don't change image type if "src" is missing (a broken image), unless there's "uploadId" set.
		// This state may happen during image upload (before it finishes) and it should be possible to change type
		// of the image in the meantime.
		if ( !attributes.src && !attributes.uploadId ) {
			return null;
		}

		return model.change( writer => {
			// Get all markers that contain the old image element.
			const markers = Array.from( model.markers )
				.filter( marker => marker.getRange().containsItem( oldElement ) );

			const newElement = imageUtils.insertImage( attributes, model.createSelection( oldElement, 'on' ), this._modelElementName );

			if ( !newElement ) {
				return null;
			}

			const newElementRange = writer.createRangeOn( newElement );

			// Expand the previously intersecting markers' ranges to include the new image element.
			for ( const marker of markers ) {
				const markerRange = marker.getRange();

				// Join the survived part of the old marker range with the new element range
				// (loosely because there could be some new paragraph or the existing one might got split).
				const range = markerRange.root.rootName != '$graveyard' ?
					markerRange.getJoined( newElementRange, true ) : newElementRange;

				writer.updateMarker( marker, { range } );
			}

			return {
				oldElement,
				newElement
			};
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/imageblockediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/imageblockediting
 */












/**
 * The image block plugin.
 *
 * It registers:
 *
 * * `<imageBlock>` as a block element in the document schema, and allows `alt`, `src` and `srcset` attributes.
 * * converters for editing and data pipelines.,
 * * {@link module:image/image/imagetypecommand~ImageTypeCommand `'imageTypeBlock'`} command that converts inline images into
 * block images.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageBlockEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageEditing, ImageUtils, ClipboardPipeline ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageBlockEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;

		// Converters 'alt' and 'srcset' are added in 'ImageEditing' plugin.
		schema.register( 'imageBlock', {
			inheritAllFrom: '$blockObject',
			allowAttributes: [ 'alt', 'src', 'srcset' ]
		} );

		this._setupConversion();

		if ( editor.plugins.has( 'ImageInlineEditing' ) ) {
			editor.commands.add( 'imageTypeBlock', new ImageTypeCommand( this.editor, 'imageBlock' ) );

			this._setupClipboardIntegration();
		}
	}

	/**
	 * Configures conversion pipelines to support upcasting and downcasting
	 * block images (block image widgets) and their attributes.
	 *
	 * @private
	 */
	_setupConversion() {
		const editor = this.editor;
		const t = editor.t;
		const conversion = editor.conversion;
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		conversion.for( 'dataDowncast' )
			.elementToStructure( {
				model: 'imageBlock',
				view: ( modelElement, { writer } ) => createBlockImageViewElement( writer )
			} );

		conversion.for( 'editingDowncast' )
			.elementToStructure( {
				model: 'imageBlock',
				view: ( modelElement, { writer } ) => imageUtils.toImageWidget(
					createBlockImageViewElement( writer ), writer, t( 'image widget' )
				)
			} );

		conversion.for( 'downcast' )
			.add( downcastImageAttribute( imageUtils, 'imageBlock', 'src' ) )
			.add( downcastImageAttribute( imageUtils, 'imageBlock', 'alt' ) )
			.add( downcastSrcsetAttribute( imageUtils, 'imageBlock' ) );

		// More image related upcasts are in 'ImageEditing' plugin.
		conversion.for( 'upcast' )
			.elementToElement( {
				view: getImgViewElementMatcher( editor, 'imageBlock' ),
				model: ( viewImage, { writer } ) => writer.createElement(
					'imageBlock',
					viewImage.hasAttribute( 'src' ) ? { src: viewImage.getAttribute( 'src' ) } : null
				)
			} )
			.add( upcastImageFigure( imageUtils ) );
	}

	/**
	 * Integrates the plugin with the clipboard pipeline.
	 *
	 * Idea is that the feature should recognize the user's intent when an **inline** image is
	 * pasted or dropped. If such an image is pasted/dropped:
	 *
	 * * into an empty block (e.g. an empty paragraph),
	 * * on another object (e.g. some block widget).
	 *
	 * it gets converted into a block image on the fly. We assume this is the user's intent
	 * if they decided to put their image there.
	 *
	 * See the `ImageInlineEditing` for the similar integration that works in the opposite direction.
	 *
	 * @private
	 */
	_setupClipboardIntegration() {
		const editor = this.editor;
		const model = editor.model;
		const editingView = editor.editing.view;
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', ( evt, data ) => {
			const docFragmentChildren = Array.from( data.content.getChildren() );
			let modelRange;

			// Make sure only <img> elements are dropped or pasted. Otherwise, if there some other HTML
			// mixed up, this should be handled as a regular paste.
			if ( !docFragmentChildren.every( imageUtils.isInlineImageView ) ) {
				return;
			}

			// When drag and dropping, data.targetRanges specifies where to drop because
			// this is usually a different place than the current model selection (the user
			// uses a drop marker to specify the drop location).
			if ( data.targetRanges ) {
				modelRange = editor.editing.mapper.toModelRange( data.targetRanges[ 0 ] );
			}
			// Pasting, however, always occurs at the current model selection.
			else {
				modelRange = model.document.selection.getFirstRange();
			}

			const selection = model.createSelection( modelRange );

			// Convert inline images into block images only when the currently selected block is empty
			// (e.g. an empty paragraph) or some object is selected (to replace it).
			if ( determineImageTypeForInsertionAtSelection( model.schema, selection ) === 'imageBlock' ) {
				const writer = new UpcastWriter( editingView.document );

				// Wrap <img ... /> -> <figure class="image"><img .../></figure>
				const blockViewImages = docFragmentChildren.map(
					inlineViewImage => writer.createElement( 'figure', { class: 'image' }, inlineViewImage )
				);

				data.content = writer.createDocumentFragment( blockViewImages );
			}
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/image.css
var theme_image = __webpack_require__(9048);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/image.css

            

var image_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

image_options.insert = "head";
image_options.singleton = true;

var image_update = injectStylesIntoStyleTag_default()(theme_image/* default */.Z, image_options);



/* harmony default export */ const ckeditor5_image_theme_image = (theme_image/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageblock.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageblock
 */









/**
 * The image inline plugin.
 *
 * This is a "glue" plugin which loads the following plugins:
 *
 * * {@link module:image/image/imageblockediting~ImageBlockEditing},
 * * {@link module:image/imagetextalternative~ImageTextAlternative}.
 *
 * Usually, it is used in conjunction with other plugins from this package. See the {@glink api/image package page}
 * for more information.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageBlock extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageBlockEditing, Widget, ImageTextAlternative ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageBlock';
	}
}


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image/imageinlineediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image/imageinlineediting
 */












/**
 * The image inline plugin.
 *
 * It registers:
 *
 * * `<imageInline>` as an inline element in the document schema, and allows `alt`, `src` and `srcset` attributes.
 * * converters for editing and data pipelines.
 * * {@link module:image/image/imagetypecommand~ImageTypeCommand `'imageTypeInline'`} command that converts block images into
 * inline images.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageInlineEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageEditing, ImageUtils, ClipboardPipeline ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageInlineEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;

		// Converters 'alt' and 'srcset' are added in 'ImageEditing' plugin.
		schema.register( 'imageInline', {
			inheritAllFrom: '$inlineObject',
			allowAttributes: [ 'alt', 'src', 'srcset' ]
		} );

		// Disallow inline images in captions (for now). This is the best spot to do that because
		// independent packages can introduce captions (ImageCaption, TableCaption, etc.) so better this
		// be future-proof.
		schema.addChildCheck( ( context, childDefinition ) => {
			if ( context.endsWith( 'caption' ) && childDefinition.name === 'imageInline' ) {
				return false;
			}
		} );

		this._setupConversion();

		if ( editor.plugins.has( 'ImageBlockEditing' ) ) {
			editor.commands.add( 'imageTypeInline', new ImageTypeCommand( this.editor, 'imageInline' ) );

			this._setupClipboardIntegration();
		}
	}

	/**
	 * Configures conversion pipelines to support upcasting and downcasting
	 * inline images (inline image widgets) and their attributes.
	 *
	 * @private
	 */
	_setupConversion() {
		const editor = this.editor;
		const t = editor.t;
		const conversion = editor.conversion;
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		conversion.for( 'dataDowncast' )
			.elementToElement( {
				model: 'imageInline',
				view: ( modelElement, { writer } ) => writer.createEmptyElement( 'img' )
			} );

		conversion.for( 'editingDowncast' )
			.elementToStructure( {
				model: 'imageInline',
				view: ( modelElement, { writer } ) => imageUtils.toImageWidget(
					createInlineImageViewElement( writer ), writer, t( 'image widget' )
				)
			} );

		conversion.for( 'downcast' )
			.add( downcastImageAttribute( imageUtils, 'imageInline', 'src' ) )
			.add( downcastImageAttribute( imageUtils, 'imageInline', 'alt' ) )
			.add( downcastSrcsetAttribute( imageUtils, 'imageInline' ) );

		// More image related upcasts are in 'ImageEditing' plugin.
		conversion.for( 'upcast' )
			.elementToElement( {
				view: getImgViewElementMatcher( editor, 'imageInline' ),
				model: ( viewImage, { writer } ) => writer.createElement(
					'imageInline',
					viewImage.hasAttribute( 'src' ) ? { src: viewImage.getAttribute( 'src' ) } : null
				)
			} );
	}

	/**
	 * Integrates the plugin with the clipboard pipeline.
	 *
	 * Idea is that the feature should recognize the user's intent when an **block** image is
	 * pasted or dropped. If such an image is pasted/dropped into a non-empty block
	 * (e.g. a paragraph with some text) it gets converted into an inline image on the fly.
	 *
	 * We assume this is the user's intent if they decided to put their image there.
	 *
	 * **Note**: If a block image has a caption, it will not be converted to an inline image
	 * to avoid the confusion. Captions are added on purpose and they should never be lost
	 * in the clipboard pipeline.
	 *
	 * See the `ImageBlockEditing` for the similar integration that works in the opposite direction.
	 *
	 * @private
	 */
	_setupClipboardIntegration() {
		const editor = this.editor;
		const model = editor.model;
		const editingView = editor.editing.view;
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', ( evt, data ) => {
			const docFragmentChildren = Array.from( data.content.getChildren() );
			let modelRange;

			// Make sure only <figure class="image"></figure> elements are dropped or pasted. Otherwise, if there some other HTML
			// mixed up, this should be handled as a regular paste.
			if ( !docFragmentChildren.every( imageUtils.isBlockImageView ) ) {
				return;
			}

			// When drag and dropping, data.targetRanges specifies where to drop because
			// this is usually a different place than the current model selection (the user
			// uses a drop marker to specify the drop location).
			if ( data.targetRanges ) {
				modelRange = editor.editing.mapper.toModelRange( data.targetRanges[ 0 ] );
			}
			// Pasting, however, always occurs at the current model selection.
			else {
				modelRange = model.document.selection.getFirstRange();
			}

			const selection = model.createSelection( modelRange );

			// Convert block images into inline images only when pasting or dropping into non-empty blocks
			// and when the block is not an object (e.g. pasting to replace another widget).
			if ( determineImageTypeForInsertionAtSelection( model.schema, selection ) === 'imageInline' ) {
				const writer = new UpcastWriter( editingView.document );

				// Unwrap <figure class="image"><img .../></figure> -> <img ... />
				// but <figure class="image"><img .../><figcaption>...</figcaption></figure> -> stays the same
				const inlineViewImages = docFragmentChildren.map( blockViewImage => {
					// If there's just one child, it can be either <img /> or <a><img></a>.
					// If there are other children than <img>, this means that the block image
					// has a caption or some other features and this kind of image should be
					// pasted/dropped without modifications.
					if ( blockViewImage.childCount === 1 ) {
						// Pass the attributes which are present only in the <figure> to the <img>
						// (e.g. the style="width:10%" attribute applied by the ImageResize plugin).
						Array.from( blockViewImage.getAttributes() )
							.forEach( attribute => writer.setAttribute(
								...attribute,
								imageUtils.findViewImgElement( blockViewImage )
							) );

						return blockViewImage.getChild( 0 );
					} else {
						return blockViewImage;
					}
				} );

				data.content = writer.createDocumentFragment( inlineViewImages );
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageinline.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageinline
 */









/**
 * The image inline plugin.
 *
 * This is a "glue" plugin which loads the following plugins:
 *
 * * {@link module:image/image/imageinlineediting~ImageInlineEditing},
 * * {@link module:image/imagetextalternative~ImageTextAlternative}.
 *
 * Usually, it is used in conjunction with other plugins from this package. See the {@glink api/image package page}
 * for more information.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageInline extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageInlineEditing, Widget, ImageTextAlternative ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageInline';
	}
}


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/image.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/image
 */








/**
 * The image plugin.
 *
 * For a detailed overview, check the {@glink features/images/images-overview image feature} documentation.
 *
 * This is a "glue" plugin which loads the following plugins:
 *
 * * {@link module:image/imageblock~ImageBlock},
 * * {@link module:image/imageinline~ImageInline},
 *
 * Usually, it is used in conjunction with other plugins from this package. See the {@glink api/image package page}
 * for more information.
 *
 * @extends module:core/plugin~Plugin
 */
class Image extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageBlock, ImageInline ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Image';
	}
}

/**
 * The configuration of the image features. Used by the image features in the `@ckeditor/ckeditor5-image` package.
 *
 * Read more in {@link module:image/image~ImageConfig}.
 *
 * @member {module:image/image~ImageConfig} module:core/editor/editorconfig~EditorConfig#image
 */

/**
 * The configuration of the image features. Used by the image features in the `@ckeditor/ckeditor5-image` package.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				image: ... // Image feature options.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface ImageConfig
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagecaption/toggleimagecaptioncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagecaption/toggleimagecaptioncommand
 */





/**
 * The toggle image caption command.
 *
 * This command is registered by {@link module:image/imagecaption/imagecaptionediting~ImageCaptionEditing} as the
 * `'toggleImageCaption'` editor command.
 *
 * Executing this command:
 *
 * * either adds or removes the image caption of a selected image (depending on whether the caption is present or not),
 * * removes the image caption if the selection is anchored in one.
 *
 *		// Toggle the presence of the caption.
 *		editor.execute( 'toggleImageCaption' );
 *
 * **Note**: Upon executing this command, the selection will be set on the image if previously anchored in the caption element.
 *
 * **Note**: You can move the selection to the caption right away as it shows up upon executing this command by using
 * the `focusCaptionOnShow` option:
 *
 *		editor.execute( 'toggleImageCaption', { focusCaptionOnShow: true } );
 *
 * @extends module:core/command~Command
 */
class ToggleImageCaptionCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );

		// Only block images can get captions.
		if ( !editor.plugins.has( ImageBlockEditing ) ) {
			this.isEnabled = false;
			this.value = false;

			return;
		}

		const selection = editor.model.document.selection;
		const selectedElement = selection.getSelectedElement();

		if ( !selectedElement ) {
			const ancestorCaptionElement = imageCaptionUtils.getCaptionFromModelSelection( selection );

			this.isEnabled = !!ancestorCaptionElement;
			this.value = !!ancestorCaptionElement;

			return;
		}

		// Block images support captions by default but the command should also be enabled for inline
		// images because toggling the caption when one is selected should convert it into a block image.
		this.isEnabled = this.editor.plugins.get( 'ImageUtils' ).isImage( selectedElement );

		if ( !this.isEnabled ) {
			this.value = false;
		} else {
			this.value = !!imageCaptionUtils.getCaptionFromImageModelElement( selectedElement );
		}
	}

	/**
	 * Executes the command.
	 *
	 *		editor.execute( 'toggleImageCaption' );
	 *
	 * @param {Object} [options] Options for the executed command.
	 * @param {String} [options.focusCaptionOnShow] When true and the caption shows up, the selection will be moved into it straight away.
	 * @fires execute
	 */
	execute( options = {} ) {
		const { focusCaptionOnShow } = options;

		this.editor.model.change( writer => {
			if ( this.value ) {
				this._hideImageCaption( writer );
			} else {
				this._showImageCaption( writer, focusCaptionOnShow );
			}
		} );
	}

	/**
	 * Shows the caption of the `<imageBlock>` or `<imageInline>`. Also:
	 *
	 * * it converts `<imageInline>` to `<imageBlock>` to show the caption,
	 * * it attempts to restore the caption content from the `ImageCaptionEditing` caption registry,
	 * * it moves the selection to the caption right away, it the `focusCaptionOnShow` option was set.
	 *
	 * @private
	 * @param {module:engine/model/writer~Writer} writer
	 */
	_showImageCaption( writer, focusCaptionOnShow ) {
		const model = this.editor.model;
		const selection = model.document.selection;
		const imageCaptionEditing = this.editor.plugins.get( 'ImageCaptionEditing' );

		let selectedImage = selection.getSelectedElement();

		const savedCaption = imageCaptionEditing._getSavedCaption( selectedImage );

		// Convert imageInline -> image first.
		if ( this.editor.plugins.get( 'ImageUtils' ).isInlineImage( selectedImage ) ) {
			this.editor.execute( 'imageTypeBlock' );

			// Executing the command created a new model element. Let's pick it again.
			selectedImage = selection.getSelectedElement();
		}

		// Try restoring the caption from the ImageCaptionEditing plugin storage.
		const newCaptionElement = savedCaption || writer.createElement( 'caption' );

		writer.append( newCaptionElement, selectedImage );

		if ( focusCaptionOnShow ) {
			writer.setSelection( newCaptionElement, 'in' );
		}
	}

	/**
	 * Hides the caption of a selected image (or an image caption the selection is anchored to).
	 *
	 * The content of the caption is stored in the `ImageCaptionEditing` caption registry to make this
	 * a reversible action.
	 *
	 * @private
	 * @param {module:engine/model/writer~Writer} writer
	 */
	_hideImageCaption( writer ) {
		const editor = this.editor;
		const selection = editor.model.document.selection;
		const imageCaptionEditing = editor.plugins.get( 'ImageCaptionEditing' );
		const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
		let selectedImage = selection.getSelectedElement();
		let captionElement;

		if ( selectedImage ) {
			captionElement = imageCaptionUtils.getCaptionFromImageModelElement( selectedImage );
		} else {
			captionElement = imageCaptionUtils.getCaptionFromModelSelection( selection );
			selectedImage = captionElement.parent;
		}

		// Store the caption content so it can be restored quickly if the user changes their mind even if they toggle image<->imageInline.
		imageCaptionEditing._saveCaption( selectedImage, captionElement );

		writer.setSelection( selectedImage, 'on' );
		writer.remove( captionElement );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionutils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagecaptionutils/utils
 */





/**
 * The image caption utilities plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageCaptionUtils extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageCaptionUtils';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUtils ];
	}

	/**
	 * Returns the caption model element from a given image element. Returns `null` if no caption is found.
	 *
	 * @param {module:engine/model/element~Element} imageModelElement
	 * @returns {module:engine/model/element~Element|null}
	 */
	getCaptionFromImageModelElement( imageModelElement ) {
		for ( const node of imageModelElement.getChildren() ) {
			if ( !!node && node.is( 'element', 'caption' ) ) {
				return node;
			}
		}

		return null;
	}

	/**
	 * Returns the caption model element for a model selection. Returns `null` if the selection has no caption element ancestor.
	 *
	 * @param {module:engine/model/selection~Selection} selection
	 * @returns {module:engine/model/element~Element|null}
	 */
	getCaptionFromModelSelection( selection ) {
		const imageUtils = this.editor.plugins.get( 'ImageUtils' );
		const captionElement = selection.getFirstPosition().findAncestor( 'caption' );

		if ( !captionElement ) {
			return null;
		}

		if ( imageUtils.isBlockImage( captionElement.parent ) ) {
			return captionElement;
		}

		return null;
	}

	/**
	 * {@link module:engine/view/matcher~Matcher} pattern. Checks if a given element is a `<figcaption>` element that is placed
	 * inside the image `<figure>` element.
	 *
	 * @param {module:engine/view/element~Element} element
	 * @returns {Object|null} Returns the object accepted by {@link module:engine/view/matcher~Matcher} or `null` if the element
	 * cannot be matched.
	 */
	matchImageCaptionViewElement( element ) {
		const imageUtils = this.editor.plugins.get( 'ImageUtils' );

		// Convert only captions for images.
		if ( element.name == 'figcaption' && imageUtils.isBlockImageView( element.parent ) ) {
			return { name: true };
		}

		return null;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagecaption/imagecaptionediting
 */










/**
 * The image caption engine plugin. It is responsible for:
 *
 * * registering converters for the caption element,
 * * registering converters for the caption model attribute,
 * * registering the {@link module:image/imagecaption/toggleimagecaptioncommand~ToggleImageCaptionCommand `toggleImageCaption`} command.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageCaptionEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUtils, ImageCaptionUtils ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageCaptionEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * A map that keeps saved JSONified image captions and image model elements they are
		 * associated with.
		 *
		 * To learn more about this system, see {@link #_saveCaption}.
		 *
		 * @member {WeakMap.<module:engine/model/element~Element,Object>}
		 */
		this._savedCaptionsMap = new WeakMap();
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;

		// Schema configuration.
		if ( !schema.isRegistered( 'caption' ) ) {
			schema.register( 'caption', {
				allowIn: 'imageBlock',
				allowContentOf: '$block',
				isLimit: true
			} );
		} else {
			schema.extend( 'caption', {
				allowIn: 'imageBlock'
			} );
		}

		editor.commands.add( 'toggleImageCaption', new ToggleImageCaptionCommand( this.editor ) );

		this._setupConversion();
		this._setupImageTypeCommandsIntegration();
		this._registerCaptionReconversion();
	}

	/**
	 * Configures conversion pipelines to support upcasting and downcasting
	 * image captions.
	 *
	 * @private
	 */
	_setupConversion() {
		const editor = this.editor;
		const view = editor.editing.view;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
		const t = editor.t;

		// View -> model converter for the data pipeline.
		editor.conversion.for( 'upcast' ).elementToElement( {
			view: element => imageCaptionUtils.matchImageCaptionViewElement( element ),
			model: 'caption'
		} );

		// Model -> view converter for the data pipeline.
		editor.conversion.for( 'dataDowncast' ).elementToElement( {
			model: 'caption',
			view: ( modelElement, { writer } ) => {
				if ( !imageUtils.isBlockImage( modelElement.parent ) ) {
					return null;
				}

				return writer.createContainerElement( 'figcaption' );
			}
		} );

		// Model -> view converter for the editing pipeline.
		editor.conversion.for( 'editingDowncast' ).elementToElement( {
			model: 'caption',
			view: ( modelElement, { writer } ) => {
				if ( !imageUtils.isBlockImage( modelElement.parent ) ) {
					return null;
				}

				const figcaptionElement = writer.createEditableElement( 'figcaption' );
				writer.setCustomProperty( 'imageCaption', true, figcaptionElement );

				enablePlaceholder( {
					view,
					element: figcaptionElement,
					text: t( 'Enter image caption' ),
					keepOnFocus: true
				} );

				const imageAlt = modelElement.parent.getAttribute( 'alt' );
				const label = imageAlt ? t( 'Caption for image: %0', [ imageAlt ] ) : t( 'Caption for the image' );

				return toWidgetEditable( figcaptionElement, writer, { label } );
			}
		} );
	}

	/**
	 * Integrates with {@link module:image/image/imagetypecommand~ImageTypeCommand image type commands}
	 * to make sure the caption is preserved when the type of an image changes so it can be restored
	 * in the future if the user decides they want their caption back.
	 *
	 * @private
	 */
	_setupImageTypeCommandsIntegration() {
		const editor = this.editor;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
		const imageTypeInlineCommand = editor.commands.get( 'imageTypeInline' );
		const imageTypeBlockCommand = editor.commands.get( 'imageTypeBlock' );

		const handleImageTypeChange = evt => {
			// The image type command execution can be unsuccessful.
			if ( !evt.return ) {
				return;
			}

			const { oldElement, newElement } = evt.return;

			/* istanbul ignore if: paranoid check */
			if ( !oldElement ) {
				return;
			}

			if ( imageUtils.isBlockImage( oldElement ) ) {
				const oldCaptionElement = imageCaptionUtils.getCaptionFromImageModelElement( oldElement );

				// If the old element was a captioned block image (the caption was visible),
				// simply save it so it can be restored.
				if ( oldCaptionElement ) {
					this._saveCaption( newElement, oldCaptionElement );

					return;
				}
			}

			const savedOldElementCaption = this._getSavedCaption( oldElement );

			// If either:
			//
			// * the block image didn't have a visible caption,
			// * the block image caption was hidden (and already saved),
			// * the inline image was passed
			//
			// just try to "pass" the saved caption from the old image to the new image
			// so it can be retrieved in the future if the user wants it back.
			if ( savedOldElementCaption ) {
				// Note: Since we're writing to a WeakMap, we don't bother with removing the
				// [ oldElement, savedOldElementCaption ] pair from it.
				this._saveCaption( newElement, savedOldElementCaption );
			}
		};

		// Presence of the commands depends on the Image(Inline|Block)Editing plugins loaded in the editor.
		if ( imageTypeInlineCommand ) {
			this.listenTo( imageTypeInlineCommand, 'execute', handleImageTypeChange, { priority: 'low' } );
		}

		if ( imageTypeBlockCommand ) {
			this.listenTo( imageTypeBlockCommand, 'execute', handleImageTypeChange, { priority: 'low' } );
		}
	}

	/**
	 * Returns the saved {@link module:engine/model/element~Element#toJSON JSONified} caption
	 * of an image model element.
	 *
	 * See {@link #_saveCaption}.
	 *
	 * @protected
	 * @param {module:engine/model/element~Element} imageModelElement The model element the
	 * caption should be returned for.
	 * @returns {module:engine/model/element~Element|null} The model caption element or `null` if there is none.
	 */
	_getSavedCaption( imageModelElement ) {
		const jsonObject = this._savedCaptionsMap.get( imageModelElement );

		return jsonObject ? element_Element.fromJSON( jsonObject ) : null;
	}

	/**
	 * Saves a {@link module:engine/model/element~Element#toJSON JSONified} caption for
	 * an image element to allow restoring it in the future.
	 *
	 * A caption is saved every time it gets hidden and/or the type of an image changes. The
	 * user should be able to restore it on demand.
	 *
	 * **Note**: The caption cannot be stored in the image model element attribute because,
	 * for instance, when the model state propagates to collaborators, the attribute would get
	 * lost (mainly because it does not convert to anything when the caption is hidden) and
	 * the states of collaborators' models would de-synchronize causing numerous issues.
	 *
	 * See {@link #_getSavedCaption}.
	 *
	 * @protected
	 * @param {module:engine/model/element~Element} imageModelElement The model element the
	 * caption is saved for.
	 * @param {module:engine/model/element~Element} caption The caption model element to be saved.
	 */
	_saveCaption( imageModelElement, caption ) {
		this._savedCaptionsMap.set( imageModelElement, caption.toJSON() );
	}

	/**
	 * Reconverts image caption when image alt attribute changes.
	 * The change of alt attribute is reflected in caption's aria-label attribute.
	 *
	 * @private
	 */
	_registerCaptionReconversion() {
		const editor = this.editor;
		const model = editor.model;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );

		model.document.on( 'change:data', () => {
			const changes = model.document.differ.getChanges();

			for ( const change of changes ) {
				if ( change.attributeKey !== 'alt' ) {
					continue;
				}

				const image = change.range.start.nodeAfter;

				if ( imageUtils.isBlockImage( image ) ) {
					const caption = imageCaptionUtils.getCaptionFromImageModelElement( image );

					if ( !caption ) {
						return;
					}

					editor.editing.reconvertItem( caption );
				}
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagecaption/imagecaptionui
 */





/**
 * The image caption UI plugin. It introduces the `'toggleImageCaption'` UI button.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageCaptionUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageCaptionUtils ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageCaptionUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const editingView = editor.editing.view;
		const imageCaptionUtils = editor.plugins.get( 'ImageCaptionUtils' );
		const t = editor.t;

		editor.ui.componentFactory.add( 'toggleImageCaption', locale => {
			const command = editor.commands.get( 'toggleImageCaption' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				icon: icons.caption,
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
			view.bind( 'label' ).to( command, 'value', value => value ? t( 'Toggle caption off' ) : t( 'Toggle caption on' ) );

			this.listenTo( view, 'execute', () => {
				editor.execute( 'toggleImageCaption', { focusCaptionOnShow: true } );

				// Scroll to the selection and highlight the caption if the caption showed up.
				const modelCaptionElement = imageCaptionUtils.getCaptionFromModelSelection( editor.model.document.selection );

				if ( modelCaptionElement ) {
					const figcaptionElement = editor.editing.mapper.toViewElement( modelCaptionElement );

					editingView.scrollToTheSelection();

					editingView.change( writer => {
						writer.addClass( 'image__caption_highlighted', figcaptionElement );
					} );
				}

				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imagecaption.css
var imagecaption = __webpack_require__(8662);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imagecaption.css

            

var imagecaption_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imagecaption_options.insert = "head";
imagecaption_options.singleton = true;

var imagecaption_update = injectStylesIntoStyleTag_default()(imagecaption/* default */.Z, imagecaption_options);



/* harmony default export */ const theme_imagecaption = (imagecaption/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagecaption.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagecaption
 */







/**
 * The image caption plugin.
 *
 * For a detailed overview, check the {@glink features/images/images-captions image caption} documentation.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageCaption extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageCaptionEditing, ImageCaptionUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageCaption';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-upload/src/ui/filedialogbuttonview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module upload/ui/filedialogbuttonview
 */


/**
 * The file dialog button view.
 *
 * This component provides a button that opens the native file selection dialog.
 * It can be used to implement the UI of a file upload feature.
 *
 *		const view = new FileDialogButtonView( locale );
 *
 *		view.set( {
 *			acceptedType: 'image/*',
 *			allowMultipleFiles: true
 *		} );
 *
 *		view.buttonView.set( {
 *			label: t( 'Insert image' ),
 *			icon: imageIcon,
 *			tooltip: true
 *		} );
 *
 *		view.on( 'done', ( evt, files ) => {
 *			for ( const file of Array.from( files ) ) {
 *				console.log( 'Selected file', file );
 *			}
 *		} );
 *
 * @extends module:ui/view~View
 */
class FileDialogButtonView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        /**
         * The button view of the component.
         *
         * @member {module:ui/button/buttonview~ButtonView}
         */
        this.buttonView = new buttonview_ButtonView(locale);
        /**
         * A hidden `<input>` view used to execute file dialog.
         *
         * @protected
         * @member {module:upload/ui/filedialogbuttonview~FileInputView}
         */
        this._fileInputView = new FileInputView(locale);
        /**
         * Accepted file types. Can be provided in form of file extensions, media type or one of:
         * * `audio/*`,
         * * `video/*`,
         * * `image/*`.
         *
         * @observable
         * @member {String} #acceptedType
         */
        this._fileInputView.bind('acceptedType').to(this);
        /**
         * Indicates if multiple files can be selected. Defaults to `true`.
         *
         * @observable
         * @member {Boolean} #allowMultipleFiles
         */
        this._fileInputView.bind('allowMultipleFiles').to(this);
        /**
         * Fired when file dialog is closed with file selected.
         *
         *		view.on( 'done', ( evt, files ) => {
         *			for ( const file of files ) {
         *				console.log( 'Selected file', file );
         *			}
         *		}
         *
         * @event done
         * @param {Array.<File>} files Array of selected files.
         */
        this._fileInputView.delegate('done').to(this);
        this.setTemplate({
            tag: 'span',
            attributes: {
                class: 'ck-file-dialog-button'
            },
            children: [
                this.buttonView,
                this._fileInputView
            ]
        });
        this.buttonView.on('execute', () => {
            this._fileInputView.open();
        });
    }
    /**
     * Focuses the {@link #buttonView}.
     */
    focus() {
        this.buttonView.focus();
    }
}
/**
 * The hidden file input view class.
 *
 * @private
 * @extends module:ui/view~View
 */
class FileInputView extends src_view_View {
    /**
     * @inheritDoc
     */
    constructor(locale) {
        super(locale);
        /**
         * Accepted file types. Can be provided in form of file extensions, media type or one of:
         * * `audio/*`,
         * * `video/*`,
         * * `image/*`.
         *
         * @observable
         * @member {String} #acceptedType
         */
        this.set('acceptedType', undefined);
        /**
         * Indicates if multiple files can be selected. Defaults to `false`.
         *
         * @observable
         * @member {Boolean} #allowMultipleFiles
         */
        this.set('allowMultipleFiles', false);
        const bind = this.bindTemplate;
        this.setTemplate({
            tag: 'input',
            attributes: {
                class: [
                    'ck-hidden'
                ],
                type: 'file',
                tabindex: '-1',
                accept: bind.to('acceptedType'),
                multiple: bind.to('allowMultipleFiles')
            },
            on: {
                // Removing from code coverage since we cannot programmatically set input element files.
                change: bind.to(/* istanbul ignore next */ () => {
                    if (this.element && this.element.files && this.element.files.length) {
                        this.fire('done', this.element.files);
                    }
                    this.element.value = '';
                })
            }
        });
    }
    /**
     * Opens file dialog.
     */
    open() {
        this.element.click();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-upload/src/adapters/base64uploadadapter.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module upload/adapters/base64uploadadapter
 */
/* globals window */


/**
 * A plugin that converts images inserted into the editor into [Base64 strings](https://en.wikipedia.org/wiki/Base64)
 * in the {@glink installation/advanced/saving-data editor output}.
 *
 * This kind of image upload does not require server processing – images are stored with the rest of the text and
 * displayed by the web browser without additional requests.
 *
 * Check out the {@glink features/images/image-upload/image-upload comprehensive "Image upload overview"} to learn about
 * other ways to upload images into CKEditor 5.
 *
 * @extends module:core/plugin~Plugin
 */
class Base64UploadAdapter extends (/* unused pure expression or super */ null && (Plugin)) {
    /**
     * @inheritDoc
     */
    static get requires() {
        return [FileRepository];
    }
    /**
     * @inheritDoc
     */
    static get pluginName() {
        return 'Base64UploadAdapter';
    }
    /**
     * @inheritDoc
     */
    init() {
        this.editor.plugins.get(FileRepository).createUploadAdapter = loader => new base64uploadadapter_Adapter(loader);
    }
}
/**
 * The upload adapter that converts images inserted into the editor into Base64 strings.
 *
 * @private
 * @implements module:upload/filerepository~UploadAdapter
 */
class base64uploadadapter_Adapter {
    /**
     * Creates a new adapter instance.
     *
     * @param {module:upload/filerepository~FileLoader} loader
     */
    constructor(loader) {
        /**
         * `FileLoader` instance to use during the upload.
         *
         * @member {module:upload/filerepository~FileLoader} #loader
         */
        this.loader = loader;
    }
    /**
     * Starts the upload process.
     *
     * @see module:upload/filerepository~UploadAdapter#upload
     * @returns {Promise}
     */
    upload() {
        return new Promise((resolve, reject) => {
            const reader = this.reader = new window.FileReader();
            reader.addEventListener('load', () => {
                resolve({ default: reader.result });
            });
            reader.addEventListener('error', err => {
                reject(err);
            });
            reader.addEventListener('abort', () => {
                reject();
            });
            this.loader.file.then(file => {
                reader.readAsDataURL(file);
            });
        });
    }
    /**
     * Aborts the upload process.
     *
     * @see module:upload/filerepository~UploadAdapter#abort
     * @returns {Promise}
     */
    abort() {
        this.reader.abort();
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-upload/src/index.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 * @module upload
 */





;// CONCATENATED MODULE: ./node_modules/ckeditor5/src/upload.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */
/**
 *  @module ckeditor5/upload
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageupload/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageupload/utils
 */

/* global fetch, File */



/**
 * Creates a regular expression used to test for image files.
 *
 *		const imageType = createImageTypeRegExp( [ 'png', 'jpeg', 'svg+xml', 'vnd.microsoft.icon' ] );
 *
 *		console.log( 'is supported image', imageType.test( file.type ) );
 *
 * @param {Array.<String>} types
 * @returns {RegExp}
 */
function createImageTypeRegExp( types ) {
	// Sanitize the MIME type name which may include: "+", "-" or ".".
	const regExpSafeNames = types.map( type => type.replace( '+', '\\+' ) );

	return new RegExp( `^image\\/(${ regExpSafeNames.join( '|' ) })$` );
}

/**
 * Creates a promise that fetches the image local source (Base64 or blob) and resolves with a `File` object.
 *
 * @param {module:engine/view/element~Element} image Image whose source to fetch.
 * @returns {Promise.<File>} A promise which resolves when an image source is fetched and converted to a `File` instance.
 * It resolves with a `File` object. If there were any errors during file processing, the promise will be rejected.
 */
function fetchLocalImage( image ) {
	return new Promise( ( resolve, reject ) => {
		const imageSrc = image.getAttribute( 'src' );

		// Fetch works asynchronously and so does not block browser UI when processing data.
		fetch( imageSrc )
			.then( resource => resource.blob() )
			.then( blob => {
				const mimeType = getImageMimeType( blob, imageSrc );
				const ext = mimeType.replace( 'image/', '' );
				const filename = `image.${ ext }`;
				const file = new File( [ blob ], filename, { type: mimeType } );

				resolve( file );
			} )
			.catch( err => {
				// Fetch fails only, if it can't make a request due to a network failure or if anything prevented the request
				// from completing, i.e. the Content Security Policy rules. It is not possible to detect the exact cause of failure,
				// so we are just trying the fallback solution, if general TypeError is thrown.
				return err && err.name === 'TypeError' ?
					convertLocalImageOnCanvas( imageSrc ).then( resolve ).catch( reject ) :
					reject( err );
			} );
	} );
}

/**
 * Checks whether a given node is an image element with a local source (Base64 or blob).
 *
 * @param {module:image/imageutils~ImageUtils} imageUtils
 * @param {module:engine/view/node~Node} node The node to check.
 * @returns {Boolean}
 */
function isLocalImage( imageUtils, node ) {
	if ( !imageUtils.isInlineImageView( node ) || !node.getAttribute( 'src' ) ) {
		return false;
	}

	return node.getAttribute( 'src' ).match( /^data:image\/\w+;base64,/g ) ||
		node.getAttribute( 'src' ).match( /^blob:/g );
}

// Extracts an image type based on its blob representation or its source.
//
// @param {String} src Image `src` attribute value.
// @param {Blob} blob Image blob representation.
// @returns {String}
function getImageMimeType( blob, src ) {
	if ( blob.type ) {
		return blob.type;
	} else if ( src.match( /data:(image\/\w+);base64/ ) ) {
		return src.match( /data:(image\/\w+);base64/ )[ 1 ].toLowerCase();
	} else {
		// Fallback to 'jpeg' as common extension.
		return 'image/jpeg';
	}
}

// Creates a promise that converts the image local source (Base64 or blob) to a blob using canvas and resolves
// with a `File` object.
//
// @param {String} imageSrc Image `src` attribute value.
// @returns {Promise.<File>} A promise which resolves when an image source is converted to a `File` instance.
// It resolves with a `File` object. If there were any errors during file processing, the promise will be rejected.
function convertLocalImageOnCanvas( imageSrc ) {
	return getBlobFromCanvas( imageSrc ).then( blob => {
		const mimeType = getImageMimeType( blob, imageSrc );
		const ext = mimeType.replace( 'image/', '' );
		const filename = `image.${ ext }`;

		return new File( [ blob ], filename, { type: mimeType } );
	} );
}

// Creates a promise that resolves with a `Blob` object converted from the image source (Base64 or blob).
//
// @param {String} imageSrc Image `src` attribute value.
// @returns {Promise.<Blob>}
function getBlobFromCanvas( imageSrc ) {
	return new Promise( ( resolve, reject ) => {
		const image = dom_global.document.createElement( 'img' );

		image.addEventListener( 'load', () => {
			const canvas = dom_global.document.createElement( 'canvas' );

			canvas.width = image.width;
			canvas.height = image.height;

			const ctx = canvas.getContext( '2d' );

			ctx.drawImage( image, 0, 0 );

			canvas.toBlob( blob => blob ? resolve( blob ) : reject() );
		} );

		image.addEventListener( 'error', () => reject() );

		image.src = imageSrc;
	} );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageupload/imageuploadui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageupload/imageuploadui
 */





/**
 * The image upload button plugin.
 *
 * For a detailed overview, check the {@glink features/images/image-upload/image-upload Image upload feature} documentation.
 *
 * Adds the `'uploadImage'` button to the {@link module:ui/componentfactory~ComponentFactory UI component factory}
 * and also the `imageUpload` button as an alias for backward compatibility.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageUploadUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageUploadUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;
		const componentCreator = locale => {
			const view = new FileDialogButtonView( locale );
			const command = editor.commands.get( 'uploadImage' );
			const imageTypes = editor.config.get( 'image.upload.types' );
			const imageTypesRegExp = createImageTypeRegExp( imageTypes );

			view.set( {
				acceptedType: imageTypes.map( type => `image/${ type }` ).join( ',' ),
				allowMultipleFiles: true
			} );

			view.buttonView.set( {
				label: t( 'Insert image' ),
				icon: icons.image,
				tooltip: true
			} );

			view.buttonView.bind( 'isEnabled' ).to( command );

			view.on( 'done', ( evt, files ) => {
				const imagesToUpload = Array.from( files ).filter( file => imageTypesRegExp.test( file.type ) );

				if ( imagesToUpload.length ) {
					editor.execute( 'uploadImage', { file: imagesToUpload } );

					editor.editing.view.focus();
				}
			} );

			return view;
		};

		// Setup `uploadImage` button and add `imageUpload` button as an alias for backward compatibility.
		editor.ui.componentFactory.add( 'uploadImage', componentCreator );
		editor.ui.componentFactory.add( 'imageUpload', componentCreator );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadprogress.css
var imageuploadprogress = __webpack_require__(5870);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadprogress.css

            

var imageuploadprogress_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imageuploadprogress_options.insert = "head";
imageuploadprogress_options.singleton = true;

var imageuploadprogress_update = injectStylesIntoStyleTag_default()(imageuploadprogress/* default */.Z, imageuploadprogress_options);



/* harmony default export */ const theme_imageuploadprogress = (imageuploadprogress/* default.locals */.Z.locals || {});
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadicon.css
var imageuploadicon = __webpack_require__(9899);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadicon.css

            

var imageuploadicon_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imageuploadicon_options.insert = "head";
imageuploadicon_options.singleton = true;

var imageuploadicon_update = injectStylesIntoStyleTag_default()(imageuploadicon/* default */.Z, imageuploadicon_options);



/* harmony default export */ const theme_imageuploadicon = (imageuploadicon/* default.locals */.Z.locals || {});
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadloader.css
var imageuploadloader = __webpack_require__(9825);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imageuploadloader.css

            

var imageuploadloader_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imageuploadloader_options.insert = "head";
imageuploadloader_options.singleton = true;

var imageuploadloader_update = injectStylesIntoStyleTag_default()(imageuploadloader/* default */.Z, imageuploadloader_options);



/* harmony default export */ const theme_imageuploadloader = (imageuploadloader/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageupload/imageuploadprogress.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageupload/imageuploadprogress
 */

/* globals setTimeout */








/**
 * The image upload progress plugin.
 * It shows a placeholder when the image is read from the disk and a progress bar while the image is uploading.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageUploadProgress extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageUploadProgress';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * The image placeholder that is displayed before real image data can be accessed.
		 *
		 * For the record, this image is a 1x1 px GIF with an aspect ratio set by CSS.
		 *
		 * @protected
		 * @member {String} #placeholder
		 */
		this.placeholder = 'data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Upload status change - update image's view according to that status.
		if ( editor.plugins.has( 'ImageBlockEditing' ) ) {
			editor.editing.downcastDispatcher.on( 'attribute:uploadStatus:imageBlock', ( ...args ) => this.uploadStatusChange( ...args ) );
		}

		if ( editor.plugins.has( 'ImageInlineEditing' ) ) {
			editor.editing.downcastDispatcher.on( 'attribute:uploadStatus:imageInline', ( ...args ) => this.uploadStatusChange( ...args ) );
		}
	}

	/**
	 * This method is called each time the image `uploadStatus` attribute is changed.
	 *
	 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
	 * @param {Object} data Additional information about the change.
	 * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
	 */
	uploadStatusChange( evt, data, conversionApi ) {
		const editor = this.editor;
		const modelImage = data.item;
		const uploadId = modelImage.getAttribute( 'uploadId' );

		if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
			return;
		}

		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const fileRepository = editor.plugins.get( filerepository_FileRepository );
		const status = uploadId ? data.attributeNewValue : null;
		const placeholder = this.placeholder;
		const viewFigure = editor.editing.mapper.toViewElement( modelImage );
		const viewWriter = conversionApi.writer;

		if ( status == 'reading' ) {
			// Start "appearing" effect and show placeholder with infinite progress bar on the top
			// while image is read from disk.
			_startAppearEffect( viewFigure, viewWriter );
			_showPlaceholder( imageUtils, placeholder, viewFigure, viewWriter );

			return;
		}

		// Show progress bar on the top of the image when image is uploading.
		if ( status == 'uploading' ) {
			const loader = fileRepository.loaders.get( uploadId );

			// Start appear effect if needed - see https://github.com/ckeditor/ckeditor5-image/issues/191.
			_startAppearEffect( viewFigure, viewWriter );

			if ( !loader ) {
				// There is no loader associated with uploadId - this means that image came from external changes.
				// In such cases we still want to show the placeholder until image is fully uploaded.
				// Show placeholder if needed - see https://github.com/ckeditor/ckeditor5-image/issues/191.
				_showPlaceholder( imageUtils, placeholder, viewFigure, viewWriter );
			} else {
				// Hide placeholder and initialize progress bar showing upload progress.
				_hidePlaceholder( viewFigure, viewWriter );
				_showProgressBar( viewFigure, viewWriter, loader, editor.editing.view );
				_displayLocalImage( imageUtils, viewFigure, viewWriter, loader );
			}

			return;
		}

		if ( status == 'complete' && fileRepository.loaders.get( uploadId ) ) {
			_showCompleteIcon( viewFigure, viewWriter, editor.editing.view );
		}

		// Clean up.
		_hideProgressBar( viewFigure, viewWriter );
		_hidePlaceholder( viewFigure, viewWriter );
		_stopAppearEffect( viewFigure, viewWriter );
	}
}

// Adds ck-appear class to the image figure if one is not already applied.
//
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function _startAppearEffect( viewFigure, writer ) {
	if ( !viewFigure.hasClass( 'ck-appear' ) ) {
		writer.addClass( 'ck-appear', viewFigure );
	}
}

// Removes ck-appear class to the image figure if one is not already removed.
//
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function _stopAppearEffect( viewFigure, writer ) {
	writer.removeClass( 'ck-appear', viewFigure );
}

// Shows placeholder together with infinite progress bar on given image figure.
//
// @param {module:image/imageutils~ImageUtils} imageUtils
// @param {String} Data-uri with a svg placeholder.
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function _showPlaceholder( imageUtils, placeholder, viewFigure, writer ) {
	if ( !viewFigure.hasClass( 'ck-image-upload-placeholder' ) ) {
		writer.addClass( 'ck-image-upload-placeholder', viewFigure );
	}

	const viewImg = imageUtils.findViewImgElement( viewFigure );

	if ( viewImg.getAttribute( 'src' ) !== placeholder ) {
		writer.setAttribute( 'src', placeholder, viewImg );
	}

	if ( !_getUIElement( viewFigure, 'placeholder' ) ) {
		writer.insert( writer.createPositionAfter( viewImg ), _createPlaceholder( writer ) );
	}
}

// Removes placeholder together with infinite progress bar on given image figure.
//
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function _hidePlaceholder( viewFigure, writer ) {
	if ( viewFigure.hasClass( 'ck-image-upload-placeholder' ) ) {
		writer.removeClass( 'ck-image-upload-placeholder', viewFigure );
	}

	_removeUIElement( viewFigure, writer, 'placeholder' );
}

// Shows progress bar displaying upload progress.
// Attaches it to the file loader to update when upload percentace is changed.
//
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {module:upload/filerepository~FileLoader} loader
// @param {module:engine/view/view~View} view
function _showProgressBar( viewFigure, writer, loader, view ) {
	const progressBar = _createProgressBar( writer );
	writer.insert( writer.createPositionAt( viewFigure, 'end' ), progressBar );

	// Update progress bar width when uploadedPercent is changed.
	loader.on( 'change:uploadedPercent', ( evt, name, value ) => {
		view.change( writer => {
			writer.setStyle( 'width', value + '%', progressBar );
		} );
	} );
}

// Hides upload progress bar.
//
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
function _hideProgressBar( viewFigure, writer ) {
	_removeUIElement( viewFigure, writer, 'progressBar' );
}

// Shows complete icon and hides after a certain amount of time.
//
// @param {module:engine/view/containerelement~ContainerElement} viewFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {module:engine/view/view~View} view
function _showCompleteIcon( viewFigure, writer, view ) {
	const completeIcon = writer.createUIElement( 'div', { class: 'ck-image-upload-complete-icon' } );

	writer.insert( writer.createPositionAt( viewFigure, 'end' ), completeIcon );

	setTimeout( () => {
		view.change( writer => writer.remove( writer.createRangeOn( completeIcon ) ) );
	}, 3000 );
}

// Create progress bar element using {@link module:engine/view/uielement~UIElement}.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @returns {module:engine/view/uielement~UIElement}
function _createProgressBar( writer ) {
	const progressBar = writer.createUIElement( 'div', { class: 'ck-progress-bar' } );

	writer.setCustomProperty( 'progressBar', true, progressBar );

	return progressBar;
}

// Create placeholder element using {@link module:engine/view/uielement~UIElement}.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @returns {module:engine/view/uielement~UIElement}
function _createPlaceholder( writer ) {
	const placeholder = writer.createUIElement( 'div', { class: 'ck-upload-placeholder-loader' } );

	writer.setCustomProperty( 'placeholder', true, placeholder );

	return placeholder;
}

// Returns {@link module:engine/view/uielement~UIElement} of given unique property from image figure element.
// Returns `undefined` if element is not found.
//
// @private
// @param {module:engine/view/element~Element} imageFigure
// @param {String} uniqueProperty
// @returns {module:engine/view/uielement~UIElement|undefined}
function _getUIElement( imageFigure, uniqueProperty ) {
	for ( const child of imageFigure.getChildren() ) {
		if ( child.getCustomProperty( uniqueProperty ) ) {
			return child;
		}
	}
}

// Removes {@link module:engine/view/uielement~UIElement} of given unique property from image figure element.
//
// @private
// @param {module:engine/view/element~Element} imageFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {String} uniqueProperty
function _removeUIElement( viewFigure, writer, uniqueProperty ) {
	const element = _getUIElement( viewFigure, uniqueProperty );

	if ( element ) {
		writer.remove( writer.createRangeOn( element ) );
	}
}

// Displays local data from file loader.
//
// @param {module:image/imageutils~ImageUtils} imageUtils
// @param {module:engine/view/element~Element} imageFigure
// @param {module:engine/view/downcastwriter~DowncastWriter} writer
// @param {module:upload/filerepository~FileLoader} loader
function _displayLocalImage( imageUtils, viewFigure, writer, loader ) {
	if ( loader.data ) {
		const viewImg = imageUtils.findViewImgElement( viewFigure );

		writer.setAttribute( 'src', loader.data, viewImg );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageupload/uploadimagecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */





/**
 * @module image/imageupload/uploadimagecommand
 */

/**
 * The upload image command.
 *
 * The command is registered by the {@link module:image/imageupload/imageuploadediting~ImageUploadEditing} plugin as `uploadImage`
 * and it is also available via aliased `imageUpload` name.
 *
 * In order to upload an image at the current selection position
 * (according to the {@link module:widget/utils~findOptimalInsertionRange} algorithm),
 * execute the command and pass the native image file instance:
 *
 *		this.listenTo( editor.editing.view.document, 'clipboardInput', ( evt, data ) => {
 *			// Assuming that only images were pasted:
 *			const images = Array.from( data.dataTransfer.files );
 *
 *			// Upload the first image:
 *			editor.execute( 'uploadImage', { file: images[ 0 ] } );
 *		} );
 *
 * It is also possible to insert multiple images at once:
 *
 *		editor.execute( 'uploadImage', {
 *			file: [
 *				file1,
 *				file2
 *			]
 *		} );
 *
 * @extends module:core/command~Command
 */
class UploadImageCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const selectedElement = editor.model.document.selection.getSelectedElement();

		// TODO: This needs refactoring.
		this.isEnabled = imageUtils.isImageAllowed() || imageUtils.isImage( selectedElement );
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} options Options for the executed command.
	 * @param {File|Array.<File>} options.file The image file or an array of image files to upload.
	 */
	execute( options ) {
		const files = toArray( options.file );
		const selection = this.editor.model.document.selection;
		const imageUtils = this.editor.plugins.get( 'ImageUtils' );

		// In case of multiple files, each file (starting from the 2nd) will be inserted at a position that
		// follows the previous one. That will move the selection and, to stay on the safe side and make sure
		// all images inherit the same selection attributes, they are collected beforehand.
		//
		// Applying these attributes ensures, for instance, that inserting an (inline) image into a link does
		// not split that link but preserves its continuity.
		//
		// Note: Selection attributes that do not make sense for images will be filtered out by insertImage() anyway.
		const selectionAttributes = Object.fromEntries( selection.getAttributes() );

		files.forEach( ( file, index ) => {
			const selectedElement = selection.getSelectedElement();

			// Inserting of an inline image replace the selected element and make a selection on the inserted image.
			// Therefore inserting multiple inline images requires creating position after each element.
			if ( index && selectedElement && imageUtils.isImage( selectedElement ) ) {
				const position = this.editor.model.createPositionAfter( selectedElement );

				this._uploadImage( file, selectionAttributes, position );
			} else {
				this._uploadImage( file, selectionAttributes );
			}
		} );
	}

	/**
	 * Handles uploading single file.
	 *
	 * @private
	 * @param {File} file
	 * @param {Object} attributes
	 * @param {module:engine/model/position~Position} position
	 */
	_uploadImage( file, attributes, position ) {
		const editor = this.editor;
		const fileRepository = editor.plugins.get( filerepository_FileRepository );
		const loader = fileRepository.createLoader( file );
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		// Do not throw when upload adapter is not set. FileRepository will log an error anyway.
		if ( !loader ) {
			return;
		}

		imageUtils.insertImage( { ...attributes, uploadId: loader.id }, position );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageupload/imageuploadediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageupload/imageuploadediting
 */















/**
 * The editing part of the image upload feature. It registers the `'uploadImage'` command
 * and the `imageUpload` command as an aliased name.
 *
 * When an image is uploaded, it fires the {@link ~ImageUploadEditing#event:uploadComplete `uploadComplete`} event
 * that allows adding custom attributes to the {@link module:engine/model/element~Element image element}.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageUploadEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ filerepository_FileRepository, Notification, ClipboardPipeline, ImageUtils ];
	}

	static get pluginName() {
		return 'ImageUploadEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'image', {
			upload: {
				types: [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ]
			}
		} );

		/**
		 * An internal mapping of {@link module:upload/filerepository~FileLoader#id file loader UIDs} and
		 * model elements during the upload.
		 *
		 * Model element of the uploaded image can change, for instance, when {@link module:image/image/imagetypecommand~ImageTypeCommand}
		 * is executed as a result of adding caption or changing image style. As a result, the upload logic must keep track of the model
		 * element (reference) and resolve the upload for the correct model element (instead of the one that landed in the `$graveyard`
		 * after image type changed).
		 *
		 * @private
		 * @readonly
		 * @member {Map.<String,module:engine/model/element~Element>}
		 */
		this._uploadImageElements = new Map();
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const doc = editor.model.document;
		const conversion = editor.conversion;
		const fileRepository = editor.plugins.get( filerepository_FileRepository );
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const imageTypes = createImageTypeRegExp( editor.config.get( 'image.upload.types' ) );
		const uploadImageCommand = new UploadImageCommand( editor );

		// Register `uploadImage` command and add `imageUpload` command as an alias for backward compatibility.
		editor.commands.add( 'uploadImage', uploadImageCommand );
		editor.commands.add( 'imageUpload', uploadImageCommand );

		// Register upcast converter for uploadId.
		conversion.for( 'upcast' )
			.attributeToAttribute( {
				view: {
					name: 'img',
					key: 'uploadId'
				},
				model: 'uploadId'
			} );

		// Handle pasted images.
		// For every image file, a new file loader is created and a placeholder image is
		// inserted into the content. Then, those images are uploaded once they appear in the model
		// (see Document#change listener below).
		this.listenTo( editor.editing.view.document, 'clipboardInput', ( evt, data ) => {
			// Skip if non empty HTML data is included.
			// https://github.com/ckeditor/ckeditor5-upload/issues/68
			if ( isHtmlIncluded( data.dataTransfer ) ) {
				return;
			}

			const images = Array.from( data.dataTransfer.files ).filter( file => {
				// See https://github.com/ckeditor/ckeditor5-image/pull/254.
				if ( !file ) {
					return false;
				}

				return imageTypes.test( file.type );
			} );

			if ( !images.length ) {
				return;
			}

			evt.stop();

			editor.model.change( writer => {
				// Set selection to paste target.
				if ( data.targetRanges ) {
					writer.setSelection( data.targetRanges.map( viewRange => editor.editing.mapper.toModelRange( viewRange ) ) );
				}

				// Upload images after the selection has changed in order to ensure the command's state is refreshed.
				editor.model.enqueueChange( () => {
					editor.execute( 'uploadImage', { file: images } );
				} );
			} );
		} );

		// Handle HTML pasted with images with base64 or blob sources.
		// For every image file, a new file loader is created and a placeholder image is
		// inserted into the content. Then, those images are uploaded once they appear in the model
		// (see Document#change listener below).
		this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', ( evt, data ) => {
			const fetchableImages = Array.from( editor.editing.view.createRangeIn( data.content ) )
				.filter( value => isLocalImage( imageUtils, value.item ) && !value.item.getAttribute( 'uploadProcessed' ) )
				.map( value => { return { promise: fetchLocalImage( value.item ), imageElement: value.item }; } );

			if ( !fetchableImages.length ) {
				return;
			}

			const writer = new UpcastWriter( editor.editing.view.document );

			for ( const fetchableImage of fetchableImages ) {
				// Set attribute marking that the image was processed already.
				writer.setAttribute( 'uploadProcessed', true, fetchableImage.imageElement );

				const loader = fileRepository.createLoader( fetchableImage.promise );

				if ( loader ) {
					writer.setAttribute( 'src', '', fetchableImage.imageElement );
					writer.setAttribute( 'uploadId', loader.id, fetchableImage.imageElement );
				}
			}
		} );

		// Prevents from the browser redirecting to the dropped image.
		editor.editing.view.document.on( 'dragover', ( evt, data ) => {
			data.preventDefault();
		} );

		// Upload placeholder images that appeared in the model.
		doc.on( 'change', () => {
			// Note: Reversing changes to start with insertions and only then handle removals. If it was the other way around,
			// loaders for **all** images that land in the $graveyard would abort while in fact only those that were **not** replaced
			// by other images should be aborted.
			const changes = doc.differ.getChanges( { includeChangesInGraveyard: true } ).reverse();
			const insertedImagesIds = new Set();

			for ( const entry of changes ) {
				if ( entry.type == 'insert' && entry.name != '$text' ) {
					const item = entry.position.nodeAfter;
					const isInsertedInGraveyard = entry.position.root.rootName == '$graveyard';

					for ( const imageElement of getImagesFromChangeItem( editor, item ) ) {
						// Check if the image element still has upload id.
						const uploadId = imageElement.getAttribute( 'uploadId' );

						if ( !uploadId ) {
							continue;
						}

						// Check if the image is loaded on this client.
						const loader = fileRepository.loaders.get( uploadId );

						if ( !loader ) {
							continue;
						}

						if ( isInsertedInGraveyard ) {
							// If the image was inserted to the graveyard for good (**not** replaced by another image),
							// only then abort the loading process.
							if ( !insertedImagesIds.has( uploadId ) ) {
								loader.abort();
							}
						} else {
							// Remember the upload id of the inserted image. If it acted as a replacement for another
							// image (which landed in the $graveyard), the related loader will not be aborted because
							// this is still the same image upload.
							insertedImagesIds.add( uploadId );

							// Keep the mapping between the upload ID and the image model element so the upload
							// can later resolve in the context of the correct model element. The model element could
							// change for the same upload if one image was replaced by another (e.g. image type was changed),
							// so this may also replace an existing mapping.
							this._uploadImageElements.set( uploadId, imageElement );

							if ( loader.status == 'idle' ) {
								// If the image was inserted into content and has not been loaded yet, start loading it.
								this._readAndUpload( loader );
							}
						}
					}
				}
			}
		} );

		// Set the default handler for feeding the image element with `src` and `srcset` attributes.
		this.on( 'uploadComplete', ( evt, { imageElement, data } ) => {
			const urls = data.urls ? data.urls : data;

			this.editor.model.change( writer => {
				writer.setAttribute( 'src', urls.default, imageElement );
				this._parseAndSetSrcsetAttributeOnImage( urls, imageElement, writer );
			} );
		}, { priority: 'low' } );
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const schema = this.editor.model.schema;

		// Setup schema to allow uploadId and uploadStatus for images.
		// Wait for ImageBlockEditing or ImageInlineEditing to register their elements first,
		// that's why doing this in afterInit() instead of init().
		if ( this.editor.plugins.has( 'ImageBlockEditing' ) ) {
			schema.extend( 'imageBlock', {
				allowAttributes: [ 'uploadId', 'uploadStatus' ]
			} );
		}

		if ( this.editor.plugins.has( 'ImageInlineEditing' ) ) {
			schema.extend( 'imageInline', {
				allowAttributes: [ 'uploadId', 'uploadStatus' ]
			} );
		}
	}

	/**
	 * Reads and uploads an image.
	 *
	 * The image is read from the disk and as a Base64-encoded string it is set temporarily to
	 * `image[src]`. When the image is successfully uploaded, the temporary data is replaced with the target
	 * image's URL (the URL to the uploaded image on the server).
	 *
	 * @protected
	 * @param {module:upload/filerepository~FileLoader} loader
	 * @returns {Promise}
	 */
	_readAndUpload( loader ) {
		const editor = this.editor;
		const model = editor.model;
		const t = editor.locale.t;
		const fileRepository = editor.plugins.get( filerepository_FileRepository );
		const notification = editor.plugins.get( Notification );
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const imageUploadElements = this._uploadImageElements;

		model.enqueueChange( { isUndoable: false }, writer => {
			writer.setAttribute( 'uploadStatus', 'reading', imageUploadElements.get( loader.id ) );
		} );

		return loader.read()
			.then( () => {
				const promise = loader.upload();
				const imageElement = imageUploadElements.get( loader.id );

				// Force re–paint in Safari. Without it, the image will display with a wrong size.
				// https://github.com/ckeditor/ckeditor5/issues/1975
				/* istanbul ignore next */
				if ( src_env.isSafari ) {
					const viewFigure = editor.editing.mapper.toViewElement( imageElement );
					const viewImg = imageUtils.findViewImgElement( viewFigure );

					editor.editing.view.once( 'render', () => {
						// Early returns just to be safe. There might be some code ran
						// in between the outer scope and this callback.
						if ( !viewImg.parent ) {
							return;
						}

						const domFigure = editor.editing.view.domConverter.mapViewToDom( viewImg.parent );

						if ( !domFigure ) {
							return;
						}

						const originalDisplay = domFigure.style.display;

						domFigure.style.display = 'none';

						// Make sure this line will never be removed during minification for having "no effect".
						domFigure._ckHack = domFigure.offsetHeight;

						domFigure.style.display = originalDisplay;
					} );
				}

				model.enqueueChange( { isUndoable: false }, writer => {
					writer.setAttribute( 'uploadStatus', 'uploading', imageElement );
				} );

				return promise;
			} )
			.then( data => {
				model.enqueueChange( { isUndoable: false }, writer => {
					const imageElement = imageUploadElements.get( loader.id );

					writer.setAttribute( 'uploadStatus', 'complete', imageElement );

					/**
					 * An event fired when an image is uploaded. You can hook into this event to provide
					 * custom attributes to the {@link module:engine/model/element~Element image element} based on the data from
					 * the server.
					 *
					 * 		const imageUploadEditing = editor.plugins.get( 'ImageUploadEditing' );
					 *
					 * 		imageUploadEditing.on( 'uploadComplete', ( evt, { data, imageElement } ) => {
					 * 			editor.model.change( writer => {
					 * 				writer.setAttribute( 'someAttribute', 'foo', imageElement );
					 * 			} );
					 * 		} );
					 *
					 * You can also stop the default handler that sets the `src` and `srcset` attributes
					 * if you want to provide custom values for these attributes.
					 *
					 * 		imageUploadEditing.on( 'uploadComplete', ( evt, { data, imageElement } ) => {
					 * 			evt.stop();
					 * 		} );
					 *
					 * **Note**: This event is fired by the {@link module:image/imageupload/imageuploadediting~ImageUploadEditing} plugin.
					 *
					 * @event uploadComplete
					 * @param {Object} data The `uploadComplete` event data.
					 * @param {Object} data.data The data coming from the upload adapter.
					 * @param {module:engine/model/element~Element} data.imageElement The
					 * model {@link module:engine/model/element~Element image element} that can be customized.
					 */
					this.fire( 'uploadComplete', { data, imageElement } );
				} );

				clean();
			} )
			.catch( error => {
				// If status is not 'error' nor 'aborted' - throw error because it means that something else went wrong,
				// it might be generic error and it would be real pain to find what is going on.
				if ( loader.status !== 'error' && loader.status !== 'aborted' ) {
					throw error;
				}

				// Might be 'aborted'.
				if ( loader.status == 'error' && error ) {
					notification.showWarning( error, {
						title: t( 'Upload failed' ),
						namespace: 'upload'
					} );
				}

				// Permanently remove image from insertion batch.
				model.enqueueChange( { isUndoable: false }, writer => {
					writer.remove( imageUploadElements.get( loader.id ) );
				} );

				clean();
			} );

		function clean() {
			model.enqueueChange( { isUndoable: false }, writer => {
				const imageElement = imageUploadElements.get( loader.id );

				writer.removeAttribute( 'uploadId', imageElement );
				writer.removeAttribute( 'uploadStatus', imageElement );

				imageUploadElements.delete( loader.id );
			} );

			fileRepository.destroyLoader( loader );
		}
	}

	/**
	 * Creates the `srcset` attribute based on a given file upload response and sets it as an attribute to a specific image element.
	 *
	 * @protected
	 * @param {Object} data Data object from which `srcset` will be created.
	 * @param {module:engine/model/element~Element} image The image element on which the `srcset` attribute will be set.
	 * @param {module:engine/model/writer~Writer} writer
	 */
	_parseAndSetSrcsetAttributeOnImage( data, image, writer ) {
		// Srcset attribute for responsive images support.
		let maxWidth = 0;

		const srcsetAttribute = Object.keys( data )
			// Filter out keys that are not integers.
			.filter( key => {
				const width = parseInt( key, 10 );

				if ( !isNaN( width ) ) {
					maxWidth = Math.max( maxWidth, width );

					return true;
				}
			} )

			// Convert each key to srcset entry.
			.map( key => `${ data[ key ] } ${ key }w` )

			// Join all entries.
			.join( ', ' );

		if ( srcsetAttribute != '' ) {
			writer.setAttribute( 'srcset', {
				data: srcsetAttribute,
				width: maxWidth
			}, image );
		}
	}
}

// Returns `true` if non-empty `text/html` is included in the data transfer.
//
// @param {module:engine/view/datatransfer~DataTransfer} dataTransfer
// @returns {Boolean}
function isHtmlIncluded( dataTransfer ) {
	return Array.from( dataTransfer.types ).includes( 'text/html' ) && dataTransfer.getData( 'text/html' ) !== '';
}

function getImagesFromChangeItem( editor, item ) {
	const imageUtils = editor.plugins.get( 'ImageUtils' );

	return Array.from( editor.model.createRangeOn( item ) )
		.filter( value => imageUtils.isImage( value.item ) )
		.map( value => value.item );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageupload.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageupload
 */






/**
 * The image upload plugin.
 *
 * For a detailed overview, check the {@glink features/images/image-upload/image-upload image upload feature} documentation.
 *
 * This plugin does not do anything directly, but it loads a set of specific plugins to enable image uploading:
 *
 * * {@link module:image/imageupload/imageuploadediting~ImageUploadEditing},
 * * {@link module:image/imageupload/imageuploadui~ImageUploadUI},
 * * {@link module:image/imageupload/imageuploadprogress~ImageUploadProgress}.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageUpload extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageUpload';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUploadEditing, ImageUploadUI, ImageUploadProgress ];
	}
}

/**
 * The image upload configuration.
 *
 * @member {module:image/imageupload~ImageUploadConfig} module:image/image~ImageConfig#upload
 */

/**
 * The configuration of the image upload feature. Used by the image upload feature in the `@ckeditor/ckeditor5-image` package.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				image: {
 * 					upload:  ... // Image upload feature options.
 * 				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface module:image/imageupload~ImageUploadConfig
 */

/**
 * The list of accepted image types.
 *
 * The accepted types of images can be customized to allow only certain types of images:
 *
 *		// Allow only JPEG and PNG images:
 *		const imageUploadConfig = {
 *			types: [ 'png', 'jpeg' ]
 *		};
 *
 * The type string should match [one of the sub-types](https://www.iana.org/assignments/media-types/media-types.xhtml#image)
 * of the image MIME type. For example, for the `image/jpeg` MIME type, add `'jpeg'` to your image upload configuration.
 *
 * **Note:** This setting only restricts some image types to be selected and uploaded through the CKEditor UI and commands. Image type
 * recognition and filtering should also be implemented on the server which accepts image uploads.
 *
 * @member {Array.<String>} module:image/imageupload~ImageUploadConfig#types
 * @default [ 'jpeg', 'png', 'gif', 'bmp', 'webp', 'tiff' ]
 */

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imageinsertformrowview.css
var imageinsertformrowview = __webpack_require__(5150);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imageinsertformrowview.css

            

var imageinsertformrowview_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imageinsertformrowview_options.insert = "head";
imageinsertformrowview_options.singleton = true;

var imageinsertformrowview_update = injectStylesIntoStyleTag_default()(imageinsertformrowview/* default */.Z, imageinsertformrowview_options);



/* harmony default export */ const theme_imageinsertformrowview = (imageinsertformrowview/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageinsert/ui/imageinsertformrowview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageinsert/ui/imageinsertformrowview
 */





/**
 * The class representing a single row in a complex form,
 * used by {@link module:image/imageinsert/ui/imageinsertpanelview~ImageInsertPanelView}.
 *
 * **Note**: For now this class is private. When more use cases appear (beyond `ckeditor5-table` and `ckeditor5-image`),
 * it will become a component in `ckeditor5-ui`.
 *
 * @private
 * @extends module:ui/view~View
 */
class ImageUploadFormRowView extends src_view_View {
	/**
	 * Creates an instance of the form row class.
	 *
	 * @param {module:utils/locale~Locale} locale The locale instance.
	 * @param {Object} options
	 * @param {Array.<module:ui/view~View>} [options.children]
	 * @param {String} [options.class]
	 * @param {module:ui/view~View} [options.labelView] When passed, the row gets the `group` and `aria-labelledby`
	 * DOM attributes and gets described by the label.
	 */
	constructor( locale, options = {} ) {
		super( locale );

		const bind = this.bindTemplate;

		/**
		 * An additional CSS class added to the {@link #element}.
		 *
		 * @observable
		 * @member {String} #class
		 */
		this.set( 'class', options.class || null );

		/**
		 * A collection of row items (buttons, dropdowns, etc.).
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.children = this.createCollection();

		if ( options.children ) {
			options.children.forEach( child => this.children.add( child ) );
		}

		/**
		 * The role property reflected by the `role` DOM attribute of the {@link #element}.
		 *
		 * **Note**: Used only when a `labelView` is passed to constructor `options`.
		 *
		 * @private
		 * @observable
		 * @member {String} #role
		 */
		this.set( '_role', null );

		/**
		 * The ARIA property reflected by the `aria-labelledby` DOM attribute of the {@link #element}.
		 *
		 * **Note**: Used only when a `labelView` is passed to constructor `options`.
		 *
		 * @private
		 * @observable
		 * @member {String} #ariaLabelledBy
		 */
		this.set( '_ariaLabelledBy', null );

		if ( options.labelView ) {
			this.set( {
				_role: 'group',
				_ariaLabelledBy: options.labelView.id
			} );
		}

		this.setTemplate( {
			tag: 'div',
			attributes: {
				class: [
					'ck',
					'ck-form__row',
					bind.to( 'class' )
				],
				role: bind.to( '_role' ),
				'aria-labelledby': bind.to( '_ariaLabelledBy' )
			},
			children: this.children
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imageinsert.css
var imageinsert = __webpack_require__(9292);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imageinsert.css

            

var imageinsert_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imageinsert_options.insert = "head";
imageinsert_options.singleton = true;

var imageinsert_update = injectStylesIntoStyleTag_default()(imageinsert/* default */.Z, imageinsert_options);



/* harmony default export */ const theme_imageinsert = (imageinsert/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageinsert/ui/imageinsertpanelview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageinsert/ui/imageinsertpanelview
 */









/**
 * The insert an image via URL view controller class.
 *
 * See {@link module:image/imageinsert/ui/imageinsertpanelview~ImageInsertPanelView}.
 *
 * @extends module:ui/view~View
 */
class ImageInsertPanelView extends src_view_View {
	/**
	 * Creates a view for the dropdown panel of {@link module:image/imageinsert/imageinsertui~ImageInsertUI}.
	 *
	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
	 * @param {Object} [integrations] An integrations object that contains
	 * components (or tokens for components) to be shown in the panel view.
	 */
	constructor( locale, integrations ) {
		super( locale );

		const { insertButtonView, cancelButtonView } = this._createActionButtons( locale );

		/**
		 * The "insert/update" button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.insertButtonView = insertButtonView;

		/**
		 * The "cancel" button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.cancelButtonView = cancelButtonView;

		/**
		 * The value of the URL input.
		 *
		 * @member {String} #imageURLInputValue
		 * @observable
		 */
		this.set( 'imageURLInputValue', '' );

		/**
		 * Tracks information about DOM focus in the form.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * A collection of views that can be focused in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate form fields backwards using the Shift + Tab keystroke.
				focusPrevious: 'shift + tab',

				// Navigate form fields forwards using the Tab key.
				focusNext: 'tab'
			}
		} );

		/**
		 * A collection of the defined integrations for inserting the images.
		 *
		 * @private
		 * @member {module:utils/collection~Collection}
		 */
		this.set( '_integrations', new Collection() );

		if ( integrations ) {
			for ( const [ integration, integrationView ] of Object.entries( integrations ) ) {
				if ( integration === 'insertImageViaUrl' ) {
					integrationView.fieldView.bind( 'value' ).to( this, 'imageURLInputValue', value => value || '' );

					integrationView.fieldView.on( 'input', () => {
						this.imageURLInputValue = integrationView.fieldView.element.value.trim();
					} );
				}

				integrationView.name = integration;

				this._integrations.add( integrationView );
			}
		}

		this.setTemplate( {
			tag: 'form',

			attributes: {
				class: [
					'ck',
					'ck-image-insert-form'
				],

				tabindex: '-1'
			},

			children: [
				...this._integrations,
				new ImageUploadFormRowView( locale, {
					children: [
						this.insertButtonView,
						this.cancelButtonView
					],
					class: 'ck-image-insert-form__action-row'
				} )
			]
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		submitHandler( {
			view: this
		} );

		const childViews = [
			...this._integrations,
			this.insertButtonView,
			this.cancelButtonView
		];

		childViews.forEach( v => {
			// Register the view as focusable.
			this._focusables.add( v );

			// Register the view in the focus tracker.
			this.focusTracker.add( v.element );
		} );

		// Start listening for the keystrokes coming from #element.
		this.keystrokes.listenTo( this.element );

		const stopPropagation = data => data.stopPropagation();

		// Since the form is in the dropdown panel which is a child of the toolbar, the toolbar's
		// keystroke handler would take over the key management in the URL input. We need to prevent
		// this ASAP. Otherwise, the basic caret movement using the arrow keys will be impossible.
		this.keystrokes.set( 'arrowright', stopPropagation );
		this.keystrokes.set( 'arrowleft', stopPropagation );
		this.keystrokes.set( 'arrowup', stopPropagation );
		this.keystrokes.set( 'arrowdown', stopPropagation );

		// Intercept the "selectstart" event, which is blocked by default because of the default behavior
		// of the DropdownView#panelView.
		// TODO: blocking "selectstart" in the #panelView should be configurable per–drop–down instance.
		this.listenTo( childViews[ 0 ].element, 'selectstart', ( evt, domEvt ) => {
			domEvt.stopPropagation();
		}, { priority: 'high' } );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Returns a view of the integration.
	 *
	 * @param {String} name The name of the integration.
	 * @returns {module:ui/view~View}
	 */
	getIntegration( name ) {
		return this._integrations.find( integration => integration.name === name );
	}

	/**
	 * Creates the following form controls:
	 *
	 * * {@link #insertButtonView},
	 * * {@link #cancelButtonView}.
	 *
	 * @param {module:utils/locale~Locale} locale The localization services instance.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createActionButtons( locale ) {
		const t = locale.t;
		const insertButtonView = new buttonview_ButtonView( locale );
		const cancelButtonView = new buttonview_ButtonView( locale );

		insertButtonView.set( {
			label: t( 'Insert' ),
			icon: icons.check,
			class: 'ck-button-save',
			type: 'submit',
			withText: true,
			isEnabled: this.imageURLInputValue
		} );

		cancelButtonView.set( {
			label: t( 'Cancel' ),
			icon: icons.cancel,
			class: 'ck-button-cancel',
			withText: true
		} );

		insertButtonView.bind( 'isEnabled' ).to( this, 'imageURLInputValue', value => !!value );
		insertButtonView.delegate( 'execute' ).to( this, 'submit' );
		cancelButtonView.delegate( 'execute' ).to( this, 'cancel' );

		return { insertButtonView, cancelButtonView };
	}

	/**
	 * Focuses the first {@link #_focusables focusable} in the form.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}
}

/**
 * Fired when the form view is submitted (when one of the children triggered the submit event),
 * e.g. by a click on {@link #insertButtonView}.
 *
 * @event submit
 */

/**
 * Fired when the form view is canceled, e.g. by a click on {@link #cancelButtonView}.
 *
 * @event cancel
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageinsert/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageinsert/utils
 */



/**
 * Creates integrations object that will be passed to the
 * {@link module:image/imageinsert/ui/imageinsertpanelview~ImageInsertPanelView}.
 *
 * @param {module:core/editor/editor~Editor} editor Editor instance.
 *
 * @returns {Object.<String, module:ui/view~View>} Integrations object.
 */
function prepareIntegrations( editor ) {
	const panelItems = editor.config.get( 'image.insert.integrations' );
	const imageInsertUIPlugin = editor.plugins.get( 'ImageInsertUI' );

	const PREDEFINED_INTEGRATIONS = {
		'insertImageViaUrl': createLabeledInputView( editor.locale )
	};

	if ( !panelItems ) {
		return PREDEFINED_INTEGRATIONS;
	}

	// Prepares ckfinder component for the `openCKFinder` integration token.
	if ( panelItems.find( item => item === 'openCKFinder' ) && editor.ui.componentFactory.has( 'ckfinder' ) ) {
		const ckFinderButton = editor.ui.componentFactory.create( 'ckfinder' );
		ckFinderButton.set( {
			withText: true,
			class: 'ck-image-insert__ck-finder-button'
		} );

		// We want to close the dropdown panel view when user clicks the ckFinderButton.
		ckFinderButton.delegate( 'execute' ).to( imageInsertUIPlugin, 'cancel' );

		PREDEFINED_INTEGRATIONS.openCKFinder = ckFinderButton;
	}

	// Creates integrations object of valid views to pass it to the ImageInsertPanelView.
	return panelItems.reduce( ( object, key ) => {
		if ( PREDEFINED_INTEGRATIONS[ key ] ) {
			object[ key ] = PREDEFINED_INTEGRATIONS[ key ];
		} else if ( editor.ui.componentFactory.has( key ) ) {
			object[ key ] = editor.ui.componentFactory.create( key );
		}

		return object;
	}, {} );
}

/**
 * Creates labeled field view.
 *
 * @param {module:utils/locale~Locale} locale The localization services instance.
 *
 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
 */
function createLabeledInputView( locale ) {
	const t = locale.t;
	const labeledInputView = new LabeledFieldView( locale, createLabeledInputText );

	labeledInputView.set( {
		label: t( 'Insert image via URL' )
	} );
	labeledInputView.fieldView.placeholder = 'https://example.com/image.png';

	return labeledInputView;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageinsert/imageinsertui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageinsert/imageinsertui
 */






/**
 * The image insert dropdown plugin.
 *
 * For a detailed overview, check the {@glink features/images/image-upload/image-upload Image upload feature}
 * and {@glink features/images/image-upload/images-inserting#inserting-images-via-source-url Insert images via source URL} documentation.
 *
 * Adds the `'insertImage'` dropdown to the {@link module:ui/componentfactory~ComponentFactory UI component factory}
 * and also the `imageInsert` dropdown as an alias for backward compatibility.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageInsertUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageInsertUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const componentCreator = locale => {
			return this._createDropdownView( locale );
		};

		// Register `insertImage` dropdown and add `imageInsert` dropdown as an alias for backward compatibility.
		editor.ui.componentFactory.add( 'insertImage', componentCreator );
		editor.ui.componentFactory.add( 'imageInsert', componentCreator );
	}

	/**
	 * Creates the dropdown view.
	 *
	 * @param {module:utils/locale~Locale} locale The localization services instance.
	 *
	 * @private
	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
	 */
	_createDropdownView( locale ) {
		const editor = this.editor;
		const t = locale.t;

		const uploadImageCommand = editor.commands.get( 'uploadImage' );
		const insertImageCommand = editor.commands.get( 'insertImage' );

		/**
		 * The dropdown view responsible for displaying the image insert UI.
		 *
		 * @member {module:ui/dropdown/dropdownview~DropdownView}
		 */
		this.dropdownView = createDropdown( locale, uploadImageCommand ? SplitButtonView : undefined );

		const buttonView = this.dropdownView.buttonView;
		const panelView = this.dropdownView.panelView;

		buttonView.set( {
			label: t( 'Insert image' ),
			icon: icons.image,
			tooltip: true
		} );

		panelView.extendTemplate( {
			attributes: {
				class: 'ck-image-insert__panel'
			}
		} );

		if ( uploadImageCommand ) {
			const splitButtonView = this.dropdownView.buttonView;

			splitButtonView.actionView = editor.ui.componentFactory.create( 'uploadImage' );
			// After we replaced action button with `uploadImage` component,
			// we have lost a proper styling and some minor visual quirks have appeared.
			// Brining back original split button classes helps fix the button styling
			// See https://github.com/ckeditor/ckeditor5/issues/7986.
			splitButtonView.actionView.extendTemplate( {
				attributes: {
					class: 'ck ck-button ck-splitbutton__action'
				}
			} );
		}

		return this._setUpDropdown( uploadImageCommand || insertImageCommand );
	}

	/**
	 * Sets up the dropdown view.
	 *
	 * @param {module:core/command~Command} command An uploadImage or insertImage command.
	 *
	 * @private
	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
	 */
	_setUpDropdown( command ) {
		const editor = this.editor;
		const t = editor.t;
		const imageInsertView = new ImageInsertPanelView( editor.locale, prepareIntegrations( editor ) );
		const insertButtonView = imageInsertView.insertButtonView;
		const insertImageViaUrlForm = imageInsertView.getIntegration( 'insertImageViaUrl' );
		const dropdownView = this.dropdownView;
		const panelView = dropdownView.panelView;
		const imageUtils = this.editor.plugins.get( 'ImageUtils' );

		dropdownView.bind( 'isEnabled' ).to( command );

		// Defer the children injection to improve initial performance.
		// See https://github.com/ckeditor/ckeditor5/pull/8019#discussion_r484069652.
		dropdownView.once( 'change:isOpen', () => {
			panelView.children.add( imageInsertView );
		} );

		dropdownView.on( 'change:isOpen', () => {
			const selectedElement = editor.model.document.selection.getSelectedElement();

			if ( dropdownView.isOpen ) {
				if ( imageUtils.isImage( selectedElement ) ) {
					imageInsertView.imageURLInputValue = selectedElement.getAttribute( 'src' );
					insertButtonView.label = t( 'Update' );
					insertImageViaUrlForm.label = t( 'Update image URL' );
				} else {
					imageInsertView.imageURLInputValue = '';
					insertButtonView.label = t( 'Insert' );
					insertImageViaUrlForm.label = t( 'Insert image via URL' );
				}
			}
		// Note: Use the low priority to make sure the following listener starts working after the
		// default action of the drop-down is executed (i.e. the panel showed up). Otherwise, the
		// invisible form/input cannot be focused/selected.
		}, { priority: 'low' } );

		imageInsertView.delegate( 'submit', 'cancel' ).to( dropdownView );
		this.delegate( 'cancel' ).to( dropdownView );

		dropdownView.on( 'submit', () => {
			closePanel();
			onSubmit();
		} );

		dropdownView.on( 'cancel', () => {
			closePanel();
		} );

		function onSubmit() {
			const selectedElement = editor.model.document.selection.getSelectedElement();

			if ( imageUtils.isImage( selectedElement ) ) {
				editor.model.change( writer => {
					writer.setAttribute( 'src', imageInsertView.imageURLInputValue, selectedElement );
					writer.removeAttribute( 'srcset', selectedElement );
					writer.removeAttribute( 'sizes', selectedElement );
				} );
			} else {
				editor.execute( 'insertImage', { source: imageInsertView.imageURLInputValue } );
			}
		}

		function closePanel() {
			editor.editing.view.focus();
			dropdownView.isOpen = false;
		}

		return dropdownView;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageinsertviaurl.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageinsertviaurl
 */




/**
 * The image insert via URL plugin.
 *
 * For a detailed overview, check the {@glink features/images/image-upload/images-inserting#inserting-images-via-source-url
 * Insert images via source URL} documentation.
 *
 * This plugin does not do anything directly, but it loads a set of specific plugins
 * to enable image inserting via implemented integrations:
 *
 * * {@link module:image/imageinsert/imageinsertui~ImageInsertUI},
 *
 * @extends module:core/plugin~Plugin
 */
class ImageInsertViaUrl extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageInsertViaUrl';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageInsertUI ];
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageinsert.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageinsert
 */






/**
 * The image insert plugin.
 *
 * For a detailed overview, check the {@glink features/images/image-upload/image-upload Image upload feature}
 * and {@glink features/images/image-upload/images-inserting#inserting-images-via-source-url Insert images via source URL} documentation.
 *
 * This plugin does not do anything directly, but it loads a set of specific plugins
 * to enable image uploading or inserting via implemented integrations:
 *
 * * {@link module:image/imageupload~ImageUpload}
 * * {@link module:image/imageinsert/imageinsertui~ImageInsertUI},
 *
 * @extends module:core/plugin~Plugin
 */
class ImageInsert extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageInsert';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUpload, ImageInsertViaUrl, ImageInsertUI ];
	}
}

/**
 * The image insert configuration.
 *
 * @member {module:image/imageinsert~ImageInsertConfig} module:image/image~ImageConfig#insert
 */

/**
 * The configuration of the image insert dropdown panel view. Used by the image insert feature in the `@ckeditor/ckeditor5-image` package.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				image: {
 * 					insert: {
 *						... // settings for "insertImage" view goes here
 * 					}
 * 				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface module:image/imageinsert~ImageInsertConfig
 */

/**
 * The image insert panel view configuration contains a list of {@link module:image/imageinsert~ImageInsert} integrations.
 *
 * The option accepts string tokens.
 * * for predefined integrations, we have two special strings: `insertImageViaUrl` and `openCKFinder`.
 * The former adds the **Insert image via URL** feature, while the latter adds the built-in **CKFinder** integration.
 * * for custom integrations, each string should be a name of the component registered in the
 * {@link module:ui/componentfactory~ComponentFactory component factory}.
 * If you have a plugin `PluginX` that registers `pluginXButton` component, then the integration token
 * in that case should be `pluginXButton`.
 *
 *		// Add `insertImageViaUrl`, `openCKFinder` and custom `pluginXButton` integrations.
 *		const imageInsertConfig = {
 *			insert: {
 *				integrations: [
 *					'insertImageViaUrl',
 *					'openCKFinder',
 *					'pluginXButton'
 *				]
 *			}
 *		};
 *
 * @protected
 * @member {Array.<String>} module:image/imageinsert~ImageInsertConfig#integrations
 * @default [ 'insertImageViaUrl' ]
 */

/**
 * This options allows to override the image type used by the {@link module:image/image/insertimagecommand~InsertImageCommand} when the user
 * inserts new images into the editor content. By default, this option is unset which means the editor will choose the optimal image type
 * based on the context of the insertion (e.g. the current selection and availability of plugins)
 *
 * Available options are:
 *
 * * `'block'` – all images inserted into the editor will be block (requires the {@link module:image/imageblock~ImageBlock} plugin),
 * * `'inline'` – all images inserted into the editor will be inline (requires the {@link module:image/imageinline~ImageInline} plugin).
 *
 * @member {'inline'|'block'|undefined} module:image/imageinsert~ImageInsertConfig#type
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageresize/resizeimagecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageresize/resizeimagecommand
 */



/**
 * The resize image command. Currently, it only supports the width attribute.
 *
 * @extends module:core/command~Command
 */
class ResizeImageCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const element = imageUtils.getClosestSelectedImageElement( editor.model.document.selection );

		this.isEnabled = !!element;

		if ( !element || !element.hasAttribute( 'width' ) ) {
			this.value = null;
		} else {
			this.value = {
				width: element.getAttribute( 'width' ),
				height: null
			};
		}
	}

	/**
	 * Executes the command.
	 *
	 *		// Sets the width to 50%:
	 *		editor.execute( 'resizeImage', { width: '50%' } );
	 *
	 *		// Removes the width attribute:
	 *		editor.execute( 'resizeImage', { width: null } );
	 *
	 * @param {Object} options
	 * @param {String|null} options.width The new width of the image.
	 * @fires execute
	 */
	execute( options ) {
		const editor = this.editor;
		const model = editor.model;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const imageElement = imageUtils.getClosestSelectedImageElement( model.document.selection );

		this.value = {
			width: options.width,
			height: null
		};

		if ( imageElement ) {
			model.change( writer => {
				writer.setAttribute( 'width', options.width, imageElement );
			} );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageresize/imageresizeediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageresize/imageresizeediting
 */





/**
 * The image resize editing feature.
 *
 * It adds the ability to resize each image using handles or manually by
 * {@link module:image/imageresize/imageresizebuttons~ImageResizeButtons} buttons.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageResizeEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUtils ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageResizeEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'image', {
			resizeUnit: '%',
			resizeOptions: [ {
				name: 'resizeImage:original',
				value: null,
				icon: 'original'
			},
			{
				name: 'resizeImage:25',
				value: '25',
				icon: 'small'
			},
			{
				name: 'resizeImage:50',
				value: '50',
				icon: 'medium'
			},
			{
				name: 'resizeImage:75',
				value: '75',
				icon: 'large'
			} ]
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const resizeImageCommand = new ResizeImageCommand( editor );

		this._registerSchema();
		this._registerConverters( 'imageBlock' );
		this._registerConverters( 'imageInline' );

		// Register `resizeImage` command and add `imageResize` command as an alias for backward compatibility.
		editor.commands.add( 'resizeImage', resizeImageCommand );
		editor.commands.add( 'imageResize', resizeImageCommand );
	}

	/**
	 * @private
	 */
	_registerSchema() {
		if ( this.editor.plugins.has( 'ImageBlockEditing' ) ) {
			this.editor.model.schema.extend( 'imageBlock', { allowAttributes: 'width' } );
		}

		if ( this.editor.plugins.has( 'ImageInlineEditing' ) ) {
			this.editor.model.schema.extend( 'imageInline', { allowAttributes: 'width' } );
		}
	}

	/**
	 * Registers image resize converters.
	 *
	 * @private
	 * @param {'imageBlock'|'imageInline'} imageType The type of the image.
	 */
	_registerConverters( imageType ) {
		const editor = this.editor;

		// Dedicated converter to propagate image's attribute to the img tag.
		editor.conversion.for( 'downcast' ).add( dispatcher =>
			dispatcher.on( `attribute:width:${ imageType }`, ( evt, data, conversionApi ) => {
				if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
					return;
				}

				const viewWriter = conversionApi.writer;
				const figure = conversionApi.mapper.toViewElement( data.item );

				if ( data.attributeNewValue !== null ) {
					viewWriter.setStyle( 'width', data.attributeNewValue, figure );
					viewWriter.addClass( 'image_resized', figure );
				} else {
					viewWriter.removeStyle( 'width', figure );
					viewWriter.removeClass( 'image_resized', figure );
				}
			} )
		);

		editor.conversion.for( 'upcast' )
			.attributeToAttribute( {
				view: {
					name: imageType === 'imageBlock' ? 'figure' : 'img',
					styles: {
						width: /.+/
					}
				},
				model: {
					key: 'width',
					value: viewElement => viewElement.getStyle( 'width' )
				}
			} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageresize/imageresizebuttons.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageresize/imageresizebuttons
 */







const RESIZE_ICONS = {
	small: icons.objectSizeSmall,
	medium: icons.objectSizeMedium,
	large: icons.objectSizeLarge,
	original: icons.objectSizeFull
};

/**
 * The image resize buttons plugin.
 *
 * It adds a possibility to resize images using the toolbar dropdown or individual buttons, depending on the plugin configuration.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageResizeButtons extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageResizeEditing ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageResizeButtons';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * The resize unit.
		 *
		 * @readonly
		 * @private
		 * @type {module:image/image~ImageConfig#resizeUnit}
		 * @default '%'
		 */
		this._resizeUnit = editor.config.get( 'image.resizeUnit' );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const options = editor.config.get( 'image.resizeOptions' );
		const command = editor.commands.get( 'resizeImage' );

		this.bind( 'isEnabled' ).to( command );

		for ( const option of options ) {
			this._registerImageResizeButton( option );
		}

		this._registerImageResizeDropdown( options );
	}

	/**
	 * A helper function that creates a standalone button component for the plugin.
	 *
	 * @private
	 * @param {module:image/imageresize/imageresizebuttons~ImageResizeOption} resizeOption A model of the resize option.
	 */
	_registerImageResizeButton( option ) {
		const editor = this.editor;
		const { name, value, icon } = option;
		const optionValueWithUnit = value ? value + this._resizeUnit : null;

		editor.ui.componentFactory.add( name, locale => {
			const button = new buttonview_ButtonView( locale );
			const command = editor.commands.get( 'resizeImage' );
			const labelText = this._getOptionLabelValue( option, true );

			if ( !RESIZE_ICONS[ icon ] ) {
				/**
				 * When configuring {@link module:image/image~ImageConfig#resizeOptions `config.image.resizeOptions`} for standalone
				 * buttons, a valid `icon` token must be set for each option.
				 *
				 * See all valid options described in the
				 * {@link module:image/imageresize/imageresizebuttons~ImageResizeOption plugin configuration}.
				 *
				 * @error imageresizebuttons-missing-icon
				 * @param {module:image/imageresize/imageresizebuttons~ImageResizeOption} option Invalid image resize option.
				*/
				throw new CKEditorError(
					'imageresizebuttons-missing-icon',
					editor,
					option
				);
			}

			button.set( {
				// Use the `label` property for a verbose description (because of ARIA).
				label: labelText,
				icon: RESIZE_ICONS[ icon ],
				tooltip: labelText,
				isToggleable: true
			} );

			// Bind button to the command.
			button.bind( 'isEnabled' ).to( this );
			button.bind( 'isOn' ).to( command, 'value', getIsOnButtonCallback( optionValueWithUnit ) );

			this.listenTo( button, 'execute', () => {
				editor.execute( 'resizeImage', { width: optionValueWithUnit } );
			} );

			return button;
		} );
	}

	/**
	 * A helper function that creates a dropdown component for the plugin containing all the resize options defined in
	 * the editor configuration.
	 *
	 * @private
	 * @param {Array.<module:image/imageresize/imageresizebuttons~ImageResizeOption>} options An array of configured options.
	 */
	_registerImageResizeDropdown( options ) {
		const editor = this.editor;
		const t = editor.t;
		const originalSizeOption = options.find( option => !option.value );

		const componentCreator = locale => {
			const command = editor.commands.get( 'resizeImage' );
			const dropdownView = createDropdown( locale, DropdownButtonView );
			const dropdownButton = dropdownView.buttonView;

			dropdownButton.set( {
				tooltip: t( 'Resize image' ),
				commandValue: originalSizeOption.value,
				icon: RESIZE_ICONS.medium,
				isToggleable: true,
				label: this._getOptionLabelValue( originalSizeOption ),
				withText: true,
				class: 'ck-resize-image-button'
			} );

			dropdownButton.bind( 'label' ).to( command, 'value', commandValue => {
				if ( commandValue && commandValue.width ) {
					return commandValue.width;
				} else {
					return this._getOptionLabelValue( originalSizeOption );
				}
			} );
			dropdownView.bind( 'isOn' ).to( command );
			dropdownView.bind( 'isEnabled' ).to( this );

			addListToDropdown( dropdownView, this._getResizeDropdownListItemDefinitions( options, command ) );

			dropdownView.listView.ariaLabel = t( 'Image resize list' );

			// Execute command when an item from the dropdown is selected.
			this.listenTo( dropdownView, 'execute', evt => {
				editor.execute( evt.source.commandName, { width: evt.source.commandValue } );
				editor.editing.view.focus();
			} );

			return dropdownView;
		};

		// Register `resizeImage` dropdown and add `imageResize` dropdown as an alias for backward compatibility.
		editor.ui.componentFactory.add( 'resizeImage', componentCreator );
		editor.ui.componentFactory.add( 'imageResize', componentCreator );
	}

	/**
	 * A helper function for creating an option label value string.
	 *
	 * @private
	 * @param {module:image/imageresize/imageresizebuttons~ImageResizeOption} option A resize option object.
	 * @param {Boolean} [forTooltip] An optional flag for creating a tooltip label.
	 * @returns {String} A user-defined label combined from the numeric value and the resize unit or the default label
	 * for reset options (`Original`).
	 */
	_getOptionLabelValue( option, forTooltip ) {
		const t = this.editor.t;

		if ( option.label ) {
			return option.label;
		} else if ( forTooltip ) {
			if ( option.value ) {
				return t( 'Resize image to %0', option.value + this._resizeUnit );
			} else {
				return t( 'Resize image to the original size' );
			}
		} else {
			if ( option.value ) {
				return option.value + this._resizeUnit;
			} else {
				return t( 'Original' );
			}
		}
	}

	/**
	 * A helper function that parses the resize options and returns list item definitions ready for use in the dropdown.
	 *
	 * @private
	 * @param {Array.<module:image/imageresize/imageresizebuttons~ImageResizeOption>} options The resize options.
	 * @param {module:image/imageresize/resizeimagecommand~ResizeImageCommand} command The resize image command.
	 * @returns {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>} Dropdown item definitions.
	 */
	_getResizeDropdownListItemDefinitions( options, command ) {
		const itemDefinitions = new Collection();

		options.map( option => {
			const optionValueWithUnit = option.value ? option.value + this._resizeUnit : null;
			const definition = {
				type: 'button',
				model: new model_Model( {
					commandName: 'resizeImage',
					commandValue: optionValueWithUnit,
					label: this._getOptionLabelValue( option ),
					withText: true,
					icon: null
				} )
			};

			definition.model.bind( 'isOn' ).to( command, 'value', getIsOnButtonCallback( optionValueWithUnit ) );

			itemDefinitions.add( definition );
		} );

		return itemDefinitions;
	}
}

// A helper function for setting the `isOn` state of buttons in value bindings.
function getIsOnButtonCallback( value ) {
	return commandValue => {
		if ( value === null && commandValue === value ) {
			return true;
		}

		return commandValue && commandValue.width === value;
	};
}

/**
 * The image resize option used in the {@link module:image/image~ImageConfig#resizeOptions image resize configuration}.
 *
 * @typedef {Object} module:image/imageresize/imageresizebuttons~ImageResizeOption
 * @property {String} name The name of the UI component that changes the image size.
 * * If you configure the feature using individual resize buttons, you can refer to this name in the
 * {@link module:image/image~ImageConfig#toolbar image toolbar configuration}.
 * * If you configure the feature using the resize dropdown, this name will be used for a list item in the dropdown.
 * @property {String} value The value of the resize option without the unit
 * ({@link module:image/image~ImageConfig#resizeUnit configured separately}). `null` resets an image to its original size.
 * @property {String} [icon] An icon used by an individual resize button (see the `name` property to learn more).
 * Available icons are: `'small'`, `'medium'`, `'large'`, `'original'`.
 * @property {String} [label] An option label displayed in the dropdown or, if the feature is configured using
 * individual buttons, a {@link module:ui/button/buttonview~ButtonView#tooltip} and an ARIA attribute of a button.
 * If not specified, the label is generated automatically based on the `value` option and the
 * {@link module:image/image~ImageConfig#resizeUnit `config.image.resizeUnit`}.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageresize/imageresizehandles.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageresize/imageresizehandles
 */






const RESIZABLE_IMAGES_CSS_SELECTOR =
	'figure.image.ck-widget > img,' +
	'figure.image.ck-widget > picture > img,' +
	'figure.image.ck-widget > a > img,' +
	'figure.image.ck-widget > a > picture > img,' +
	'span.image-inline.ck-widget > img,' +
	'span.image-inline.ck-widget > picture > img';

const IMAGE_WIDGETS_CLASSES_MATCH_REGEXP = /(image|image-inline)/;

const RESIZED_IMAGE_CLASS = 'image_resized';

/**
 * The image resize by handles feature.
 *
 * It adds the ability to resize each image using handles or manually by
 * {@link module:image/imageresize/imageresizebuttons~ImageResizeButtons} buttons.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageResizeHandles extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ WidgetResize ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageResizeHandles';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const command = this.editor.commands.get( 'resizeImage' );
		this.bind( 'isEnabled' ).to( command );

		this._setupResizerCreator();
	}

	/**
	 * Attaches the listeners responsible for creating a resizer for each image, except for images inside the HTML embed preview.
	 *
	 * @private
	 */
	_setupResizerCreator() {
		const editor = this.editor;
		const editingView = editor.editing.view;

		editingView.addObserver( ImageLoadObserver );

		this.listenTo( editingView.document, 'imageLoaded', ( evt, domEvent ) => {
			// The resizer must be attached only to images loaded by the `ImageInsert`, `ImageUpload` or `LinkImage` plugins.
			if ( !domEvent.target.matches( RESIZABLE_IMAGES_CSS_SELECTOR ) ) {
				return;
			}

			const domConverter = editor.editing.view.domConverter;
			const imageView = domConverter.domToView( domEvent.target );
			const widgetView = imageView.findAncestor( { classes: IMAGE_WIDGETS_CLASSES_MATCH_REGEXP } );
			let resizer = this.editor.plugins.get( WidgetResize ).getResizerByViewElement( widgetView );

			if ( resizer ) {
				// There are rare cases when the image will be triggered multiple times for the same widget, e.g. when
				// the image's source was changed after upload (https://github.com/ckeditor/ckeditor5/pull/8108#issuecomment-708302992).
				resizer.redraw();

				return;
			}

			const mapper = editor.editing.mapper;
			const imageModel = mapper.toModelElement( widgetView );

			resizer = editor.plugins
				.get( WidgetResize )
				.attachTo( {
					unit: editor.config.get( 'image.resizeUnit' ),

					modelElement: imageModel,
					viewElement: widgetView,
					editor,

					getHandleHost( domWidgetElement ) {
						return domWidgetElement.querySelector( 'img' );
					},
					getResizeHost() {
						// Return the model image element parent to avoid setting an inline element (<a>/<span>) as a resize host.
						return domConverter.mapViewToDom( mapper.toViewElement( imageModel.parent ) );
					},
					// TODO consider other positions.
					isCentered() {
						const imageStyle = imageModel.getAttribute( 'imageStyle' );

						return !imageStyle || imageStyle == 'block' || imageStyle == 'alignCenter';
					},

					onCommit( newValue ) {
						// Get rid of the CSS class in case the command execution that follows is unsuccessful
						// (e.g. Track Changes can override it and the new dimensions will not apply). Otherwise,
						// the presence of the class and the absence of the width style will cause it to take 100%
						// of the horizontal space.
						editingView.change( writer => {
							writer.removeClass( RESIZED_IMAGE_CLASS, widgetView );
						} );

						editor.execute( 'resizeImage', { width: newValue } );
					}
				} );

			resizer.on( 'updateSize', () => {
				if ( !widgetView.hasClass( RESIZED_IMAGE_CLASS ) ) {
					editingView.change( writer => {
						writer.addClass( RESIZED_IMAGE_CLASS, widgetView );
					} );
				}
			} );

			resizer.bind( 'isEnabled' ).to( this );
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imageresize.css
var imageresize = __webpack_require__(1043);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imageresize.css

            

var imageresize_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imageresize_options.insert = "head";
imageresize_options.singleton = true;

var imageresize_update = injectStylesIntoStyleTag_default()(imageresize/* default */.Z, imageresize_options);



/* harmony default export */ const theme_imageresize = (imageresize/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imageresize.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imageresize
 */








/**
 * The image resize plugin.
 *
 * It adds a possibility to resize each image using handles.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageResize extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageResizeEditing, ImageResizeHandles, ImageResizeButtons ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageResize';
	}
}

/**
 * The available options are `'px'` or `'%'`.
 *
 * Determines the size unit applied to the resized image.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				image: {
 *					resizeUnit: 'px'
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 *
 * This option is used by the {@link module:image/imageresize~ImageResize} feature.
 *
 * @default '%'
 * @member {String} module:image/image~ImageConfig#resizeUnit
 */

/**
 * The image resize options.
 *
 * Each option should have at least these two properties:
 *
 * * name: The name of the UI component registered in the global
 * {@link module:core/editor/editorui~EditorUI#componentFactory component factory} of the editor,
 * representing the button a user can click to change the size of an image,
 * * value: An actual image width applied when a user clicks the mentioned button
 * ({@link module:image/imageresize/resizeimagecommand~ResizeImageCommand} gets executed).
 * The value property is combined with the {@link module:image/image~ImageConfig#resizeUnit `config.image.resizeUnit`} (`%` by default).
 * For instance: `value: '50'` and `resizeUnit: '%'` will render as `'50%'` in the UI.
 *
 * **Resetting the image size**
 *
 * If you want to set an option that will reset image to its original size, you need to pass a `null` value
 * to one of the options. The `:original` token is not mandatory, you can call it anything you wish, but it must reflect
 * in the standalone buttons configuration for the image toolbar.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				image: {
 *					resizeUnit: "%",
 *					resizeOptions: [ {
 *						name: 'resizeImage:original',
 *						value: null
 *					},
 *					{
 *						name: 'resizeImage:50',
 *						value: '50'
 *					},
 *					{
 *						name: 'resizeImage:75',
 *						value: '75'
 *					} ]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * **Resizing images using a dropdown**
 *
 * With resize options defined, you can decide whether you want to display them as a dropdown or as standalone buttons.
 * For the dropdown, you need to pass only the `resizeImage` token to the
{@link module:image/image~ImageConfig#toolbar `config.image.toolbar`}. The dropdown contains all defined options by default:
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				image: {
 *					resizeUnit: "%",
 *					resizeOptions: [ {
 *						name: 'resizeImage:original',
 *						value: null
 *					},
 *					{
 *						name: 'resizeImage:50',
 *						value: '50'
 *					},
 *					{
 *						name: 'resizeImage:75',
 *						value: '75'
 *					} ],
 *					toolbar: [ 'resizeImage', ... ],
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * **Resizing images using individual buttons**
 *
 * If you want to have separate buttons for {@link module:image/imageresize/imageresizebuttons~ImageResizeOption each option},
 * pass their names to the {@link module:image/image~ImageConfig#toolbar `config.image.toolbar`} instead. Please keep in mind
 * that this time **you must define the additional
 * {@link module:image/imageresize/imageresizebuttons~ImageResizeOption `icon` property}**:
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				image: {
 *					resizeUnit: "%",
 *					resizeOptions: [ {
 *						name: 'resizeImage:original',
 *						value: null,
 *						icon: 'original'
 *					},
 *					{
 *						name: 'resizeImage:25',
 *						value: '25',
 *						icon: 'small'
 *					},
 *					{
 *						name: 'resizeImage:50',
 *						value: '50',
 *						icon: 'medium'
 *					},
 *					{
 *						name: 'resizeImage:75',
 *						value: '75',
 *						icon: 'large'
 *					} ],
 *					toolbar: [ 'resizeImage:25', 'resizeImage:50', 'resizeImage:75', 'resizeImage:original', ... ],
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * **Customizing resize button labels**
 *
 * You can set your own label for each resize button. To do that, add the `label` property like in the example below.
 *
 * * When using the **dropdown**, the labels are displayed on the list of all options when you open the dropdown.
 * * When using **standalone buttons**, the labels will are displayed as tooltips when a user hovers over the button.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				image: {
 *					resizeUnit: "%",
 *					resizeOptions: [ {
 *						name: 'resizeImage:original',
 *						value: null,
 *						label: 'Original size'
 *						// Note: add the "icon" property if you're configuring a standalone button.
 *					},
 *					{
 *						name: 'resizeImage:50',
 *						value: '50',
 *						label: 'Medium size'
 *						// Note: add the "icon" property if you're configuring a standalone button.
 *					},
 *					{
 *						name: 'resizeImage:75',
 *						value: '75',
 *						label: 'Large size'
 *						// Note: add the "icon" property if you're configuring a standalone button.
 *					} ]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * **Default value**
 *
 * The following configuration is used by default:
 *
 *		resizeOptions = [
 *			{
 *				name: 'resizeImage:original',
 *				value: null,
 *				icon: 'original'
 *			},
 *			{
 *				name: 'resizeImage:25',
 *				value: '25',
 *				icon: 'small'
 *			},
 *			{
 *				name: 'resizeImage:50',
 *				value: '50',
 *				icon: 'medium'
 *			},
 *			{
 *				name: 'resizeImage:75',
 *				value: '75',
 *				icon: 'large'
 *			}
 *		];
 *
 * @member {Array.<module:image/imageresize/imageresizebuttons~ImageResizeOption>} module:image/image~ImageConfig#resizeOptions
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagestyle/imagestylecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagestyle/imagestylecommand
 */



/**
 * The image style command. It is used to apply {@link module:image/imagestyle~ImageStyleConfig#options image style option}
 * to a selected image.
 *
 * **Note**: Executing this command may change the image model element if the desired style requires an image of a different
 * type. See {@link module:image/imagestyle/imagestylecommand~ImageStyleCommand#execute} to learn more.
 *
 * @extends module:core/command~Command
 */
class ImageStyleCommand extends command_Command {
	/**
	 * Creates an instance of the image style command. When executed, the command applies one of
	 * {@link module:image/imagestyle~ImageStyleConfig#options style options} to the currently selected image.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {Array.<module:image/imagestyle~ImageStyleOptionDefinition>} styles
	 * The style options that this command supports.
	 */
	constructor( editor, styles ) {
		super( editor );

		/**
		 * An object containing names of default style options for the inline and block images.
		 * If there is no default style option for the given image type in the configuration,
		 * the name will be `false`.
		 *
		 * @private
		 * @type {Object.<String,module:image/imagestyle~ImageStyleOptionDefinition#name>}
		 */
		this._defaultStyles = {
			imageBlock: false,
			imageInline: false
		};

		/**
		 * The styles handled by this command.
		 *
		 * @private
		 * @type {module:image/imagestyle~ImageStyleConfig#options}
		 */
		this._styles = new Map( styles.map( style => {
			if ( style.isDefault ) {
				for ( const modelElementName of style.modelElements ) {
					this._defaultStyles[ modelElementName ] = style.name;
				}
			}

			return [ style.name, style ];
		} ) );
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const imageUtils = editor.plugins.get( 'ImageUtils' );
		const element = imageUtils.getClosestSelectedImageElement( this.editor.model.document.selection );

		this.isEnabled = !!element;

		if ( !this.isEnabled ) {
			this.value = false;
		} else if ( element.hasAttribute( 'imageStyle' ) ) {
			this.value = element.getAttribute( 'imageStyle' );
		} else {
			this.value = this._defaultStyles[ element.name ];
		}
	}

	/**
	 * Executes the command and applies the style to the currently selected image:
	 *
	 *		editor.execute( 'imageStyle', { value: 'side' } );
	 *
	 * **Note**: Executing this command may change the image model element if the desired style requires an image
	 * of a different type. Learn more about {@link module:image/imagestyle~ImageStyleOptionDefinition#modelElements model element}
	 * configuration for the style option.
	 *
	 * @param {Object} options
	 * @param {module:image/imagestyle~ImageStyleOptionDefinition#name} options.value The name of the style (as configured in
	 * {@link module:image/imagestyle~ImageStyleConfig#options}).
	 * @fires execute
	 */
	execute( options = {} ) {
		const editor = this.editor;
		const model = editor.model;
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		model.change( writer => {
			const requestedStyle = options.value;

			let imageElement = imageUtils.getClosestSelectedImageElement( model.document.selection );

			// Change the image type if a style requires it.
			if ( requestedStyle && this.shouldConvertImageType( requestedStyle, imageElement ) ) {
				this.editor.execute( imageUtils.isBlockImage( imageElement ) ? 'imageTypeInline' : 'imageTypeBlock' );

				// Update the imageElement to the newly created image.
				imageElement = imageUtils.getClosestSelectedImageElement( model.document.selection );
			}

			// Default style means that there is no `imageStyle` attribute in the model.
			// https://github.com/ckeditor/ckeditor5-image/issues/147
			if ( !requestedStyle || this._styles.get( requestedStyle ).isDefault ) {
				writer.removeAttribute( 'imageStyle', imageElement );
			} else {
				writer.setAttribute( 'imageStyle', requestedStyle, imageElement );
			}
		} );
	}

	/**
	 * Returns `true` if requested style change would trigger the image type change.
	 *
	 * @param {module:image/imagestyle~ImageStyleOptionDefinition} requestedStyle The name of the style (as configured in
	 * {@link module:image/imagestyle~ImageStyleConfig#options}).
	 * @param {module:engine/model/element~Element} imageElement The image model element.
	 * @returns {Boolean}
	 */
	shouldConvertImageType( requestedStyle, imageElement ) {
		const supportedTypes = this._styles.get( requestedStyle ).modelElements;

		return !supportedTypes.includes( imageElement.name );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagestyle/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagestyle/utils
 */




const {
	objectFullWidth,
	objectInline,
	objectLeft,	objectRight, objectCenter,
	objectBlockLeft, objectBlockRight
} = icons;

/**
 * Default image style options provided by the plugin that can be referred in the {@link module:image/image~ImageConfig#styles}
 * configuration.
 *
 * There are available 5 styles focused on formatting:
 *
 * * **`'alignLeft'`** aligns the inline or block image to the left and wraps it with the text using the `image-style-align-left` class,
 * * **`'alignRight'`** aligns the inline or block image to the right and wraps it with the text using the `image-style-align-right` class,
 * * **`'alignCenter'`** centers the block image using the `image-style-align-center` class,
 * * **`'alignBlockLeft'`** aligns the block image to the left using the `image-style-block-align-left` class,
 * * **`'alignBlockRight'`** aligns the block image to the right using the `image-style-block-align-right` class,
 *
 * and 3 semantic styles:
 *
 * * **`'inline'`** is an inline image without any CSS class,
 * * **`'block'`** is a block image without any CSS class,
 * * **`'side'`** is a block image styled with the `image-style-side` CSS class.
 *
 * @readonly
 * @type {Object.<String,module:image/imagestyle~ImageStyleOptionDefinition>}
 */
const DEFAULT_OPTIONS = {
	// This style represents an image placed in the line of text.
	get inline() {
		return {
			name: 'inline',
			title: 'In line',
			icon: objectInline,
			modelElements: [ 'imageInline' ],
			isDefault: true
		};
	},

	// This style represents an image aligned to the left and wrapped with text.
	get alignLeft() {
		return {
			name: 'alignLeft',
			title: 'Left aligned image',
			icon: objectLeft,
			modelElements: [ 'imageBlock', 'imageInline' ],
			className: 'image-style-align-left'
		};
	},

	// This style represents an image aligned to the left.
	get alignBlockLeft() {
		return {
			name: 'alignBlockLeft',
			title: 'Left aligned image',
			icon: objectBlockLeft,
			modelElements: [ 'imageBlock' ],
			className: 'image-style-block-align-left'
		};
	},

	// This style represents a centered image.
	get alignCenter() {
		return {
			name: 'alignCenter',
			title: 'Centered image',
			icon: objectCenter,
			modelElements: [ 'imageBlock' ],
			className: 'image-style-align-center'
		};
	},

	// This style represents an image aligned to the right and wrapped with text.
	get alignRight() {
		return {
			name: 'alignRight',
			title: 'Right aligned image',
			icon: objectRight,
			modelElements: [ 'imageBlock', 'imageInline' ],
			className: 'image-style-align-right'
		};
	},

	// This style represents an image aligned to the right.
	get alignBlockRight() {
		return {
			name: 'alignBlockRight',
			title: 'Right aligned image',
			icon: objectBlockRight,
			modelElements: [ 'imageBlock' ],
			className: 'image-style-block-align-right'
		};
	},

	// This option is equal to the situation when no style is applied.
	get block() {
		return {
			name: 'block',
			title: 'Centered image',
			icon: objectCenter,
			modelElements: [ 'imageBlock' ],
			isDefault: true
		};
	},

	// This represents a side image.
	get side() {
		return {
			name: 'side',
			title: 'Side image',
			icon: objectRight,
			modelElements: [ 'imageBlock' ],
			className: 'image-style-side'
		};
	}
};

/**
 * Default image style icons provided by the plugin that can be referred in the {@link module:image/image~ImageConfig#styles}
 * configuration.
 *
 * See {@link module:image/imagestyle~ImageStyleOptionDefinition#icon} to learn more.
 *
 * There are 7 default icons available: `'full'`, `'left'`, `'inlineLeft'`, `'center'`, `'right'`, `'inlineRight'`, and `'inline'`.
 *
 * @readonly
 * @type {Object.<String,String>}
 */
const DEFAULT_ICONS = {
	full: objectFullWidth,
	left: objectBlockLeft,
	right: objectBlockRight,
	center: objectCenter,
	inlineLeft: objectLeft,
	inlineRight: objectRight,
	inline: objectInline
};

/**
 * Default drop-downs provided by the plugin that can be referred in the {@link module:image/image~ImageConfig#toolbar}
 * configuration. The drop-downs are containers for the {@link module:image/imagestyle~ImageStyleConfig#options image style options}.
 *
 * If both of the `ImageEditing` plugins are loaded, there are 2 predefined drop-downs available:
 *
 * * **`'imageStyle:wrapText'`**, which contains the `alignLeft` and `alignRight` options, that is,
 * those that wraps the text around the image,
 * * **`'imageStyle:breakText'`**, which contains the `alignBlockLeft`, `alignCenter` and `alignBlockRight` options, that is,
 * those that breaks the text around the image.
 *
 * @readonly
 * @type {Array.<module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition>}
 */
const DEFAULT_DROPDOWN_DEFINITIONS = [ {
	name: 'imageStyle:wrapText',
	title: 'Wrap text',
	defaultItem: 'imageStyle:alignLeft',
	items: [ 'imageStyle:alignLeft', 'imageStyle:alignRight' ]
}, {
	name: 'imageStyle:breakText',
	title: 'Break text',
	defaultItem: 'imageStyle:block',
	items: [ 'imageStyle:alignBlockLeft', 'imageStyle:block', 'imageStyle:alignBlockRight' ]
} ];

/**
 * Returns a list of the normalized and validated image style options.
 *
 * @protected
 * @param {Object} config
 * @param {Boolean} config.isInlinePluginLoaded
 * Determines whether the {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin has been loaded.
 * @param {Boolean} config.isBlockPluginLoaded
 * Determines whether the {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin has been loaded.
 * @param {module:image/imagestyle~ImageStyleConfig} config.configuredStyles
 * The image styles configuration provided in the image styles {@link module:image/image~ImageConfig#styles configuration}
 * as a default or custom value.
 * @returns {module:image/imagestyle~ImageStyleConfig}
 * * Each of options contains a complete icon markup.
 * * The image style options not supported by any of the loaded plugins are filtered out.
 */
function normalizeStyles( config ) {
	const configuredStyles = config.configuredStyles.options || [];

	const styles = configuredStyles
		.map( arrangement => normalizeDefinition( arrangement ) )
		.filter( arrangement => isValidOption( arrangement, config ) );

	return styles;
}

/**
 * Returns the default image styles configuration depending on the loaded image editing plugins.
 * @protected
 *
 * @param {Boolean} isInlinePluginLoaded
 * Determines whether the {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin has been loaded.
 *
 * @param {Boolean} isBlockPluginLoaded
 * Determines whether the {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin has been loaded.
 *
 * @returns {Object<String,Array>}
 * It returns an object with the lists of the image style options and groups defined as strings related to the
 * {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default options}
 */
function getDefaultStylesConfiguration( isBlockPluginLoaded, isInlinePluginLoaded ) {
	if ( isBlockPluginLoaded && isInlinePluginLoaded ) {
		return {
			options: [
				'inline', 'alignLeft', 'alignRight',
				'alignCenter', 'alignBlockLeft', 'alignBlockRight',
				'block', 'side'
			]
		};
	} else if ( isBlockPluginLoaded ) {
		return {
			options: [ 'block', 'side' ]
		};
	} else if ( isInlinePluginLoaded ) {
		return {
			options: [ 'inline', 'alignLeft', 'alignRight' ]
		};
	}

	return {};
}

/**
 * Returns a list of the available predefined drop-downs' definitions depending on the loaded image editing plugins.
 * @protected
 *
 * @param {module:core/plugincollection~PluginCollection} pluginCollection
 * @returns {Array.<module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition>}
 */
function getDefaultDropdownDefinitions( pluginCollection ) {
	if ( pluginCollection.has( 'ImageBlockEditing' ) && pluginCollection.has( 'ImageInlineEditing' ) ) {
		return [ ...DEFAULT_DROPDOWN_DEFINITIONS ];
	} else {
		return [];
	}
}

// Normalizes an image style option or group provided in the {@link module:image/image~ImageConfig#styles}
// and returns it in a {@link module:image/imagestyle~ImageStyleOptionDefinition}/
//
// @param {Object|String} definition
//
// @returns {module:image/imagestyle~ImageStyleOptionDefinition}}
function normalizeDefinition( definition ) {
	if ( typeof definition === 'string' ) {
		// Just the name of the style has been passed, but none of the defaults.
		if ( !DEFAULT_OPTIONS[ definition ] ) {
			// Normalize the style anyway to prevent errors.
			definition = { name: definition };
		}
		// Just the name of the style has been passed and it's one of the defaults, just use it.
		// Clone the style to avoid overriding defaults.
		else {
			definition = { ...DEFAULT_OPTIONS[ definition ] };
		}
	} else {
		// If an object style has been passed and if the name matches one of the defaults,
		// extend it with defaults – the user wants to customize a default style.
		// Note: Don't override the user–defined style object, clone it instead.
		definition = extendStyle( DEFAULT_OPTIONS[ definition.name ], definition );
	}

	// If an icon is defined as a string and correspond with a name
	// in default icons, use the default icon provided by the plugin.
	if ( typeof definition.icon === 'string' ) {
		definition.icon = DEFAULT_ICONS[ definition.icon ] || definition.icon;
	}

	return definition;
}

// Checks if the image style option is valid:
// * if it has the modelElements fields defined and filled,
// * if the defined modelElements are supported by any of the loaded image editing plugins.
// It also displays a console warning these conditions are not met.
//
// @param {module:image/imagestyle~ImageStyleOptionDefinition} image style option
// @param {Object.<String,Boolean>} { isBlockPluginLoaded, isInlinePluginLoaded }
//
// @returns Boolean
function isValidOption( option, { isBlockPluginLoaded, isInlinePluginLoaded } ) {
	const { modelElements, name } = option;

	if ( !modelElements || !modelElements.length || !name ) {
		warnInvalidStyle( { style: option } );

		return false;
	} else {
		const supportedElements = [ isBlockPluginLoaded ? 'imageBlock' : null, isInlinePluginLoaded ? 'imageInline' : null ];

		// Check if the option is supported by any of the loaded plugins.
		if ( !modelElements.some( elementName => supportedElements.includes( elementName ) ) ) {
			/**
			 * In order to work correctly, each image style {@link module:image/imagestyle~ImageStyleOptionDefinition option}
			 * requires specific model elements (also: types of images) to be supported by the editor.
			 *
			 * Model element names to which the image style option can be applied are defined in the
			 * {@link module:image/imagestyle~ImageStyleOptionDefinition#modelElements} property of the style option
			 * definition.
			 *
			 * Explore the warning in the console to find out precisely which option is not supported and which editor plugins
			 * are missing. Make sure these plugins are loaded in your editor to get this image style option working.
			 *
			 * @error image-style-missing-dependency
			 * @param {String} [option] The name of the unsupported option.
			 * @param {String} [missingPlugins] The names of the plugins one of which has to be loaded for the particular option.
			 */
			logWarning( 'image-style-missing-dependency', {
				style: option,
				missingPlugins: modelElements.map( name => name === 'imageBlock' ? 'ImageBlockEditing' : 'ImageInlineEditing' )
			} );

			return false;
		}
	}

	return true;
}

// Extends the default style with a style provided by the developer.
// Note: Don't override the custom–defined style object, clone it instead.
//
// @param {module:image/imagestyle~ImageStyleOptionDefinition} source
// @param {Object} style
//
// @returns {module:image/imagestyle~ImageStyleOptionDefinition}
function extendStyle( source, style ) {
	const extendedStyle = { ...style };

	for ( const prop in source ) {
		if ( !Object.prototype.hasOwnProperty.call( style, prop ) ) {
			extendedStyle[ prop ] = source[ prop ];
		}
	}

	return extendedStyle;
}

// Displays a console warning with the 'image-style-configuration-definition-invalid' error.
// @param {Object} info
function warnInvalidStyle( info ) {
	/**
	 * The image style definition provided in the configuration is invalid.
	 *
	 * Please make sure the definition implements properly one of the following:
	 *
	 * * {@link module:image/imagestyle~ImageStyleOptionDefinition image style option definition},
	 * * {@link module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition image style dropdown definition}
	 *
	 * @error image-style-configuration-definition-invalid
	 * @param {String} [dropdown] The name of the invalid drop-down
	 * @param {String} [style] The name of the invalid image style option
	 */
	logWarning( 'image-style-configuration-definition-invalid', info );
}

/* harmony default export */ const utils = ({
	normalizeStyles,
	getDefaultStylesConfiguration,
	getDefaultDropdownDefinitions,
	warnInvalidStyle,
	DEFAULT_OPTIONS,
	DEFAULT_ICONS,
	DEFAULT_DROPDOWN_DEFINITIONS
});

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagestyle/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */



/**
 * @module image/imagestyle/converters
 */

/**
 * Returns a converter for the `imageStyle` attribute. It can be used for adding, changing and removing the attribute.
 *
 * @param {Array.<module:image/imagestyle~ImageStyleOptionDefinition>} styles
 * An array containing available image style options.
 * @returns {Function} A model-to-view attribute converter.
 */
function modelToViewStyleAttribute( styles ) {
	return ( evt, data, conversionApi ) => {
		if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
			return;
		}

		// Check if there is class name associated with given value.
		const newStyle = getStyleDefinitionByName( data.attributeNewValue, styles );
		const oldStyle = getStyleDefinitionByName( data.attributeOldValue, styles );

		const viewElement = conversionApi.mapper.toViewElement( data.item );
		const viewWriter = conversionApi.writer;

		if ( oldStyle ) {
			viewWriter.removeClass( oldStyle.className, viewElement );
		}

		if ( newStyle ) {
			viewWriter.addClass( newStyle.className, viewElement );
		}
	};
}

/**
 * Returns a view-to-model converter converting image CSS classes to a proper value in the model.
 *
 * @param {Array.<module:image/imagestyle~ImageStyleOptionDefinition>} styles
 * Image style options for which the converter is created.
 * @returns {Function} A view-to-model converter.
 */
function viewToModelStyleAttribute( styles ) {
	// Convert only non–default styles.
	const nonDefaultStyles = {
		imageInline: styles.filter( style => !style.isDefault && style.modelElements.includes( 'imageInline' ) ),
		imageBlock: styles.filter( style => !style.isDefault && style.modelElements.includes( 'imageBlock' ) )
	};

	return ( evt, data, conversionApi ) => {
		if ( !data.modelRange ) {
			return;
		}

		const viewElement = data.viewItem;
		const modelImageElement = first_first( data.modelRange.getItems() );

		// Run this converter only if an image has been found in the model.
		// In some cases it may not be found (for example if we run this on a figure with different type than image).
		if ( !modelImageElement ) {
			return;
		}

		// ...and the `imageStyle` attribute is allowed for that element, otherwise stop conversion early.
		if ( !conversionApi.schema.checkAttribute( modelImageElement, 'imageStyle' ) ) {
			return;
		}

		// Convert styles one by one.
		for ( const style of nonDefaultStyles[ modelImageElement.name ] ) {
			// Try to consume class corresponding with the style.
			if ( conversionApi.consumable.consume( viewElement, { classes: style.className } ) ) {
				// And convert this style to model attribute.
				conversionApi.writer.setAttribute( 'imageStyle', style.name, modelImageElement );
			}
		}
	};
}

// Returns the style with a given `name` from an array of styles.
//
// @param {String} name
// @param {Array.<module:image/imagestyle~ImageStyleOptionDefinition> } styles
// @returns {module:image/imagestyle~ImageStyleOptionDefinition|undefined}
function getStyleDefinitionByName( name, styles ) {
	for ( const style of styles ) {
		if ( style.name === name ) {
			return style;
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagestyle/imagestyleediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagestyle/imagestyleediting
 */







/**
 * The image style engine plugin. It sets the default configuration, creates converters and registers
 * {@link module:image/imagestyle/imagestylecommand~ImageStyleCommand ImageStyleCommand}.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageStyleEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageStyleEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageUtils ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const { normalizeStyles, getDefaultStylesConfiguration } = utils;
		const editor = this.editor;
		const isBlockPluginLoaded = editor.plugins.has( 'ImageBlockEditing' );
		const isInlinePluginLoaded = editor.plugins.has( 'ImageInlineEditing' );

		editor.config.define( 'image.styles', getDefaultStylesConfiguration( isBlockPluginLoaded, isInlinePluginLoaded ) );

		/**
		 * It contains a list of the normalized and validated style options.
		 *
		 * * Each option contains a complete icon markup.
		 * * The style options not supported by any of the loaded image editing plugins (
		 * {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} or
		 * {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`}) are filtered out.
		 *
		 * @protected
		 * @readonly
		 * @type {module:image/imagestyle~ImageStyleConfig}
		 */
		this.normalizedStyles = normalizeStyles( {
			configuredStyles: editor.config.get( 'image.styles' ),
			isBlockPluginLoaded,
			isInlinePluginLoaded
		} );

		this._setupConversion( isBlockPluginLoaded, isInlinePluginLoaded );
		this._setupPostFixer();

		// Register imageStyle command.
		editor.commands.add( 'imageStyle', new ImageStyleCommand( editor, this.normalizedStyles ) );
	}

	/**
	 * Sets the editor conversion taking the presence of
	 * {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`}
	 * and {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugins into consideration.
	 *
	 * @private
	 * @param {Boolean} isBlockPluginLoaded
	 * @param {Boolean} isInlinePluginLoaded
	 */
	_setupConversion( isBlockPluginLoaded, isInlinePluginLoaded ) {
		const editor = this.editor;
		const schema = editor.model.schema;

		const modelToViewConverter = modelToViewStyleAttribute( this.normalizedStyles );
		const viewToModelConverter = viewToModelStyleAttribute( this.normalizedStyles );

		editor.editing.downcastDispatcher.on( 'attribute:imageStyle', modelToViewConverter );
		editor.data.downcastDispatcher.on( 'attribute:imageStyle', modelToViewConverter );

		// Allow imageStyle attribute in image and imageInline.
		// We could call it 'style' but https://github.com/ckeditor/ckeditor5-engine/issues/559.
		if ( isBlockPluginLoaded ) {
			schema.extend( 'imageBlock', { allowAttributes: 'imageStyle' } );

			// Converter for figure element from view to model.
			editor.data.upcastDispatcher.on( 'element:figure', viewToModelConverter, { priority: 'low' } );
		}

		if ( isInlinePluginLoaded ) {
			schema.extend( 'imageInline', { allowAttributes: 'imageStyle' } );

			// Converter for the img element from view to model.
			editor.data.upcastDispatcher.on( 'element:img', viewToModelConverter, { priority: 'low' } );
		}
	}

	/**
	 * Registers a post-fixer that will make sure that the style attribute value is correct for a specific image type (block vs inline).
	 *
	 * @private
	 */
	_setupPostFixer() {
		const editor = this.editor;
		const document = editor.model.document;

		const imageUtils = editor.plugins.get( ImageUtils );
		const stylesMap = new Map( this.normalizedStyles.map( style => [ style.name, style ] ) );

		// Make sure that style attribute is valid for the image type.
		document.registerPostFixer( writer => {
			let changed = false;

			for ( const change of document.differ.getChanges() ) {
				if ( change.type == 'insert' || change.type == 'attribute' && change.attributeKey == 'imageStyle' ) {
					let element = change.type == 'insert' ? change.position.nodeAfter : change.range.start.nodeAfter;

					if ( element && element.is( 'element', 'paragraph' ) && element.childCount > 0 ) {
						element = element.getChild( 0 );
					}

					if ( !imageUtils.isImage( element ) ) {
						continue;
					}

					const imageStyle = element.getAttribute( 'imageStyle' );

					if ( !imageStyle ) {
						continue;
					}

					const imageStyleDefinition = stylesMap.get( imageStyle );

					if ( !imageStyleDefinition || !imageStyleDefinition.modelElements.includes( element.name ) ) {
						writer.removeAttribute( 'imageStyle', element );
						changed = true;
					}
				}
			}

			return changed;
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-image/theme/imagestyle.css
var imagestyle = __webpack_require__(4622);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/theme/imagestyle.css

            

var imagestyle_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

imagestyle_options.insert = "head";
imagestyle_options.singleton = true;

var imagestyle_update = injectStylesIntoStyleTag_default()(imagestyle/* default */.Z, imagestyle_options);



/* harmony default export */ const theme_imagestyle = (imagestyle/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagestyle/imagestyleui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagestyle/imagestyleui
 */









/**
 * The image style UI plugin.
 *
 * It registers buttons corresponding to the {@link module:image/image~ImageConfig#styles} configuration.
 * It also registers the {@link module:image/imagestyle/utils~DEFAULT_DROPDOWN_DEFINITIONS default drop-downs} and the
 * custom drop-downs defined by the developer in the {@link module:image/image~ImageConfig#toolbar} configuration.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageStyleUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageStyleEditing ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageStyleUI';
	}

	/**
	 * Returns the default localized style titles provided by the plugin.
	 *
	 * The following localized titles corresponding with
	 * {@link module:image/imagestyle/utils~DEFAULT_OPTIONS} are available:
	 *
	 * * `'Wrap text'`,
	 * * `'Break text'`,
	 * * `'In line'`,
	 * * `'Full size image'`,
	 * * `'Side image'`,
	 * * `'Left aligned image'`,
	 * * `'Centered image'`,
	 * * `'Right aligned image'`
	 *
	 * @returns {Object.<String,String>}
	 */
	get localizedDefaultStylesTitles() {
		const t = this.editor.t;

		return {
			'Wrap text': t( 'Wrap text' ),
			'Break text': t( 'Break text' ),
			'In line': t( 'In line' ),
			'Full size image': t( 'Full size image' ),
			'Side image': t( 'Side image' ),
			'Left aligned image': t( 'Left aligned image' ),
			'Centered image': t( 'Centered image' ),
			'Right aligned image': t( 'Right aligned image' )
		};
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const plugins = this.editor.plugins;
		const toolbarConfig = this.editor.config.get( 'image.toolbar' ) || [];

		const definedStyles = translateStyles(
			plugins.get( 'ImageStyleEditing' ).normalizedStyles,
			this.localizedDefaultStylesTitles
		);

		for ( const styleConfig of definedStyles ) {
			this._createButton( styleConfig );
		}

		const definedDropdowns = translateStyles(
			[ ...toolbarConfig.filter( lodash_es_isObject ), ...utils.getDefaultDropdownDefinitions( plugins ) ],
			this.localizedDefaultStylesTitles
		);

		for ( const dropdownConfig of definedDropdowns ) {
			this._createDropdown( dropdownConfig, definedStyles );
		}
	}

	/**
	 * Creates a dropdown and stores it in the editor {@link module:ui/componentfactory~ComponentFactory}.
	 *
	 * @private
	 * @param {module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition} dropdownConfig
	 * @param {Array.<module:image/imagestyle~ImageStyleOptionDefinition>} definedStyles
	 */
	_createDropdown( dropdownConfig, definedStyles ) {
		const factory = this.editor.ui.componentFactory;

		factory.add( dropdownConfig.name, locale => {
			let defaultButton;

			const { defaultItem, items, title } = dropdownConfig;
			const buttonViews = items
				.filter( itemName => definedStyles.find( ( { name } ) => getUIComponentName( name ) === itemName ) )
				.map( buttonName => {
					const button = factory.create( buttonName );

					if ( buttonName === defaultItem ) {
						defaultButton = button;
					}

					return button;
				} );

			if ( items.length !== buttonViews.length ) {
				utils.warnInvalidStyle( { dropdown: dropdownConfig } );
			}

			const dropdownView = createDropdown( locale, SplitButtonView );
			const splitButtonView = dropdownView.buttonView;
			const splitButtonViewArrow = splitButtonView.arrowView;

			addToolbarToDropdown( dropdownView, buttonViews, { enableActiveItemFocusOnDropdownOpen: true } );

			splitButtonView.set( {
				label: getDropdownButtonTitle( title, defaultButton.label ),
				class: null,
				tooltip: true
			} );

			splitButtonViewArrow.unbind( 'label' );
			splitButtonViewArrow.set( {
				label: title
			} );

			splitButtonView.bind( 'icon' ).toMany( buttonViews, 'isOn', ( ...areOn ) => {
				const index = areOn.findIndex( lodash_es_identity );

				return ( index < 0 ) ? defaultButton.icon : buttonViews[ index ].icon;
			} );

			splitButtonView.bind( 'label' ).toMany( buttonViews, 'isOn', ( ...areOn ) => {
				const index = areOn.findIndex( lodash_es_identity );

				return getDropdownButtonTitle( title, ( index < 0 ) ? defaultButton.label : buttonViews[ index ].label );
			} );

			splitButtonView.bind( 'isOn' ).toMany( buttonViews, 'isOn', ( ...areOn ) => areOn.some( lodash_es_identity ) );

			splitButtonView.bind( 'class' )
				.toMany( buttonViews, 'isOn', ( ...areOn ) => areOn.some( lodash_es_identity ) ? 'ck-splitbutton_flatten' : null );

			splitButtonView.on( 'execute', () => {
				if ( !buttonViews.some( ( { isOn } ) => isOn ) ) {
					defaultButton.fire( 'execute' );
				} else {
					dropdownView.isOpen = !dropdownView.isOpen;
				}
			} );

			dropdownView.bind( 'isEnabled' )
				.toMany( buttonViews, 'isEnabled', ( ...areEnabled ) => areEnabled.some( lodash_es_identity ) );

			// Focus the editable after executing the command.
			// Overrides a default behaviour where the focus is moved to the dropdown button (#12125).
			this.listenTo( dropdownView, 'execute', () => {
				this.editor.editing.view.focus();
			} );

			return dropdownView;
		} );
	}

	/**
	 * Creates a button and stores it in the editor {@link module:ui/componentfactory~ComponentFactory}.
	 *
	 * @private
	 * @param {module:image/imagestyle~ImageStyleOptionDefinition} buttonConfig
	 */
	_createButton( buttonConfig ) {
		const buttonName = buttonConfig.name;

		this.editor.ui.componentFactory.add( getUIComponentName( buttonName ), locale => {
			const command = this.editor.commands.get( 'imageStyle' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: buttonConfig.title,
				icon: buttonConfig.icon,
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isEnabled' ).to( command, 'isEnabled' );
			view.bind( 'isOn' ).to( command, 'value', value => value === buttonName );
			view.on( 'execute', this._executeCommand.bind( this, buttonName ) );

			return view;
		} );
	}

	_executeCommand( name ) {
		this.editor.execute( 'imageStyle', { value: name } );
		this.editor.editing.view.focus();
	}
}

// Returns the translated `title` from the passed styles array.
//
// @param {Array.<module:image/imagestyle~ImageStyleOptionDefinition|
// module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition>} styles
// @param {Object.<String,String>} titles
//
// @returns {Array.<module:image/imagestyle~ImageStyleOptionDefinition|module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition>}
function translateStyles( styles, titles ) {
	for ( const style of styles ) {
		// Localize the titles of the styles, if a title corresponds with
		// a localized default provided by the plugin.
		if ( titles[ style.title ] ) {
			style.title = titles[ style.title ];
		}
	}

	return styles;
}

// Returns the image style component name with the "imageStyle:" prefix.
//
// @param {String} name
// @returns {String}
function getUIComponentName( name ) {
	return `imageStyle:${ name }`;
}

// Returns title for the splitbutton containing the dropdown title and default action item title.
//
// @param {String|undefined} dropdownTitle
// @param {String} buttonTitle
// @returns {String}
function getDropdownButtonTitle( dropdownTitle, buttonTitle ) {
	return ( dropdownTitle ? dropdownTitle + ': ' : '' ) + buttonTitle;
}

/**
 * # **The image style custom drop-down definition descriptor**
 *
 * This definition can be implemented in the {@link module:image/image~ImageConfig#toolbar image toolbar configuration}
 * to define a completely custom drop-down in the image toolbar.
 *
 *		ClassicEditor.create( editorElement, {
 *			image: { toolbar: [
 *	 			// One of the predefined drop-downs
 *	 			'imageStyle:wrapText',
 *				// Custom drop-down
 *				{
 *					name: 'imageStyle:customDropdown',
 *					title: Custom drop-down title,
 *					items: [ 'imageStyle:alignLeft', 'imageStyle:alignRight' ],
 *					defaultItem: 'imageStyle:alignLeft'
 *				}
 *			] }
 *		} );
 *
 * **Note:** At the moment it is possible to populate the custom drop-down with only the buttons registered by the `ImageStyle` plugin.
 *
 * The defined drop-down will be registered
 * as the {@link module:ui/dropdown/dropdownview~DropdownView}
 * with the {@link module:ui/dropdown/button/splitbuttonview~SplitButtonView} under the provided name in the
 * {@link module:ui/componentfactory~ComponentFactory}
 *
 * @property {String} name The unique name of the drop-down. It is recommended to precede it with the "imageStyle:" prefix
 * to avoid collision with the components' names registered by other plugins.
 *
 * @property {String} [title] The drop-down's title. It will be used as the split button label along with the title of the default item
 * in the following manner: "Custom drop-down title: Default item title".
 *
 * Setting `title` to one of
 * {@link module:image/imagestyle/imagestyleui~ImageStyleUI#localizedDefaultStylesTitles}
 * will automatically translate it to the language of the editor.
 *
 * @property {Array.<String>} items The list of the names of the buttons that will be placed in the drop-down's toolbar.
 * Each of the buttons has to be one of the {@link module:image/image~ImageConfig#styles default image style buttons}
 * or to be defined as the {@link module:image/imagestyle~ImageStyleOptionDefinition image styling option}.
 *
 * @property {String} defaultItem The name of one of the buttons from the items list,
 * which will be used as a default button for the drop-down's split button.
 *
 * @typedef {Object} module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagestyle.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagestyle
 */





/**
 * The image style plugin.
 *
 * For a detailed overview of the image styles feature, check the {@glink features/images/images-styles documentation}.
 *
 * This is a "glue" plugin which loads the following plugins:
 * * {@link module:image/imagestyle/imagestyleediting~ImageStyleEditing},
 * * {@link module:image/imagestyle/imagestyleui~ImageStyleUI}
 *
 * It provides a default configuration, which can be extended or overwritten.
 * Read more about the {@link module:image/image~ImageConfig#styles image styles configuration}.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageStyle extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ImageStyleEditing, ImageStyleUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageStyle';
	}
}

/**
 * The configuration for the {@link module:image/imagestyle~ImageStyle} plugin that should be provided
 * while creating the editor instance.
 *
 * A detailed information about the default configuration and customization can be found in
 * {@link module:image/image~ImageConfig#styles `ImageConfig#styles`}.
 *
 * @interface ImageStyleConfig
 */

/**
 * A list of the image style options.
 *
 * @member {Array.<module:image/imagestyle~ImageStyleOptionDefinition>} module:image/imagestyle~ImageStyleConfig#options
 */

/**
 * The {@link module:image/imagestyle `ImageStyle`} plugin requires a list of the
 * {@link module:image/imagestyle~ImageStyleConfig#options image style options} to work properly.
 * The default configuration is provided (listed below) and can be customized while creating the editor instance.
 *
 * # **Command**
 *
 * The {@link module:image/imagestyle/imagestylecommand~ImageStyleCommand `imageStyleCommand`}
 * is configured based on the defined options,
 * so you can change the style of the selected image by executing the following command:
 *
 *		editor.execute( 'imageStyle' { value: 'alignLeft' } );
 *
 * # **Buttons**
 *
 * All of the image style options provided in the configuration are registered
 * in the {@link module:ui/componentfactory~ComponentFactory UI components factory} with the "imageStyle:" prefixes and can be used
 * in the {@link module:image/image~ImageConfig#toolbar image toolbar configuration}. The buttons available by default depending
 * on the loaded plugins are listed in the next section.
 *
 * Read more about styling images in the {@glink features/images/images-styles Image styles guide}.
 *
 * # **Default options and buttons**
 *
 * If the custom configuration is not provided, the default configuration will be used depending on the loaded
 * image editing plugins.
 *
 * * If both {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} and
 * {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugins are loaded
 * (which is usually the default editor configuration), the following options will be available for the toolbar
 * configuration. These options will be registered as the buttons with the "imageStyle:" prefixes.
 *
 *		const imageDefaultConfig = {
 *			styles: {
 *				options: [
 *					'inline', 'alignLeft', 'alignRight',
 *					'alignCenter', 'alignBlockLeft', 'alignBlockRight',
 *					'block', 'side'
 *				]
 *			}
 *		};
 *
 * * If only the {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin is loaded,
 * the following buttons (options) and groups will be available for the toolbar configuration.
 * These options will be registered as the buttons with the "imageStyle:" prefixes.
 *
 *		const imageDefaultConfig = {
 *			styles: {
 *				options: [ 'block', 'side' ]
 *			}
 *		};
 *
 * * If only the {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin is loaded,
 * the following buttons (options) and groups will available for the toolbar configuration.
 * These options will be registered as the buttons with the "imageStyle:" prefixes.
 *
 *		const imageDefaultConfig = {
 *			styles: {
 *				options: [ 'inline', 'alignLeft', 'alignRight' ]
 *			}
 *		};
 *
 * Read more about the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options}.
 *
 * # **Custom configuration**
 *
 * The image styles configuration can be customized in several ways:
 *
 * * Any of the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options}
 * can be loaded by the reference to its name as follows:
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				image: {
 *					styles: {
 *						options: [ 'alignLeft', 'alignRight' ]
 *					}
 *				}
 *			} );
 *
 * * Each of the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default image style options} can be customized,
 * e.g. to change the `icon`, `title` or CSS `className` of the style. The feature also provides several
 * {@link module:image/imagestyle/utils~DEFAULT_ICONS default icons} to choose from.
 *
 *		import customIcon from 'custom-icon.svg';
 *
 *		// ...
 *
 *		ClassicEditor.create( editorElement, { image:
 *			styles: {
 *				options: {
 *					// This will only customize the icon of the "block" style.
 *					// Note: 'right' is one of default icons provided by the feature.
 *					{
 *						name: 'block',
 *						icon: 'right'
 *					},
 *
 *					// This will customize the icon, title and CSS class of the default "side" style.
 *					{
 *						name: 'side',
 *						icon: customIcon,
 *						title: 'My side style',
 *						className: 'custom-side-image'
 *					}
 *				}
 *			}
 *		} );
 *
 * * If none of the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default image style options}
 * works for the integration, it is possible to define independent custom styles, too.
 *
 * See the documentation about the image style {@link module:image/imagestyle~ImageStyleOptionDefinition options}
 * to define the custom image style configuration properly.
 *
 *		import redIcon from 'red-icon.svg';
 *		import blueIcon from 'blue-icon.svg';
 *
 *		// ...
 *
 *		ClassicEditor.create( editorElement, { image:
 *			styles: {
 *				// A list of completely custom styling options.
 *				options: [
 *					{
 *						name: 'regular',
 *						modelElements: [ 'imageBlock', 'imageInline' ],
 *						title: 'Regular image',
 *						icon: 'full',
 *						isDefault: true
 *					}, {
 *						name: 'blue',
 *						modelElements: [ 'imageInline' ],
 *						title: 'Blue image',
 *						icon: blueIcon,
 *						className: 'image-blue'
 *					}, {
 *						name: 'red',
 *						modelElements: [ 'imageBlock' ],
 *						title: 'Red image',
 *						icon: redIcon,
 *						className: 'image-red'
 *					}
 *				]
 *			}
 *		} );
 *
 * @member {module:image/imagestyle~ImageStyleConfig} module:image/image~ImageConfig#styles
 */

/**
 * The image styling option definition descriptor.
 *
 * This definition should be implemented in the `Image` plugin {@link module:image/image~ImageConfig#styles configuration} for:
 *
 * * customizing one of the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options} by providing the proper name
 * of the default style and the properties that should be overridden,
 * * or defining a completely custom styling option by providing a custom name and implementing the following properties.
 *
 *		import fullSizeIcon from 'path/to/icon.svg';
 *
 *		const imageStyleOptionDefinition = {
 *			name: 'fullSize',
 *			icon: fullSizeIcon,
 *			title: 'Full size image',
 *			className: 'image-full-size',
 *			modelElements: [ 'imageBlock', 'imageInline' ]
 *		}
 *
 * The styling option will be registered as the button under the name `'imageStyle:{name}'` in the
 * {@link module:ui/componentfactory~ComponentFactory UI components factory} (this functionality is provided by the
 * {@link module:image/imagestyle/imagestyleui~ImageStyleUI} plugin).
 *
 * @property {String} name The unique name of the styling option. It will be used to:
 *
 * * refer to one of the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options} or define the custom style,
 * * store the chosen style in the model by setting the `imageStyle` attribute of the model image element,
 * * as a value of the {@link module:image/imagestyle/imagestylecommand~ImageStyleCommand#execute `imageStyle` command},
 * * when registering a button for the style in the following manner: (`'imageStyle:{name}'`).
 *
 * @property {Boolean} [isDefault] When set, the style will be used as the default one for the model elements
 * listed in the `modelElements` property. A default style does not apply any CSS class to the view element.
 *
 * If this property is not defined, its value is inherited
 * from the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options} addressed in the name property.
 *
 * @property {String} icon One of the following to be used when creating the styles's button:
 *
 * * an SVG icon source (as an XML string),
 * * one of the keys in {@link module:image/imagestyle/utils~DEFAULT_ICONS} to use one of default icons provided by the plugin.
 *
 * If this property is not defined, its value is inherited
 * from the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options} addressed in the name property.
 *
 * @property {String} title The styles's title. Setting `title` to one of
 * {@link module:image/imagestyle/imagestyleui~ImageStyleUI#localizedDefaultStylesTitles}
 * will automatically translate it to the language of the editor.
 *
 * If this property is not defined, its value is inherited
 * from the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options} addressed in the name property.
 *
 * @property {String} [className] The CSS class used to represent the style in the view.
 * It should be used only for the non-default styles.
 *
 * If this property is not defined, its value is inherited
 * from the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options} addressed in the name property.
 *
 * @property {Array.<String>} modelElements The list of the names of the model elements that are supported by the style.
 * The possible values are:
 * * `[ 'imageBlock' ]` if the style can be applied to the image type introduced by the
 * {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin,
 * * `[ 'imageInline' ]` if the style can be applied to the image type introduced by the
 * {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin,
 * * `[ 'imageInline', 'imageBlock' ]` if the style can be applied to both image types introduced by the plugins mentioned above.
 *
 * This property determines which model element names work with the style. If the model element name of the currently selected
 * image is different, upon executing the
 * {@link module:image/imagestyle/imagestylecommand~ImageStyleCommand#execute `imageStyle`} command the image type (model element name)
 * will automatically change.
 *
 * If this property is not defined, its value is inherited
 * from the {@link module:image/imagestyle/utils~DEFAULT_OPTIONS default styling options} addressed in the name property.
 *
 * @typedef {Object} module:image/imagestyle~ImageStyleOptionDefinition
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-image/src/imagetoolbar.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module image/imagetoolbar
 */






/**
 * The image toolbar plugin. It creates and manages the image toolbar (the toolbar displayed when an image is selected).
 *
 * For an overview, check the {@glink features/images/images-overview#image-contextual-toolbar image contextual toolbar} documentation.
 *
 * Instances of toolbar components (e.g. buttons) are created using the editor's
 * {@link module:ui/componentfactory~ComponentFactory component factory}
 * based on the {@link module:image/image~ImageConfig#toolbar `image.toolbar` configuration option}.
 *
 * The toolbar uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon}.
 *
 * @extends module:core/plugin~Plugin
 */
class ImageToolbar extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ WidgetToolbarRepository, ImageUtils ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ImageToolbar';
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const editor = this.editor;
		const t = editor.t;
		const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
		const imageUtils = editor.plugins.get( 'ImageUtils' );

		widgetToolbarRepository.register( 'image', {
			ariaLabel: t( 'Image toolbar' ),
			items: normalizeDeclarativeConfig( editor.config.get( 'image.toolbar' ) || [] ),
			getRelatedElement: selection => imageUtils.getClosestSelectedImageWidget( selection )
		} );
	}
}

/**
 * Items to be placed in the image toolbar.
 * This option is used by the {@link module:image/imagetoolbar~ImageToolbar} feature.
 *
 * Assuming that you use the following features:
 *
 * * {@link module:image/imagestyle~ImageStyle} (with a default configuration),
 * * {@link module:image/imagetextalternative~ImageTextAlternative},
 * * {@link module:image/imagecaption~ImageCaption},
 *
 * the following toolbar items will be available in {@link module:ui/componentfactory~ComponentFactory}:
 * * `'imageTextAlternative'`,
 * * `'toggleImageCaption'`,
 * * {@link module:image/image~ImageConfig#styles buttons provided by the `ImageStyle` plugin},
 * * {@link module:image/imagestyle/utils~DEFAULT_DROPDOWN_DEFINITIONS drop-downs provided by the `ImageStyle` plugin},
 *
 * so you can configure the toolbar like this:
 *
 *		const imageConfig = {
 *			toolbar: [
 *	 			'imageStyle:inline', 'imageStyle:wrapText', 'imageStyle:breakText', '|',
 *				'toggleImageCaption', 'imageTextAlternative'
 *			]
 *		};
 *
 * Besides that, the `ImageStyle` plugin allows to define a
 * {@link module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition custom drop-down} while configuring the toolbar.
 *
 * The same items can also be used in the {@link module:core/editor/editorconfig~EditorConfig#toolbar main editor toolbar}.
 *
 * Read more about configuring toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
 *
 * @member {Array.<String>} module:image/image~ImageConfig#toolbar
 */

// Convert the dropdown definitions to their keys registered in the ComponentFactory.
// The registration precess should be handled by the plugin which handles the UI of a particular feature.
//
// @param {Array.<String|module:image/imagestyle/imagestyleui~ImageStyleDropdownDefinition>} config
//
// @returns {Array.<String>}
function normalizeDeclarativeConfig( config ) {
	return config.map( item => lodash_es_isObject( item ) ? item.name : item );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/src/indentediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module indent/indentediting
 */



/**
 * The indent editing feature.
 *
 * This plugin registers the `'indent'` and `'outdent'` commands.
 *
 * **Note**: In order for the commands to work, at least one of the compatible features is required. Read more in the
 * {@link module:indent/indent~Indent indent feature} API documentation.
 *
 * @extends module:core/plugin~Plugin
 */
class IndentEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'IndentEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		editor.commands.add( 'indent', new MultiCommand( editor ) );
		editor.commands.add( 'outdent', new MultiCommand( editor ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/theme/icons/indent.svg
/* harmony default export */ const indent = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 3.75c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm5 6c0 .414.336.75.75.75h9.5a.75.75 0 1 0 0-1.5h-9.5a.75.75 0 0 0-.75.75zM2.75 16.5h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 1 0 0 1.5zM1.632 6.95 5.02 9.358a.4.4 0 0 1-.013.661l-3.39 2.207A.4.4 0 0 1 1 11.892V7.275a.4.4 0 0 1 .632-.326z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/theme/icons/outdent.svg
/* harmony default export */ const outdent = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 3.75c0 .414.336.75.75.75h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 0 0-.75.75zm5 6c0 .414.336.75.75.75h9.5a.75.75 0 1 0 0-1.5h-9.5a.75.75 0 0 0-.75.75zM2.75 16.5h14.5a.75.75 0 1 0 0-1.5H2.75a.75.75 0 1 0 0 1.5zm1.618-9.55L.98 9.358a.4.4 0 0 0 .013.661l3.39 2.207A.4.4 0 0 0 5 11.892V7.275a.4.4 0 0 0-.632-.326z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/src/indentui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module indent/indentui
 */






/**
 * The indent UI feature.
 *
 * This plugin registers the `'indent'` and `'outdent'` buttons.
 *
 * **Note**: In order for the commands to work, at least one of the compatible features is required. Read more in
 * the {@link module:indent/indent~Indent indent feature} API documentation.
 *
 * @extends module:core/plugin~Plugin
 */
class IndentUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'IndentUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const locale = editor.locale;
		const t = editor.t;

		const localizedIndentIcon = locale.uiLanguageDirection == 'ltr' ? indent : outdent;
		const localizedOutdentIcon = locale.uiLanguageDirection == 'ltr' ? outdent : indent;

		this._defineButton( 'indent', t( 'Increase indent' ), localizedIndentIcon );
		this._defineButton( 'outdent', t( 'Decrease indent' ), localizedOutdentIcon );
	}

	/**
	 * Defines a UI button.
	 *
	 * @param {String} commandName
	 * @param {String} label
	 * @param {String} icon
	 * @private
	 */
	_defineButton( commandName, label, icon ) {
		const editor = this.editor;

		editor.ui.componentFactory.add( commandName, locale => {
			const command = editor.commands.get( commandName );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label,
				icon,
				tooltip: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			this.listenTo( view, 'execute', () => {
				editor.execute( commandName );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/src/indent.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module indent/indent
 */






/**
 * The indent feature.
 *
 * This plugin acts as a single entry point plugin for other features that implement indentation of elements like lists or paragraphs.
 *
 * The compatible features are:
 *
 * * The {@link module:list/list~List} or {@link module:list/list/listediting~ListEditing} feature for list indentation.
 * * The {@link module:indent/indentblock~IndentBlock} feature for block indentation.
 *
 * This is a "glue" plugin that loads the following plugins:
 *
 * * The {@link module:indent/indentediting~IndentEditing indent editing feature}.
 * * The {@link module:indent/indentui~IndentUI indent UI feature}.
 *
 * The dependent plugins register the `'indent'` and `'outdent'` commands and introduce the `'indent'` and `'outdent'` buttons
 * that allow to increase or decrease text indentation of supported elements.
 *
 * **Note**: In order for the commands and buttons to work, at least one of compatible features is required.
 *
 * @extends module:core/plugin~Plugin
 */
class Indent extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Indent';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ IndentEditing, IndentUI ];
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/src/indentblockcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module indent/indentblockcommand
 */




/**
 * The indent block command.
 *
 * The command is registered by the {@link module:indent/indentblock~IndentBlock} as `'indentBlock'` for indenting blocks and
 * `'outdentBlock'` for outdenting blocks.
 *
 * To increase block indentation at the current selection, execute the command:
 *
 *		editor.execute( 'indentBlock' );
 *
 * To decrease block indentation at the current selection, execute the command:
 *
 *		editor.execute( 'outdentBlock' );
 *
 * @extends module:core/command~Command
 */
class IndentBlockCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {module:indent/indentblockcommand~IndentBehavior} indentBehavior
	 */
	constructor( editor, indentBehavior ) {
		super( editor );

		/**
		 * The command's indentation behavior.
		 *
		 * @type {module:indent/indentblockcommand~IndentBehavior}
		 * @private
		 */
		this._indentBehavior = indentBehavior;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		// Check whether any of the position's ancestors is a list item.
		const editor = this.editor;
		const model = editor.model;

		const block = first_first( model.document.selection.getSelectedBlocks() );

		if ( !block || !model.schema.checkAttribute( block, 'blockIndent' ) ) {
			this.isEnabled = false;

			return;
		}

		this.isEnabled = this._indentBehavior.checkEnabled( block.getAttribute( 'blockIndent' ) );
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const model = this.editor.model;

		const blocksToChange = getBlocksToChange( model );

		model.change( writer => {
			for ( const block of blocksToChange ) {
				const currentIndent = block.getAttribute( 'blockIndent' );

				const nextIndent = this._indentBehavior.getNextIndent( currentIndent );

				if ( nextIndent ) {
					writer.setAttribute( 'blockIndent', nextIndent, block );
				} else {
					writer.removeAttribute( 'blockIndent', block );
				}
			}
		} );
	}
}

// Returns blocks from selection that should have blockIndent selection set.
//
// @param {module:engine/model/model~model} model A model.
function getBlocksToChange( model ) {
	const selection = model.document.selection;
	const schema = model.schema;
	const blocksInSelection = Array.from( selection.getSelectedBlocks() );

	return blocksInSelection.filter( block => schema.checkAttribute( block, 'blockIndent' ) );
}

/**
 * Provides indentation behavior to {@link module:indent/indentblockcommand~IndentBlockCommand}.
 *
 * @interface module:indent/indentblockcommand~IndentBehavior
 */

/**
 * Checks if the command should be enabled.
 *
 * @method #checkEnabled
 * @param {String} indentAttributeValue The current indent attribute value.
 * @returns {Boolean}
 */

/**
 * Returns a new indent attribute value based on the current indent. This method returns `undefined` when the indentation should be removed.
 *
 * @method #getNextIndent
 * @param {String} indentAttributeValue The current indent attribute value.
 * @returns {String|undefined}
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/src/indentcommandbehavior/indentusingoffset.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module indent/indentcommandbehavior/indentusingoffset
 */

/**
 * The block indentation behavior that uses offsets to set indentation.
 *
 * @implements module:indent/indentblockcommand~IndentBehavior
 */
class IndentUsingOffset {
	/**
	 * Creates an instance of the indentation behavior.
	 *
	 * @param {Object} config
	 * @param {String} config.direction The direction of indentation.
	 * @param {Number} config.offset The offset of the next indentation step.
	 * @param {String} config.unit Indentation unit.
	 */
	constructor( config ) {
		/**
		 * The direction of indentation.
		 *
		 * @type {Boolean}
		 */
		this.isForward = config.direction === 'forward';

		/**
		 * The offset of the next indentation step.
		 *
		 * @type {Number}
		 */
		this.offset = config.offset;

		/**
		 * Indentation unit.
		 *
		 * @type {String}
		 */
		this.unit = config.unit;
	}

	/**
	 * @inheritDoc
	 */
	checkEnabled( indentAttributeValue ) {
		const currentOffset = parseFloat( indentAttributeValue || 0 );

		// The command is always enabled for forward indentation.
		return this.isForward || currentOffset > 0;
	}

	/**
	 * @inheritDoc
	 */
	getNextIndent( indentAttributeValue ) {
		const currentOffset = parseFloat( indentAttributeValue || 0 );
		const isSameUnit = !indentAttributeValue || indentAttributeValue.endsWith( this.unit );

		if ( !isSameUnit ) {
			return this.isForward ? this.offset + this.unit : undefined;
		}

		const nextOffset = this.isForward ? this.offset : -this.offset;

		const offsetToSet = currentOffset + nextOffset;

		return offsetToSet > 0 ? offsetToSet + this.unit : undefined;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/src/indentcommandbehavior/indentusingclasses.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module indent/indentcommandbehavior/indentusingclasses
 */

/**
 * The block indentation behavior that uses classes to set indentation.
 *
 * @implements module:indent/indentblockcommand~IndentBehavior
 */
class IndentUsingClasses {
	/**
	 * Creates an instance of the indentation behavior.
	 *
	 * @param {Object} config
	 * @param {String} config.direction The direction of indentation.
	 * @param {Array.<String>} config.classes A list of classes used for indentation.
	 */
	constructor( config ) {
		/**
		 * The direction of indentation.
		 *
		 * @type {Boolean}
		 */
		this.isForward = config.direction === 'forward';

		/**
		 * A list of classes used for indentation.
		 *
		 * @type {Array.<String>}
		 */
		this.classes = config.classes;
	}

	/**
	 * @inheritDoc
	 */
	checkEnabled( indentAttributeValue ) {
		const currentIndex = this.classes.indexOf( indentAttributeValue );

		if ( this.isForward ) {
			return currentIndex < this.classes.length - 1;
		} else {
			return currentIndex >= 0;
		}
	}

	/**
	 * @inheritDoc
	 */
	getNextIndent( indentAttributeValue ) {
		const currentIndex = this.classes.indexOf( indentAttributeValue );
		const indexStep = this.isForward ? 1 : -1;

		return this.classes[ currentIndex + indexStep ];
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-indent/src/indentblock.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module indent/indentblock
 */








const DEFAULT_ELEMENTS = [ 'paragraph', 'heading1', 'heading2', 'heading3', 'heading4', 'heading5', 'heading6' ];

/**
 * The block indentation feature.
 *
 * It registers the `'indentBlock'` and `'outdentBlock'` commands.
 *
 * If the plugin {@link module:indent/indent~Indent} is defined, it also attaches the `'indentBlock'` and `'outdentBlock'` commands to
 * the `'indent'` and `'outdent'` commands.
 *
 * @extends module:core/plugin~Plugin
 */
class IndentBlock extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'indentBlock', {
			offset: 40,
			unit: 'px'
		} );
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'IndentBlock';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const configuration = editor.config.get( 'indentBlock' );

		const useOffsetConfig = !configuration.classes || !configuration.classes.length;

		const indentConfig = Object.assign( { direction: 'forward' }, configuration );
		const outdentConfig = Object.assign( { direction: 'backward' }, configuration );

		if ( useOffsetConfig ) {
			editor.data.addStyleProcessorRules( addMarginRules );
			this._setupConversionUsingOffset( editor.conversion );

			editor.commands.add( 'indentBlock', new IndentBlockCommand( editor, new IndentUsingOffset( indentConfig ) ) );
			editor.commands.add( 'outdentBlock', new IndentBlockCommand( editor, new IndentUsingOffset( outdentConfig ) ) );
		} else {
			this._setupConversionUsingClasses( configuration.classes );
			editor.commands.add( 'indentBlock', new IndentBlockCommand( editor, new IndentUsingClasses( indentConfig ) ) );
			editor.commands.add( 'outdentBlock', new IndentBlockCommand( editor, new IndentUsingClasses( outdentConfig ) ) );
		}
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const editor = this.editor;
		const schema = editor.model.schema;

		const indentCommand = editor.commands.get( 'indent' );
		const outdentCommand = editor.commands.get( 'outdent' );

		// Enable block indentation to heading configuration options. If it is not defined enable in paragraph and default headings.
		const options = editor.config.get( 'heading.options' );
		const configuredElements = options && options.map( option => option.model );
		const knownElements = configuredElements || DEFAULT_ELEMENTS;

		knownElements.forEach( elementName => {
			if ( schema.isRegistered( elementName ) ) {
				schema.extend( elementName, { allowAttributes: 'blockIndent' } );
			}
		} );

		schema.setAttributeProperties( 'blockIndent', { isFormatting: true } );

		indentCommand.registerChildCommand( editor.commands.get( 'indentBlock' ) );
		outdentCommand.registerChildCommand( editor.commands.get( 'outdentBlock' ) );
	}

	/**
	 * Setups conversion for using offset indents.
	 *
	 * @private
	 */
	_setupConversionUsingOffset() {
		const conversion = this.editor.conversion;
		const locale = this.editor.locale;
		const marginProperty = locale.contentLanguageDirection === 'rtl' ? 'margin-right' : 'margin-left';

		conversion.for( 'upcast' ).attributeToAttribute( {
			view: {
				styles: {
					[ marginProperty ]: /[\s\S]+/
				}
			},
			model: {
				key: 'blockIndent',
				value: viewElement => viewElement.getStyle( marginProperty )
			}
		} );

		conversion.for( 'downcast' ).attributeToAttribute( {
			model: 'blockIndent',
			view: modelAttributeValue => {
				return {
					key: 'style',
					value: {
						[ marginProperty ]: modelAttributeValue
					}
				};
			}
		} );
	}

	/**
	 * Setups conversion for using classes.
	 *
	 * @param {Array.<String>} classes
	 * @private
	 */
	_setupConversionUsingClasses( classes ) {
		const definition = {
			model: {
				key: 'blockIndent',
				values: []
			},
			view: {}
		};

		for ( const className of classes ) {
			definition.model.values.push( className );
			definition.view[ className ] = {
				key: 'class',
				value: [ className ]
			};
		}

		this.editor.conversion.attributeToAttribute( definition );
	}
}

/**
 * The configuration of the {@link module:indent/indentblock~IndentBlock block indentation feature}.
 *
 * Read more in {@link module:indent/indentblock~IndentBlockConfig}.
 *
 * @member {module:indent/indentblock~IndentBlockConfig} module:core/editor/editorconfig~EditorConfig#indentBlock
 */

/**
 * The configuration of the block indentation feature.
 *
 * If no {@link module:indent/indentblock~IndentBlockConfig#classes} are set, the block indentation feature will use
 * {@link module:indent/indentblock~IndentBlockConfig#offset} and {@link module:indent/indentblock~IndentBlockConfig#unit} to
 * create indentation steps.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				indentBlock: {
 *					offset: 2,
 *					unit: 'em'
 * 				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * Alternatively, the block indentation feature may set one of defined {@link module:indent/indentblock~IndentBlockConfig#classes} as
 * indentation steps:
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				indentBlock: {
 *					classes: [
 *						'indent-a', // The first step - smallest indentation.
 *						'indent-b',
 *						'indent-c',
 *						'indent-d',
 *						'indent-e' // The last step - biggest indentation.
 *					]
 * 				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * In the example above only 5 indentation steps will be available.
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface IndentBlockConfig
 */

/**
 * The size of indentation {@link module:indent/indentblock~IndentBlockConfig#unit units} for each indentation step.
 *
 * @default 40
 * @member {Number} module:indent/indentblock~IndentBlockConfig#offset
 */

/**
 * The unit used for indentation {@link module:indent/indentblock~IndentBlockConfig#offset}.
 *
 * @default 'px'
 * @member {String} module:indent/indentblock~IndentBlockConfig#unit
 */

/**
 * An optional list of classes to use for indenting the editor content. If not set or set to an empty array, no classes will be used.
 * The {@link module:indent/indentblock~IndentBlockConfig#unit `indentBlock.unit`} and
 * {@link module:indent/indentblock~IndentBlockConfig#offset `indentBlock.offset`} properties will be used instead.
 *
 * @default undefined
 * @member {Array.<String>|undefined} module:indent/indentblock~IndentBlockConfig#classes
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/italic/italicediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/italic/italicediting
 */




const ITALIC = 'italic';

/**
 * The italic editing feature.
 *
 * It registers the `'italic'` command, the <kbd>Ctrl+I</kbd> keystroke and introduces the `italic` attribute in the model
 * which renders to the view as an `<i>` element.
 *
 * @extends module:core/plugin~Plugin
 */
class ItalicEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ItalicEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow italic attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: ITALIC } );
		editor.model.schema.setAttributeProperties( ITALIC, {
			isFormatting: true,
			copyOnEnter: true
		} );

		editor.conversion.attributeToElement( {
			model: ITALIC,
			view: 'i',
			upcastAlso: [
				'em',
				{
					styles: {
						'font-style': 'italic'
					}
				}
			]
		} );

		// Create italic command.
		editor.commands.add( ITALIC, new AttributeCommand( editor, ITALIC ) );

		// Set the Ctrl+I keystroke.
		editor.keystrokes.set( 'CTRL+I', ITALIC );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/italic.svg
/* harmony default export */ const italic = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m9.586 14.633.021.004c-.036.335.095.655.393.962.082.083.173.15.274.201h1.474a.6.6 0 1 1 0 1.2H5.304a.6.6 0 0 1 0-1.2h1.15c.474-.07.809-.182 1.005-.334.157-.122.291-.32.404-.597l2.416-9.55a1.053 1.053 0 0 0-.281-.823 1.12 1.12 0 0 0-.442-.296H8.15a.6.6 0 0 1 0-1.2h6.443a.6.6 0 1 1 0 1.2h-1.195c-.376.056-.65.155-.823.296-.215.175-.423.439-.623.79l-2.366 9.347z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/italic/italicui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/italic/italicui
 */






const italicui_ITALIC = 'italic';

/**
 * The italic UI feature. It introduces the Italic button.
 *
 * @extends module:core/plugin~Plugin
 */
class ItalicUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ItalicUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add bold button to feature components.
		editor.ui.componentFactory.add( italicui_ITALIC, locale => {
			const command = editor.commands.get( italicui_ITALIC );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Italic' ),
				icon: italic,
				keystroke: 'CTRL+I',
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( italicui_ITALIC );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/italic.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/italic
 */





/**
 * The italic feature.
 *
 * For a detailed overview check the {@glink features/basic-styles Basic styles feature documentation}
 * and the {@glink api/basic-styles package page}.
 *
 * This is a "glue" plugin which loads the {@link module:basic-styles/italic/italicediting~ItalicEditing} and
 * {@link module:basic-styles/italic/italicui~ItalicUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Italic extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ItalicEditing, ItalicUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Italic';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/utils/automaticdecorators.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/utils
 */



/**
 * Helper class that ties together all {@link module:link/link~LinkDecoratorAutomaticDefinition} and provides
 * the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement downcast dispatchers} for them.
 */
class AutomaticDecorators {
	constructor() {
		/**
		 * Stores the definition of {@link module:link/link~LinkDecoratorAutomaticDefinition automatic decorators}.
		 * This data is used as a source for a downcast dispatcher to create a proper conversion to output data.
		 *
		 * @private
		 * @type {Set}
		 */
		this._definitions = new Set();
	}

	/**
	 * Gives information about the number of decorators stored in the {@link module:link/utils~AutomaticDecorators} instance.
	 *
	 * @readonly
	 * @protected
	 * @type {Number}
	 */
	get length() {
		return this._definitions.size;
	}

	/**
	 * Adds automatic decorator objects or an array with them to be used during downcasting.
	 *
	 * @param {module:link/link~LinkDecoratorAutomaticDefinition|Array.<module:link/link~LinkDecoratorAutomaticDefinition>} item
	 * A configuration object of automatic rules for decorating links. It might also be an array of such objects.
	 */
	add( item ) {
		if ( Array.isArray( item ) ) {
			item.forEach( item => this._definitions.add( item ) );
		} else {
			this._definitions.add( item );
		}
	}

	/**
	 * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method.
	 *
	 * @returns {Function} A dispatcher function used as conversion helper
	 * in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
	 */
	getDispatcher() {
		return dispatcher => {
			dispatcher.on( 'attribute:linkHref', ( evt, data, conversionApi ) => {
				// There is only test as this behavior decorates links and
				// it is run before dispatcher which actually consumes this node.
				// This allows on writing own dispatcher with highest priority,
				// which blocks both native converter and this additional decoration.
				if ( !conversionApi.consumable.test( data.item, 'attribute:linkHref' ) ) {
					return;
				}

				// Automatic decorators for block links are handled e.g. in LinkImageEditing.
				if ( !( data.item.is( 'selection' ) || conversionApi.schema.isInline( data.item ) ) ) {
					return;
				}

				const viewWriter = conversionApi.writer;
				const viewSelection = viewWriter.document.selection;

				for ( const item of this._definitions ) {
					const viewElement = viewWriter.createAttributeElement( 'a', item.attributes, {
						priority: 5
					} );

					if ( item.classes ) {
						viewWriter.addClass( item.classes, viewElement );
					}

					for ( const key in item.styles ) {
						viewWriter.setStyle( key, item.styles[ key ], viewElement );
					}

					viewWriter.setCustomProperty( 'link', true, viewElement );
					if ( item.callback( data.attributeNewValue ) ) {
						if ( data.item.is( 'selection' ) ) {
							viewWriter.wrap( viewSelection.getFirstRange(), viewElement );
						} else {
							viewWriter.wrap( conversionApi.mapper.toViewRange( data.range ), viewElement );
						}
					} else {
						viewWriter.unwrap( conversionApi.mapper.toViewRange( data.range ), viewElement );
					}
				}
			}, { priority: 'high' } );
		};
	}

	/**
	 * Provides the conversion helper used in the {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add} method
	 * when linking images.
	 *
	 * @returns {Function} A dispatcher function used as conversion helper
	 * in {@link module:engine/conversion/downcasthelpers~DowncastHelpers#add}.
	 */
	getDispatcherForLinkedImage() {
		return dispatcher => {
			dispatcher.on( 'attribute:linkHref:imageBlock', ( evt, data, { writer, mapper } ) => {
				const viewFigure = mapper.toViewElement( data.item );
				const linkInImage = Array.from( viewFigure.getChildren() ).find( child => child.name === 'a' );

				for ( const item of this._definitions ) {
					const attributes = toMap( item.attributes );

					if ( item.callback( data.attributeNewValue ) ) {
						for ( const [ key, val ] of attributes ) {
							// Left for backward compatibility. Since v30 decorator should
							// accept `classes` and `styles` separately from `attributes`.
							if ( key === 'class' ) {
								writer.addClass( val, linkInImage );
							} else {
								writer.setAttribute( key, val, linkInImage );
							}
						}

						if ( item.classes ) {
							writer.addClass( item.classes, linkInImage );
						}

						for ( const key in item.styles ) {
							writer.setStyle( key, item.styles[ key ], linkInImage );
						}
					} else {
						for ( const [ key, val ] of attributes ) {
							if ( key === 'class' ) {
								writer.removeClass( val, linkInImage );
							} else {
								writer.removeAttribute( key, linkInImage );
							}
						}

						if ( item.classes ) {
							writer.removeClass( item.classes, linkInImage );
						}

						for ( const key in item.styles ) {
							writer.removeStyle( key, linkInImage );
						}
					}
				}
			} );
		};
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/linkcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/linkcommand
 */








/**
 * The link command. It is used by the {@link module:link/link~Link link feature}.
 *
 * @extends module:core/command~Command
 */
class LinkCommand extends command_Command {
	/**
	 * The value of the `'linkHref'` attribute if the start of the selection is located in a node with this attribute.
	 *
	 * @observable
	 * @readonly
	 * @member {Object|undefined} #value
	 */

	constructor( editor ) {
		super( editor );

		/**
		 * A collection of {@link module:link/utils~ManualDecorator manual decorators}
		 * corresponding to the {@link module:link/link~LinkConfig#decorators decorator configuration}.
		 *
		 * You can consider it a model with states of manual decorators added to the currently selected link.
		 *
		 * @readonly
		 * @type {module:utils/collection~Collection}
		 */
		this.manualDecorators = new Collection();

		/**
		 * An instance of the helper that ties together all {@link module:link/link~LinkDecoratorAutomaticDefinition}
		 * that are used by the {@glink features/link link} and the {@glink features/images/images-linking linking images} features.
		 *
		 * @readonly
		 * @type {module:link/utils~AutomaticDecorators}
		 */
		this.automaticDecorators = new AutomaticDecorators();
	}

	/**
	 * Synchronizes the state of {@link #manualDecorators} with the currently present elements in the model.
	 */
	restoreManualDecoratorStates() {
		for ( const manualDecorator of this.manualDecorators ) {
			manualDecorator.value = this._getDecoratorStateFromModel( manualDecorator.id );
		}
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const selection = model.document.selection;
		const selectedElement = selection.getSelectedElement() || first_first( selection.getSelectedBlocks() );

		// A check for any integration that allows linking elements (e.g. `LinkImage`).
		// Currently the selection reads attributes from text nodes only. See #7429 and #7465.
		if ( isLinkableElement( selectedElement, model.schema ) ) {
			this.value = selectedElement.getAttribute( 'linkHref' );
			this.isEnabled = model.schema.checkAttribute( selectedElement, 'linkHref' );
		} else {
			this.value = selection.getAttribute( 'linkHref' );
			this.isEnabled = model.schema.checkAttributeInSelection( selection, 'linkHref' );
		}

		for ( const manualDecorator of this.manualDecorators ) {
			manualDecorator.value = this._getDecoratorStateFromModel( manualDecorator.id );
		}
	}

	/**
	 * Executes the command.
	 *
	 * When the selection is non-collapsed, the `linkHref` attribute will be applied to nodes inside the selection, but only to
	 * those nodes where the `linkHref` attribute is allowed (disallowed nodes will be omitted).
	 *
	 * When the selection is collapsed and is not inside the text with the `linkHref` attribute, a
	 * new {@link module:engine/model/text~Text text node} with the `linkHref` attribute will be inserted in place of the caret, but
	 * only if such element is allowed in this place. The `_data` of the inserted text will equal the `href` parameter.
	 * The selection will be updated to wrap the just inserted text node.
	 *
	 * When the selection is collapsed and inside the text with the `linkHref` attribute, the attribute value will be updated.
	 *
	 * # Decorators and model attribute management
	 *
	 * There is an optional argument to this command that applies or removes model
	 * {@glink framework/guides/architecture/editing-engine#text-attributes text attributes} brought by
	 * {@link module:link/utils~ManualDecorator manual link decorators}.
	 *
	 * Text attribute names in the model correspond to the entries in the {@link module:link/link~LinkConfig#decorators configuration}.
	 * For every decorator configured, a model text attribute exists with the "link" prefix. For example, a `'linkMyDecorator'` attribute
	 * corresponds to `'myDecorator'` in the configuration.
	 *
	 * To learn more about link decorators, check out the {@link module:link/link~LinkConfig#decorators `config.link.decorators`}
	 * documentation.
	 *
	 * Here is how to manage decorator attributes with the link command:
	 *
	 *		const linkCommand = editor.commands.get( 'link' );
	 *
	 *		// Adding a new decorator attribute.
	 *		linkCommand.execute( 'http://example.com', {
	 *			linkIsExternal: true
	 *		} );
	 *
	 *		// Removing a decorator attribute from the selection.
	 *		linkCommand.execute( 'http://example.com', {
	 *			linkIsExternal: false
	 *		} );
	 *
	 *		// Adding multiple decorator attributes at the same time.
	 *		linkCommand.execute( 'http://example.com', {
	 *			linkIsExternal: true,
	 *			linkIsDownloadable: true,
	 *		} );
	 *
	 *		// Removing and adding decorator attributes at the same time.
	 *		linkCommand.execute( 'http://example.com', {
	 *			linkIsExternal: false,
	 *			linkFoo: true,
	 *			linkIsDownloadable: false,
	 *		} );
	 *
	 * **Note**: If the decorator attribute name is not specified, its state remains untouched.
	 *
	 * **Note**: {@link module:link/unlinkcommand~UnlinkCommand#execute `UnlinkCommand#execute()`} removes all
	 * decorator attributes.
	 *
	 * @fires execute
	 * @param {String} href Link destination.
	 * @param {Object} [manualDecoratorIds={}] The information about manual decorator attributes to be applied or removed upon execution.
	 */
	execute( href, manualDecoratorIds = {} ) {
		const model = this.editor.model;
		const selection = model.document.selection;
		// Stores information about manual decorators to turn them on/off when command is applied.
		const truthyManualDecorators = [];
		const falsyManualDecorators = [];

		for ( const name in manualDecoratorIds ) {
			if ( manualDecoratorIds[ name ] ) {
				truthyManualDecorators.push( name );
			} else {
				falsyManualDecorators.push( name );
			}
		}

		model.change( writer => {
			// If selection is collapsed then update selected link or insert new one at the place of caret.
			if ( selection.isCollapsed ) {
				const position = selection.getFirstPosition();

				// When selection is inside text with `linkHref` attribute.
				if ( selection.hasAttribute( 'linkHref' ) ) {
					// Then update `linkHref` value.
					const linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), model );

					writer.setAttribute( 'linkHref', href, linkRange );

					truthyManualDecorators.forEach( item => {
						writer.setAttribute( item, true, linkRange );
					} );

					falsyManualDecorators.forEach( item => {
						writer.removeAttribute( item, linkRange );
					} );

					// Put the selection at the end of the updated link.
					writer.setSelection( writer.createPositionAfter( linkRange.end.nodeBefore ) );
				}
				// If not then insert text node with `linkHref` attribute in place of caret.
				// However, since selection is collapsed, attribute value will be used as data for text node.
				// So, if `href` is empty, do not create text node.
				else if ( href !== '' ) {
					const attributes = toMap( selection.getAttributes() );

					attributes.set( 'linkHref', href );

					truthyManualDecorators.forEach( item => {
						attributes.set( item, true );
					} );

					const { end: positionAfter } = model.insertContent( writer.createText( href, attributes ), position );

					// Put the selection at the end of the inserted link.
					// Using end of range returned from insertContent in case nodes with the same attributes got merged.
					writer.setSelection( positionAfter );
				}

				// Remove the `linkHref` attribute and all link decorators from the selection.
				// It stops adding a new content into the link element.
				[ 'linkHref', ...truthyManualDecorators, ...falsyManualDecorators ].forEach( item => {
					writer.removeSelectionAttribute( item );
				} );
			} else {
				// If selection has non-collapsed ranges, we change attribute on nodes inside those ranges
				// omitting nodes where the `linkHref` attribute is disallowed.
				const ranges = model.schema.getValidRanges( selection.getRanges(), 'linkHref' );

				// But for the first, check whether the `linkHref` attribute is allowed on selected blocks (e.g. the "image" element).
				const allowedRanges = [];

				for ( const element of selection.getSelectedBlocks() ) {
					if ( model.schema.checkAttribute( element, 'linkHref' ) ) {
						allowedRanges.push( writer.createRangeOn( element ) );
					}
				}

				// Ranges that accept the `linkHref` attribute. Since we will iterate over `allowedRanges`, let's clone it.
				const rangesToUpdate = allowedRanges.slice();

				// For all selection ranges we want to check whether given range is inside an element that accepts the `linkHref` attribute.
				// If so, we don't want to propagate applying the attribute to its children.
				for ( const range of ranges ) {
					if ( this._isRangeToUpdate( range, allowedRanges ) ) {
						rangesToUpdate.push( range );
					}
				}

				for ( const range of rangesToUpdate ) {
					writer.setAttribute( 'linkHref', href, range );

					truthyManualDecorators.forEach( item => {
						writer.setAttribute( item, true, range );
					} );

					falsyManualDecorators.forEach( item => {
						writer.removeAttribute( item, range );
					} );
				}
			}
		} );
	}

	/**
	 * Provides information whether a decorator with a given name is present in the currently processed selection.
	 *
	 * @private
	 * @param {String} decoratorName The name of the manual decorator used in the model
	 * @returns {Boolean} The information whether a given decorator is currently present in the selection.
	 */
	_getDecoratorStateFromModel( decoratorName ) {
		const model = this.editor.model;
		const selection = model.document.selection;
		const selectedElement = selection.getSelectedElement();

		// A check for the `LinkImage` plugin. If the selection contains an element, get values from the element.
		// Currently the selection reads attributes from text nodes only. See #7429 and #7465.
		if ( isLinkableElement( selectedElement, model.schema ) ) {
			return selectedElement.getAttribute( decoratorName );
		}

		return selection.getAttribute( decoratorName );
	}

	/**
	 * Checks whether specified `range` is inside an element that accepts the `linkHref` attribute.
	 *
	 * @private
	 * @param {module:engine/view/range~Range} range A range to check.
	 * @param {Array.<module:engine/view/range~Range>} allowedRanges An array of ranges created on elements where the attribute is accepted.
	 * @returns {Boolean}
	 */
	_isRangeToUpdate( range, allowedRanges ) {
		for ( const allowedRange of allowedRanges ) {
			// A range is inside an element that will have the `linkHref` attribute. Do not modify its nodes.
			if ( allowedRange.containsRange( range ) ) {
				return false;
			}
		}

		return true;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/unlinkcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/unlinkcommand
 */






/**
 * The unlink command. It is used by the {@link module:link/link~Link link plugin}.
 *
 * @extends module:core/command~Command
 */
class UnlinkCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const selection = model.document.selection;
		const selectedElement = selection.getSelectedElement();

		// A check for any integration that allows linking elements (e.g. `LinkImage`).
		// Currently the selection reads attributes from text nodes only. See #7429 and #7465.
		if ( isLinkableElement( selectedElement, model.schema ) ) {
			this.isEnabled = model.schema.checkAttribute( selectedElement, 'linkHref' );
		} else {
			this.isEnabled = model.schema.checkAttributeInSelection( selection, 'linkHref' );
		}
	}

	/**
	 * Executes the command.
	 *
	 * When the selection is collapsed, it removes the `linkHref` attribute from each node with the same `linkHref` attribute value.
	 * When the selection is non-collapsed, it removes the `linkHref` attribute from each node in selected ranges.
	 *
	 * # Decorators
	 *
	 * If {@link module:link/link~LinkConfig#decorators `config.link.decorators`} is specified,
	 * all configured decorators are removed together with the `linkHref` attribute.
	 *
	 * @fires execute
	 */
	execute() {
		const editor = this.editor;
		const model = this.editor.model;
		const selection = model.document.selection;
		const linkCommand = editor.commands.get( 'link' );

		model.change( writer => {
			// Get ranges to unlink.
			const rangesToUnlink = selection.isCollapsed ?
				[ findAttributeRange(
					selection.getFirstPosition(),
					'linkHref',
					selection.getAttribute( 'linkHref' ),
					model
				) ] :
				model.schema.getValidRanges( selection.getRanges(), 'linkHref' );

			// Remove `linkHref` attribute from specified ranges.
			for ( const range of rangesToUnlink ) {
				writer.removeAttribute( 'linkHref', range );
				// If there are registered custom attributes, then remove them during unlink.
				if ( linkCommand ) {
					for ( const manualDecorator of linkCommand.manualDecorators ) {
						writer.removeAttribute( manualDecorator.id, range );
					}
				}
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/utils/manualdecorator.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/utils
 */



/**
 * Helper class that stores manual decorators with observable {@link module:link/utils~ManualDecorator#value}
 * to support integration with the UI state. An instance of this class is a model with the state of individual manual decorators.
 * These decorators are kept as collections in {@link module:link/linkcommand~LinkCommand#manualDecorators}.
 *
 * @mixes module:utils/observablemixin~ObservableMixin
 */
class ManualDecorator {
	/**
	 * Creates a new instance of {@link module:link/utils~ManualDecorator}.
	 *
	 * @param {Object} config
	 * @param {String} config.id The name of the attribute used in the model that represents a given manual decorator.
	 * For example: `'linkIsExternal'`.
	 * @param {String} config.label The label used in the user interface to toggle the manual decorator.
	 * @param {Object} config.attributes A set of attributes added to output data when the decorator is active for a specific link.
	 * Attributes should keep the format of attributes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
	 * @param {Boolean} [config.defaultValue] Controls whether the decorator is "on" by default.
	 */
	constructor( { id, label, attributes, classes, styles, defaultValue } ) {
		/**
		 * An ID of a manual decorator which is the name of the attribute in the model, for example: 'linkManualDecorator0'.
		 *
		 * @type {String}
		 */
		this.id = id;

		/**
		 * The value of the current manual decorator. It reflects its state from the UI.
		 *
		 * @observable
		 * @member {Boolean} module:link/utils~ManualDecorator#value
		 */
		this.set( 'value' );

		/**
		 * The default value of manual decorator.
		 *
		 * @type {Boolean}
		 */
		this.defaultValue = defaultValue;

		/**
		 * The label used in the user interface to toggle the manual decorator.
		 *
		 * @type {String}
		 */
		this.label = label;

		/**
		 * A set of attributes added to downcasted data when the decorator is activated for a specific link.
		 * Attributes should be added in a form of attributes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
		 *
		 * @type {Object}
		 */
		this.attributes = attributes;

		/**
		 * A set of classes added to downcasted data when the decorator is activated for a specific link.
		 * Classes should be added in a form of classes defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
		 *
		 * @type {Object}
		 */
		this.classes = classes;

		/**
		 * A set of styles added to downcasted data when the decorator is activated for a specific link.
		 * Styles should be added in a form of styles defined in {@link module:engine/view/elementdefinition~ElementDefinition}.
		 *
		 * @type {Object}
		 */
		this.styles = styles;
	}

	/**
	 * Returns {@link module:engine/view/matcher~MatcherPattern} with decorator attributes.
	 *
	 * @protected
	 * @returns {module:engine/view/matcher~MatcherPattern}
	 */
	_createPattern() {
		return {
			attributes: this.attributes,
			classes: this.classes,
			styles: this.styles
		};
	}
}

mix( ManualDecorator, ObservableMixin );

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-link/theme/link.css
var theme_link = __webpack_require__(399);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/theme/link.css

            

var link_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

link_options.insert = "head";
link_options.singleton = true;

var link_update = injectStylesIntoStyleTag_default()(theme_link/* default */.Z, link_options);



/* harmony default export */ const ckeditor5_link_theme_link = (theme_link/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/linkediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/linkediting
 */














const linkediting_HIGHLIGHT_CLASS = 'ck-link_selected';
const DECORATOR_AUTOMATIC = 'automatic';
const DECORATOR_MANUAL = 'manual';
const EXTERNAL_LINKS_REGEXP = /^(https?:)?\/\//;

/**
 * The link engine feature.
 *
 * It introduces the `linkHref="url"` attribute in the model which renders to the view as a `<a href="url">` element
 * as well as `'link'` and `'unlink'` commands.
 *
 * @extends module:core/plugin~Plugin
 */
class LinkEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'LinkEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		// Clipboard is required for handling cut and paste events while typing over the link.
		return [ TwoStepCaretMovement, Input, ClipboardPipeline ];
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'link', {
			addTargetToExternalLinks: false
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow link attribute on all inline nodes.
		editor.model.schema.extend( '$text', { allowAttributes: 'linkHref' } );

		editor.conversion.for( 'dataDowncast' )
			.attributeToElement( { model: 'linkHref', view: createLinkElement } );

		editor.conversion.for( 'editingDowncast' )
			.attributeToElement( { model: 'linkHref', view: ( href, conversionApi ) => {
				return createLinkElement( ensureSafeUrl( href ), conversionApi );
			} } );

		editor.conversion.for( 'upcast' )
			.elementToAttribute( {
				view: {
					name: 'a',
					attributes: {
						href: true
					}
				},
				model: {
					key: 'linkHref',
					value: viewElement => viewElement.getAttribute( 'href' )
				}
			} );

		// Create linking commands.
		editor.commands.add( 'link', new LinkCommand( editor ) );
		editor.commands.add( 'unlink', new UnlinkCommand( editor ) );

		const linkDecorators = getLocalizedDecorators( editor.t, normalizeDecorators( editor.config.get( 'link.decorators' ) ) );

		this._enableAutomaticDecorators( linkDecorators.filter( item => item.mode === DECORATOR_AUTOMATIC ) );
		this._enableManualDecorators( linkDecorators.filter( item => item.mode === DECORATOR_MANUAL ) );

		// Enable two-step caret movement for `linkHref` attribute.
		const twoStepCaretMovementPlugin = editor.plugins.get( TwoStepCaretMovement );
		twoStepCaretMovementPlugin.registerAttribute( 'linkHref' );

		// Setup highlight over selected link.
		inlineHighlight( editor, 'linkHref', 'a', linkediting_HIGHLIGHT_CLASS );

		// Handle link following by CTRL+click or ALT+ENTER
		this._enableLinkOpen();

		// Change the attributes of the selection in certain situations after the link was inserted into the document.
		this._enableInsertContentSelectionAttributesFixer();

		// Handle a click at the beginning/end of a link element.
		this._enableClickingAfterLink();

		// Handle typing over the link.
		this._enableTypingOverLink();

		// Handle removing the content after the link element.
		this._handleDeleteContentAfterLink();
	}

	/**
	 * Processes an array of configured {@link module:link/link~LinkDecoratorAutomaticDefinition automatic decorators}
	 * and registers a {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher downcast dispatcher}
	 * for each one of them. Downcast dispatchers are obtained using the
	 * {@link module:link/utils~AutomaticDecorators#getDispatcher} method.
	 *
	 * **Note**: This method also activates the automatic external link decorator if enabled with
	 * {@link module:link/link~LinkConfig#addTargetToExternalLinks `config.link.addTargetToExternalLinks`}.
	 *
	 * @private
	 * @param {Array.<module:link/link~LinkDecoratorAutomaticDefinition>} automaticDecoratorDefinitions
	 */
	_enableAutomaticDecorators( automaticDecoratorDefinitions ) {
		const editor = this.editor;
		// Store automatic decorators in the command instance as we do the same with manual decorators.
		// Thanks to that, `LinkImageEditing` plugin can re-use the same definitions.
		const command = editor.commands.get( 'link' );
		const automaticDecorators = command.automaticDecorators;

		// Adds a default decorator for external links.
		if ( editor.config.get( 'link.addTargetToExternalLinks' ) ) {
			automaticDecorators.add( {
				id: 'linkIsExternal',
				mode: DECORATOR_AUTOMATIC,
				callback: url => EXTERNAL_LINKS_REGEXP.test( url ),
				attributes: {
					target: '_blank',
					rel: 'noopener noreferrer'
				}
			} );
		}

		automaticDecorators.add( automaticDecoratorDefinitions );

		if ( automaticDecorators.length ) {
			editor.conversion.for( 'downcast' ).add( automaticDecorators.getDispatcher() );
		}
	}

	/**
	 * Processes an array of configured {@link module:link/link~LinkDecoratorManualDefinition manual decorators},
	 * transforms them into {@link module:link/utils~ManualDecorator} instances and stores them in the
	 * {@link module:link/linkcommand~LinkCommand#manualDecorators} collection (a model for manual decorators state).
	 *
	 * Also registers an {@link module:engine/conversion/downcasthelpers~DowncastHelpers#attributeToElement attribute-to-element}
	 * converter for each manual decorator and extends the {@link module:engine/model/schema~Schema model's schema}
	 * with adequate model attributes.
	 *
	 * @private
	 * @param {Array.<module:link/link~LinkDecoratorManualDefinition>} manualDecoratorDefinitions
	 */
	_enableManualDecorators( manualDecoratorDefinitions ) {
		if ( !manualDecoratorDefinitions.length ) {
			return;
		}

		const editor = this.editor;
		const command = editor.commands.get( 'link' );
		const manualDecorators = command.manualDecorators;

		manualDecoratorDefinitions.forEach( decorator => {
			editor.model.schema.extend( '$text', { allowAttributes: decorator.id } );

			// Keeps reference to manual decorator to decode its name to attributes during downcast.
			decorator = new ManualDecorator( decorator );

			manualDecorators.add( decorator );

			editor.conversion.for( 'downcast' ).attributeToElement( {
				model: decorator.id,
				view: ( manualDecoratorValue, { writer, schema }, { item } ) => {
					// Manual decorators for block links are handled e.g. in LinkImageEditing.
					if ( !( item.is( 'selection' ) || schema.isInline( item ) ) ) {
						return;
					}

					if ( manualDecoratorValue ) {
						const element = writer.createAttributeElement( 'a', decorator.attributes, { priority: 5 } );

						if ( decorator.classes ) {
							writer.addClass( decorator.classes, element );
						}

						for ( const key in decorator.styles ) {
							writer.setStyle( key, decorator.styles[ key ], element );
						}

						writer.setCustomProperty( 'link', true, element );

						return element;
					}
				}
			} );

			editor.conversion.for( 'upcast' ).elementToAttribute( {
				view: {
					name: 'a',
					...decorator._createPattern()
				},
				model: {
					key: decorator.id
				}
			} );
		} );
	}

	/**
	 * Attaches handlers for {@link module:engine/view/document~Document#event:enter} and
	 * {@link module:engine/view/document~Document#event:click} to enable link following.
	 *
	 * @private
	 */
	_enableLinkOpen() {
		const editor = this.editor;
		const view = editor.editing.view;
		const viewDocument = view.document;

		this.listenTo( viewDocument, 'click', ( evt, data ) => {
			const shouldOpen = src_env.isMac ? data.domEvent.metaKey : data.domEvent.ctrlKey;

			if ( !shouldOpen ) {
				return;
			}

			let clickedElement = data.domTarget;

			if ( clickedElement.tagName.toLowerCase() != 'a' ) {
				clickedElement = clickedElement.closest( 'a' );
			}

			if ( !clickedElement ) {
				return;
			}

			const url = clickedElement.getAttribute( 'href' );

			if ( !url ) {
				return;
			}

			evt.stop();
			data.preventDefault();

			openLink( url );
		}, { context: '$capture' } );

		// Open link on Alt+Enter.
		this.listenTo( viewDocument, 'keydown', ( evt, data ) => {
			const url = editor.commands.get( 'link' ).value;
			const shouldOpen = url && data.keyCode === keyCodes.enter && data.altKey;

			if ( !shouldOpen ) {
				return;
			}

			evt.stop();

			openLink( url );
		} );
	}

	/**
	 * Starts listening to {@link module:engine/model/model~Model#event:insertContent} and corrects the model
	 * selection attributes if the selection is at the end of a link after inserting the content.
	 *
	 * The purpose of this action is to improve the overall UX because the user is no longer "trapped" by the
	 * `linkHref` attribute of the selection and they can type a "clean" (`linkHref`–less) text right away.
	 *
	 * See https://github.com/ckeditor/ckeditor5/issues/6053.
	 *
	 * @private
	 */
	_enableInsertContentSelectionAttributesFixer() {
		const editor = this.editor;
		const model = editor.model;
		const selection = model.document.selection;

		this.listenTo( model, 'insertContent', () => {
			const nodeBefore = selection.anchor.nodeBefore;
			const nodeAfter = selection.anchor.nodeAfter;

			// NOTE: ↰ and ↱ represent the gravity of the selection.

			// The only truly valid case is:
			//
			//		                                 ↰
			//		...<$text linkHref="foo">INSERTED[]</$text>
			//
			// If the selection is not "trapped" by the `linkHref` attribute after inserting, there's nothing
			// to fix there.
			if ( !selection.hasAttribute( 'linkHref' ) ) {
				return;
			}

			// Filter out the following case where a link with the same href (e.g. <a href="foo">INSERTED</a>) is inserted
			// in the middle of an existing link:
			//
			// Before insertion:
			//		                       ↰
			//		<$text linkHref="foo">l[]ink</$text>
			//
			// Expected after insertion:
			//		                               ↰
			//		<$text linkHref="foo">lINSERTED[]ink</$text>
			//
			if ( !nodeBefore ) {
				return;
			}

			// Filter out the following case where the selection has the "linkHref" attribute because the
			// gravity is overridden and some text with another attribute (e.g. <b>INSERTED</b>) is inserted:
			//
			// Before insertion:
			//
			//		                       ↱
			//		<$text linkHref="foo">[]link</$text>
			//
			// Expected after insertion:
			//
			//		                                                          ↱
			//		<$text bold="true">INSERTED</$text><$text linkHref="foo">[]link</$text>
			//
			if ( !nodeBefore.hasAttribute( 'linkHref' ) ) {
				return;
			}

			// Filter out the following case where a link is a inserted in the middle (or before) another link
			// (different URLs, so they will not merge). In this (let's say weird) case, we can leave the selection
			// attributes as they are because the user will end up writing in one link or another anyway.
			//
			// Before insertion:
			//
			//		                       ↰
			//		<$text linkHref="foo">l[]ink</$text>
			//
			// Expected after insertion:
			//
			//		                                                             ↰
			//		<$text linkHref="foo">l</$text><$text linkHref="bar">INSERTED[]</$text><$text linkHref="foo">ink</$text>
			//
			if ( nodeAfter && nodeAfter.hasAttribute( 'linkHref' ) ) {
				return;
			}

			model.change( writer => {
				removeLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );
			} );
		}, { priority: 'low' } );
	}

	/**
	 * Starts listening to {@link module:engine/view/document~Document#event:mousedown} and
	 * {@link module:engine/view/document~Document#event:selectionChange} and puts the selection before/after a link node
	 * if clicked at the beginning/ending of the link.
	 *
	 * The purpose of this action is to allow typing around the link node directly after a click.
	 *
	 * See https://github.com/ckeditor/ckeditor5/issues/1016.
	 *
	 * @private
	 */
	_enableClickingAfterLink() {
		const editor = this.editor;
		const model = editor.model;

		editor.editing.view.addObserver( MouseObserver );

		let clicked = false;

		// Detect the click.
		this.listenTo( editor.editing.view.document, 'mousedown', () => {
			clicked = true;
		} );

		// When the selection has changed...
		this.listenTo( editor.editing.view.document, 'selectionChange', () => {
			if ( !clicked ) {
				return;
			}

			// ...and it was caused by the click...
			clicked = false;

			const selection = model.document.selection;

			// ...and no text is selected...
			if ( !selection.isCollapsed ) {
				return;
			}

			// ...and clicked text is the link...
			if ( !selection.hasAttribute( 'linkHref' ) ) {
				return;
			}

			const position = selection.getFirstPosition();
			const linkRange = findAttributeRange( position, 'linkHref', selection.getAttribute( 'linkHref' ), model );

			// ...check whether clicked start/end boundary of the link.
			// If so, remove the `linkHref` attribute.
			if ( position.isTouching( linkRange.start ) || position.isTouching( linkRange.end ) ) {
				model.change( writer => {
					removeLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );
				} );
			}
		} );
	}

	/**
	 * Starts listening to {@link module:engine/model/model~Model#deleteContent} and {@link module:engine/model/model~Model#insertContent}
	 * and checks whether typing over the link. If so, attributes of removed text are preserved and applied to the inserted text.
	 *
	 * The purpose of this action is to allow modifying a text without loosing the `linkHref` attribute (and other).
	 *
	 * See https://github.com/ckeditor/ckeditor5/issues/4762.
	 *
	 * @private
	 */
	_enableTypingOverLink() {
		const editor = this.editor;
		const view = editor.editing.view;

		// Selection attributes when started typing over the link.
		let selectionAttributes;

		// Whether pressed `Backspace` or `Delete`. If so, attributes should not be preserved.
		let deletedContent;

		// Detect pressing `Backspace` / `Delete`.
		this.listenTo( view.document, 'delete', () => {
			deletedContent = true;
		}, { priority: 'high' } );

		// Listening to `model#deleteContent` allows detecting whether selected content was a link.
		// If so, before removing the element, we will copy its attributes.
		this.listenTo( editor.model, 'deleteContent', () => {
			const selection = editor.model.document.selection;

			// Copy attributes only if anything is selected.
			if ( selection.isCollapsed ) {
				return;
			}

			// When the content was deleted, do not preserve attributes.
			if ( deletedContent ) {
				deletedContent = false;

				return;
			}

			// Enabled only when typing.
			if ( !isTyping( editor ) ) {
				return;
			}

			if ( shouldCopyAttributes( editor.model ) ) {
				selectionAttributes = selection.getAttributes();
			}
		}, { priority: 'high' } );

		// Listening to `model#insertContent` allows detecting the content insertion.
		// We want to apply attributes that were removed while typing over the link.
		this.listenTo( editor.model, 'insertContent', ( evt, [ element ] ) => {
			deletedContent = false;

			// Enabled only when typing.
			if ( !isTyping( editor ) ) {
				return;
			}

			if ( !selectionAttributes ) {
				return;
			}

			editor.model.change( writer => {
				for ( const [ attribute, value ] of selectionAttributes ) {
					writer.setAttribute( attribute, value, element );
				}
			} );

			selectionAttributes = null;
		}, { priority: 'high' } );
	}

	/**
	 * Starts listening to {@link module:engine/model/model~Model#deleteContent} and checks whether
	 * removing a content right after the "linkHref" attribute.
	 *
	 * If so, the selection should not preserve the `linkHref` attribute. However, if
	 * the {@link module:typing/twostepcaretmovement~TwoStepCaretMovement} plugin is active and
	 * the selection has the "linkHref" attribute due to overriden gravity (at the end), the `linkHref` attribute should stay untouched.
	 *
	 * The purpose of this action is to allow removing the link text and keep the selection outside the link.
	 *
	 * See https://github.com/ckeditor/ckeditor5/issues/7521.
	 *
	 * @private
	 */
	_handleDeleteContentAfterLink() {
		const editor = this.editor;
		const model = editor.model;
		const selection = model.document.selection;
		const view = editor.editing.view;

		// A flag whether attributes `linkHref` attribute should be preserved.
		let shouldPreserveAttributes = false;

		// A flag whether the `Backspace` key was pressed.
		let hasBackspacePressed = false;

		// Detect pressing `Backspace`.
		this.listenTo( view.document, 'delete', ( evt, data ) => {
			hasBackspacePressed = data.direction === 'backward';
		}, { priority: 'high' } );

		// Before removing the content, check whether the selection is inside a link or at the end of link but with 2-SCM enabled.
		// If so, we want to preserve link attributes.
		this.listenTo( model, 'deleteContent', () => {
			// Reset the state.
			shouldPreserveAttributes = false;

			const position = selection.getFirstPosition();
			const linkHref = selection.getAttribute( 'linkHref' );

			if ( !linkHref ) {
				return;
			}

			const linkRange = findAttributeRange( position, 'linkHref', linkHref, model );

			// Preserve `linkHref` attribute if the selection is in the middle of the link or
			// the selection is at the end of the link and 2-SCM is activated.
			shouldPreserveAttributes = linkRange.containsPosition( position ) || linkRange.end.isEqual( position );
		}, { priority: 'high' } );

		// After removing the content, check whether the current selection should preserve the `linkHref` attribute.
		this.listenTo( model, 'deleteContent', () => {
			// If didn't press `Backspace`.
			if ( !hasBackspacePressed ) {
				return;
			}

			hasBackspacePressed = false;

			// Disable the mechanism if inside a link (`<$text url="foo">F[]oo</$text>` or <$text url="foo">Foo[]</$text>`).
			if ( shouldPreserveAttributes ) {
				return;
			}

			// Use `model.enqueueChange()` in order to execute the callback at the end of the changes process.
			editor.model.enqueueChange( writer => {
				removeLinkAttributesFromSelection( writer, getLinkAttributesAllowedOnText( model.schema ) );
			} );
		}, { priority: 'low' } );
	}
}

// Make the selection free of link-related model attributes.
// All link-related model attributes start with "link". That includes not only "linkHref"
// but also all decorator attributes (they have dynamic names), or even custom plugins.
//
// @param {module:engine/model/writer~Writer} writer
// @param {Array.<String>} linkAttributes
function removeLinkAttributesFromSelection( writer, linkAttributes ) {
	writer.removeSelectionAttribute( 'linkHref' );

	for ( const attribute of linkAttributes ) {
		writer.removeSelectionAttribute( attribute );
	}
}

// Checks whether selection's attributes should be copied to the new inserted text.
//
// @param {module:engine/model/model~Model} model
// @returns {Boolean}
function shouldCopyAttributes( model ) {
	const selection = model.document.selection;
	const firstPosition = selection.getFirstPosition();
	const lastPosition = selection.getLastPosition();
	const nodeAtFirstPosition = firstPosition.nodeAfter;

	// The text link node does not exist...
	if ( !nodeAtFirstPosition ) {
		return false;
	}

	// ...or it isn't the text node...
	if ( !nodeAtFirstPosition.is( '$text' ) ) {
		return false;
	}

	// ...or isn't the link.
	if ( !nodeAtFirstPosition.hasAttribute( 'linkHref' ) ) {
		return false;
	}

	// `textNode` = the position is inside the link element.
	// `nodeBefore` = the position is at the end of the link element.
	const nodeAtLastPosition = lastPosition.textNode || lastPosition.nodeBefore;

	// If both references the same node selection contains a single text node.
	if ( nodeAtFirstPosition === nodeAtLastPosition ) {
		return true;
	}

	// If nodes are not equal, maybe the link nodes has defined additional attributes inside.
	// First, we need to find the entire link range.
	const linkRange = findAttributeRange( firstPosition, 'linkHref', nodeAtFirstPosition.getAttribute( 'linkHref' ), model );

	// Then we can check whether selected range is inside the found link range. If so, attributes should be preserved.
	return linkRange.containsRange( model.createRange( firstPosition, lastPosition ), true );
}

// Checks whether provided changes were caused by typing.
//
// @params {module:core/editor/editor~Editor} editor
// @returns {Boolean}
function isTyping( editor ) {
	const currentBatch = editor.model.change( writer => writer.batch );

	return currentBatch.isTyping;
}

// Returns an array containing names of the attributes allowed on `$text` that describes the link item.
//
// @param {module:engine/model/schema~Schema} schema
// @returns {Array.<String>}
function getLinkAttributesAllowedOnText( schema ) {
	const textAttributes = schema.getDefinition( '$text' ).allowAttributes;

	return textAttributes.filter( attribute => attribute.startsWith( 'link' ) );
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-link/theme/linkform.css
var linkform = __webpack_require__(4827);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/theme/linkform.css

            

var linkform_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

linkform_options.insert = "head";
linkform_options.singleton = true;

var linkform_update = injectStylesIntoStyleTag_default()(linkform/* default */.Z, linkform_options);



/* harmony default export */ const theme_linkform = (linkform/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/ui/linkformview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/ui/linkformview
 */





// See: #8833.
// eslint-disable-next-line ckeditor5-rules/ckeditor-imports



/**
 * The link form view controller class.
 *
 * See {@link module:link/ui/linkformview~LinkFormView}.
 *
 * @extends module:ui/view~View
 */
class LinkFormView extends src_view_View {
	/**
	 * Creates an instance of the {@link module:link/ui/linkformview~LinkFormView} class.
	 *
	 * Also see {@link #render}.
	 *
	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
	 * @param {module:link/linkcommand~LinkCommand} linkCommand Reference to {@link module:link/linkcommand~LinkCommand}.
	 * @param {String} [protocol] A value of a protocol to be displayed in the input's placeholder.
	 */
	constructor( locale, linkCommand ) {
		super( locale );

		const t = locale.t;

		/**
		 * Tracks information about DOM focus in the form.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * The URL input view.
		 *
		 * @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
		 */
		this.urlInputView = this._createUrlInput();

		/**
		 * The Save button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.saveButtonView = this._createButton( t( 'Save' ), icons.check, 'ck-button-save' );
		this.saveButtonView.type = 'submit';

		/**
		 * The Cancel button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.cancelButtonView = this._createButton( t( 'Cancel' ), icons.cancel, 'ck-button-cancel', 'cancel' );

		/**
		 * A collection of {@link module:ui/button/switchbuttonview~SwitchButtonView},
		 * which corresponds to {@link module:link/linkcommand~LinkCommand#manualDecorators manual decorators}
		 * configured in the editor.
		 *
		 * @private
		 * @readonly
		 * @type {module:ui/viewcollection~ViewCollection}
		 */
		this._manualDecoratorSwitches = this._createManualDecoratorSwitches( linkCommand );

		/**
		 * A collection of child views in the form.
		 *
		 * @readonly
		 * @type {module:ui/viewcollection~ViewCollection}
		 */
		this.children = this._createFormChildren( linkCommand.manualDecorators );

		/**
		 * A collection of views that can be focused in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate form fields backwards using the Shift + Tab keystroke.
				focusPrevious: 'shift + tab',

				// Navigate form fields forwards using the Tab key.
				focusNext: 'tab'
			}
		} );

		const classList = [ 'ck', 'ck-link-form', 'ck-responsive-form' ];

		if ( linkCommand.manualDecorators.length ) {
			classList.push( 'ck-link-form_layout-vertical', 'ck-vertical-form' );
		}

		this.setTemplate( {
			tag: 'form',

			attributes: {
				class: classList,

				// https://github.com/ckeditor/ckeditor5-link/issues/90
				tabindex: '-1'
			},

			children: this.children
		} );

		injectCssTransitionDisabler( this );
	}

	/**
	 * Obtains the state of the {@link module:ui/button/switchbuttonview~SwitchButtonView switch buttons} representing
	 * {@link module:link/linkcommand~LinkCommand#manualDecorators manual link decorators}
	 * in the {@link module:link/ui/linkformview~LinkFormView}.
	 *
	 * @returns {Object.<String,Boolean>} Key-value pairs, where the key is the name of the decorator and the value is
	 * its state.
	 */
	getDecoratorSwitchesState() {
		return Array.from( this._manualDecoratorSwitches ).reduce( ( accumulator, switchButton ) => {
			accumulator[ switchButton.name ] = switchButton.isOn;
			return accumulator;
		}, {} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		submitHandler( {
			view: this
		} );

		const childViews = [
			this.urlInputView,
			...this._manualDecoratorSwitches,
			this.saveButtonView,
			this.cancelButtonView
		];

		childViews.forEach( v => {
			// Register the view as focusable.
			this._focusables.add( v );

			// Register the view in the focus tracker.
			this.focusTracker.add( v.element );
		} );

		// Start listening for the keystrokes coming from #element.
		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Focuses the fist {@link #_focusables} in the form.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}

	/**
	 * Creates a labeled input view.
	 *
	 * @private
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView} Labeled field view instance.
	 */
	_createUrlInput() {
		const t = this.locale.t;
		const labeledInput = new LabeledFieldView( this.locale, createLabeledInputText );

		labeledInput.label = t( 'Link URL' );

		return labeledInput;
	}

	/**
	 * Creates a button view.
	 *
	 * @private
	 * @param {String} label The button label.
	 * @param {String} icon The button icon.
	 * @param {String} className The additional button CSS class name.
	 * @param {String} [eventName] An event name that the `ButtonView#execute` event will be delegated to.
	 * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
	 */
	_createButton( label, icon, className, eventName ) {
		const button = new buttonview_ButtonView( this.locale );

		button.set( {
			label,
			icon,
			tooltip: true
		} );

		button.extendTemplate( {
			attributes: {
				class: className
			}
		} );

		if ( eventName ) {
			button.delegate( 'execute' ).to( this, eventName );
		}

		return button;
	}

	/**
	 * Populates {@link module:ui/viewcollection~ViewCollection} of {@link module:ui/button/switchbuttonview~SwitchButtonView}
	 * made based on {@link module:link/linkcommand~LinkCommand#manualDecorators}.
	 *
	 * @private
	 * @param {module:link/linkcommand~LinkCommand} linkCommand A reference to the link command.
	 * @returns {module:ui/viewcollection~ViewCollection} of switch buttons.
	 */
	_createManualDecoratorSwitches( linkCommand ) {
		const switches = this.createCollection();

		for ( const manualDecorator of linkCommand.manualDecorators ) {
			const switchButton = new SwitchButtonView( this.locale );

			switchButton.set( {
				name: manualDecorator.id,
				label: manualDecorator.label,
				withText: true
			} );

			switchButton.bind( 'isOn' ).toMany( [ manualDecorator, linkCommand ], 'value', ( decoratorValue, commandValue ) => {
				return commandValue === undefined && decoratorValue === undefined ? manualDecorator.defaultValue : decoratorValue;
			} );

			switchButton.on( 'execute', () => {
				manualDecorator.set( 'value', !switchButton.isOn );
			} );

			switches.add( switchButton );
		}

		return switches;
	}

	/**
	 * Populates the {@link #children} collection of the form.
	 *
	 * If {@link module:link/linkcommand~LinkCommand#manualDecorators manual decorators} are configured in the editor, it creates an
	 * additional `View` wrapping all {@link #_manualDecoratorSwitches} switch buttons corresponding
	 * to these decorators.
	 *
	 * @private
	 * @param {module:utils/collection~Collection} manualDecorators A reference to
	 * the collection of manual decorators stored in the link command.
	 * @returns {module:ui/viewcollection~ViewCollection} The children of link form view.
	 */
	_createFormChildren( manualDecorators ) {
		const children = this.createCollection();

		children.add( this.urlInputView );

		if ( manualDecorators.length ) {
			const additionalButtonsView = new src_view_View();

			additionalButtonsView.setTemplate( {
				tag: 'ul',
				children: this._manualDecoratorSwitches.map( switchButton => ( {
					tag: 'li',
					children: [ switchButton ],
					attributes: {
						class: [
							'ck',
							'ck-list__item'
						]
					}
				} ) ),
				attributes: {
					class: [
						'ck',
						'ck-reset',
						'ck-list'
					]
				}
			} );
			children.add( additionalButtonsView );
		}

		children.add( this.saveButtonView );
		children.add( this.cancelButtonView );

		return children;
	}
}

/**
 * Fired when the form view is submitted (when one of the children triggered the submit event),
 * for example with a click on {@link #saveButtonView}.
 *
 * @event submit
 */

/**
 * Fired when the form view is canceled, for example with a click on {@link #cancelButtonView}.
 *
 * @event cancel
 */

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-link/theme/linkactions.css
var linkactions = __webpack_require__(9465);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/theme/linkactions.css

            

var linkactions_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

linkactions_options.insert = "head";
linkactions_options.singleton = true;

var linkactions_update = injectStylesIntoStyleTag_default()(linkactions/* default */.Z, linkactions_options);



/* harmony default export */ const theme_linkactions = (linkactions/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/theme/icons/unlink.svg
/* harmony default export */ const unlink = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184zm4.919 10.562-1.414 1.414a.75.75 0 1 1-1.06-1.06l1.414-1.415-1.415-1.414a.75.75 0 0 1 1.061-1.06l1.414 1.414 1.414-1.415a.75.75 0 0 1 1.061 1.061l-1.414 1.414 1.414 1.415a.75.75 0 0 1-1.06 1.06l-1.415-1.414z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/ui/linkactionsview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/ui/linkactionsview
 */







// See: #8833.
// eslint-disable-next-line ckeditor5-rules/ckeditor-imports





/**
 * The link actions view class. This view displays the link preview, allows
 * unlinking or editing the link.
 *
 * @extends module:ui/view~View
 */
class LinkActionsView extends src_view_View {
	/**
	 * @inheritDoc
	 */
	constructor( locale ) {
		super( locale );

		const t = locale.t;

		/**
		 * Tracks information about DOM focus in the actions.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * The href preview view.
		 *
		 * @member {module:ui/view~View}
		 */
		this.previewButtonView = this._createPreviewButton();

		/**
		 * The unlink button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.unlinkButtonView = this._createButton( t( 'Unlink' ), unlink, 'unlink' );

		/**
		 * The edit link button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.editButtonView = this._createButton( t( 'Edit link' ), icons.pencil, 'edit' );

		/**
		 * The value of the "href" attribute of the link to use in the {@link #previewButtonView}.
		 *
		 * @observable
		 * @member {String}
		 */
		this.set( 'href' );

		/**
		 * A collection of views that can be focused in the view.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the view.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate fields backwards using the Shift + Tab keystroke.
				focusPrevious: 'shift + tab',

				// Navigate fields forwards using the Tab key.
				focusNext: 'tab'
			}
		} );

		this.setTemplate( {
			tag: 'div',

			attributes: {
				class: [
					'ck',
					'ck-link-actions',
					'ck-responsive-form'
				],

				// https://github.com/ckeditor/ckeditor5-link/issues/90
				tabindex: '-1'
			},

			children: [
				this.previewButtonView,
				this.editButtonView,
				this.unlinkButtonView
			]
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		const childViews = [
			this.previewButtonView,
			this.editButtonView,
			this.unlinkButtonView
		];

		childViews.forEach( v => {
			// Register the view as focusable.
			this._focusables.add( v );

			// Register the view in the focus tracker.
			this.focusTracker.add( v.element );
		} );

		// Start listening for the keystrokes coming from #element.
		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Focuses the fist {@link #_focusables} in the actions.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}

	/**
	 * Creates a button view.
	 *
	 * @private
	 * @param {String} label The button label.
	 * @param {String} icon The button icon.
	 * @param {String} [eventName] An event name that the `ButtonView#execute` event will be delegated to.
	 * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
	 */
	_createButton( label, icon, eventName ) {
		const button = new buttonview_ButtonView( this.locale );

		button.set( {
			label,
			icon,
			tooltip: true
		} );

		button.delegate( 'execute' ).to( this, eventName );

		return button;
	}

	/**
	 * Creates a link href preview button.
	 *
	 * @private
	 * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
	 */
	_createPreviewButton() {
		const button = new buttonview_ButtonView( this.locale );
		const bind = this.bindTemplate;
		const t = this.t;

		button.set( {
			withText: true,
			tooltip: t( 'Open link in new tab' )
		} );

		button.extendTemplate( {
			attributes: {
				class: [
					'ck',
					'ck-link-actions__preview'
				],
				href: bind.to( 'href', href => href && ensureSafeUrl( href ) ),
				target: '_blank',
				rel: 'noopener noreferrer'
			}
		} );

		button.bind( 'label' ).to( this, 'href', href => {
			return href || t( 'This link has no URL' );
		} );

		button.bind( 'isEnabled' ).to( this, 'href', href => !!href );

		button.template.tag = 'a';
		button.template.eventListeners = {};

		return button;
	}
}

/**
 * Fired when the {@link #editButtonView} is clicked.
 *
 * @event edit
 */

/**
 * Fired when the {@link #unlinkButtonView} is clicked.
 *
 * @event unlink
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/theme/icons/link.svg
/* harmony default export */ const icons_link = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.077 15 .991-1.416a.75.75 0 1 1 1.229.86l-1.148 1.64a.748.748 0 0 1-.217.206 5.251 5.251 0 0 1-8.503-5.955.741.741 0 0 1 .12-.274l1.147-1.639a.75.75 0 1 1 1.228.86L4.933 10.7l.006.003a3.75 3.75 0 0 0 6.132 4.294l.006.004zm5.494-5.335a.748.748 0 0 1-.12.274l-1.147 1.639a.75.75 0 1 1-1.228-.86l.86-1.23a3.75 3.75 0 0 0-6.144-4.301l-.86 1.229a.75.75 0 0 1-1.229-.86l1.148-1.64a.748.748 0 0 1 .217-.206 5.251 5.251 0 0 1 8.503 5.955zm-4.563-2.532a.75.75 0 0 1 .184 1.045l-3.155 4.505a.75.75 0 1 1-1.229-.86l3.155-4.506a.75.75 0 0 1 1.045-.184z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/linkui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/linkui
 */











const VISUAL_SELECTION_MARKER_NAME = 'link-ui';

/**
 * The link UI plugin. It introduces the `'link'` and `'unlink'` buttons and support for the <kbd>Ctrl+K</kbd> keystroke.
 *
 * It uses the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
 *
 * @extends module:core/plugin~Plugin
 */
class LinkUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ContextualBalloon ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'LinkUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		editor.editing.view.addObserver( ClickObserver );

		/**
		 * The actions view displayed inside of the balloon.
		 *
		 * @member {module:link/ui/linkactionsview~LinkActionsView}
		 */
		this.actionsView = this._createActionsView();

		/**
		 * The form view displayed inside the balloon.
		 *
		 * @member {module:link/ui/linkformview~LinkFormView}
		 */
		this.formView = this._createFormView();

		/**
		 * The contextual balloon plugin instance.
		 *
		 * @private
		 * @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
		 */
		this._balloon = editor.plugins.get( ContextualBalloon );

		// Create toolbar buttons.
		this._createToolbarLinkButton();

		// Attach lifecycle actions to the the balloon.
		this._enableUserBalloonInteractions();

		// Renders a fake visual selection marker on an expanded selection.
		editor.conversion.for( 'editingDowncast' ).markerToHighlight( {
			model: VISUAL_SELECTION_MARKER_NAME,
			view: {
				classes: [ 'ck-fake-link-selection' ]
			}
		} );

		// Renders a fake visual selection marker on a collapsed selection.
		editor.conversion.for( 'editingDowncast' ).markerToElement( {
			model: VISUAL_SELECTION_MARKER_NAME,
			view: {
				name: 'span',
				classes: [ 'ck-fake-link-selection', 'ck-fake-link-selection_collapsed' ]
			}
		} );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		// Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
		this.formView.destroy();
	}

	/**
	 * Creates the {@link module:link/ui/linkactionsview~LinkActionsView} instance.
	 *
	 * @private
	 * @returns {module:link/ui/linkactionsview~LinkActionsView} The link actions view instance.
	 */
	_createActionsView() {
		const editor = this.editor;
		const actionsView = new LinkActionsView( editor.locale );
		const linkCommand = editor.commands.get( 'link' );
		const unlinkCommand = editor.commands.get( 'unlink' );

		actionsView.bind( 'href' ).to( linkCommand, 'value' );
		actionsView.editButtonView.bind( 'isEnabled' ).to( linkCommand );
		actionsView.unlinkButtonView.bind( 'isEnabled' ).to( unlinkCommand );

		// Execute unlink command after clicking on the "Edit" button.
		this.listenTo( actionsView, 'edit', () => {
			this._addFormView();
		} );

		// Execute unlink command after clicking on the "Unlink" button.
		this.listenTo( actionsView, 'unlink', () => {
			editor.execute( 'unlink' );
			this._hideUI();
		} );

		// Close the panel on esc key press when the **actions have focus**.
		actionsView.keystrokes.set( 'Esc', ( data, cancel ) => {
			this._hideUI();
			cancel();
		} );

		// Open the form view on Ctrl+K when the **actions have focus**..
		actionsView.keystrokes.set( LINK_KEYSTROKE, ( data, cancel ) => {
			this._addFormView();
			cancel();
		} );

		return actionsView;
	}

	/**
	 * Creates the {@link module:link/ui/linkformview~LinkFormView} instance.
	 *
	 * @private
	 * @returns {module:link/ui/linkformview~LinkFormView} The link form view instance.
	 */
	_createFormView() {
		const editor = this.editor;
		const linkCommand = editor.commands.get( 'link' );
		const defaultProtocol = editor.config.get( 'link.defaultProtocol' );

		const formView = new LinkFormView( editor.locale, linkCommand );

		formView.urlInputView.fieldView.bind( 'value' ).to( linkCommand, 'value' );

		// Form elements should be read-only when corresponding commands are disabled.
		formView.urlInputView.bind( 'isReadOnly' ).to( linkCommand, 'isEnabled', value => !value );
		formView.saveButtonView.bind( 'isEnabled' ).to( linkCommand );

		// Execute link command after clicking the "Save" button.
		this.listenTo( formView, 'submit', () => {
			const { value } = formView.urlInputView.fieldView.element;
			const parsedUrl = addLinkProtocolIfApplicable( value, defaultProtocol );
			editor.execute( 'link', parsedUrl, formView.getDecoratorSwitchesState() );
			this._closeFormView();
		} );

		// Hide the panel after clicking the "Cancel" button.
		this.listenTo( formView, 'cancel', () => {
			this._closeFormView();
		} );

		// Close the panel on esc key press when the **form has focus**.
		formView.keystrokes.set( 'Esc', ( data, cancel ) => {
			this._closeFormView();
			cancel();
		} );

		return formView;
	}

	/**
	 * Creates a toolbar Link button. Clicking this button will show
	 * a {@link #_balloon} attached to the selection.
	 *
	 * @private
	 */
	_createToolbarLinkButton() {
		const editor = this.editor;
		const linkCommand = editor.commands.get( 'link' );
		const t = editor.t;

		// Handle the `Ctrl+K` keystroke and show the panel.
		editor.keystrokes.set( LINK_KEYSTROKE, ( keyEvtData, cancel ) => {
			// Prevent focusing the search bar in FF, Chrome and Edge. See https://github.com/ckeditor/ckeditor5/issues/4811.
			cancel();

			if ( linkCommand.isEnabled ) {
				this._showUI( true );
			}
		} );

		editor.ui.componentFactory.add( 'link', locale => {
			const button = new buttonview_ButtonView( locale );

			button.isEnabled = true;
			button.label = t( 'Link' );
			button.icon = icons_link;
			button.keystroke = LINK_KEYSTROKE;
			button.tooltip = true;
			button.isToggleable = true;

			// Bind button to the command.
			button.bind( 'isEnabled' ).to( linkCommand, 'isEnabled' );
			button.bind( 'isOn' ).to( linkCommand, 'value', value => !!value );

			// Show the panel on button click.
			this.listenTo( button, 'execute', () => this._showUI( true ) );

			return button;
		} );
	}

	/**
	 * Attaches actions that control whether the balloon panel containing the
	 * {@link #formView} is visible or not.
	 *
	 * @private
	 */
	_enableUserBalloonInteractions() {
		const viewDocument = this.editor.editing.view.document;

		// Handle click on view document and show panel when selection is placed inside the link element.
		// Keep panel open until selection will be inside the same link element.
		this.listenTo( viewDocument, 'click', () => {
			const parentLink = this._getSelectedLinkElement();

			if ( parentLink ) {
				// Then show panel but keep focus inside editor editable.
				this._showUI();
			}
		} );

		// Focus the form if the balloon is visible and the Tab key has been pressed.
		this.editor.keystrokes.set( 'Tab', ( data, cancel ) => {
			if ( this._areActionsVisible && !this.actionsView.focusTracker.isFocused ) {
				this.actionsView.focus();
				cancel();
			}
		}, {
			// Use the high priority because the link UI navigation is more important
			// than other feature's actions, e.g. list indentation.
			// https://github.com/ckeditor/ckeditor5-link/issues/146
			priority: 'high'
		} );

		// Close the panel on the Esc key press when the editable has focus and the balloon is visible.
		this.editor.keystrokes.set( 'Esc', ( data, cancel ) => {
			if ( this._isUIVisible ) {
				this._hideUI();
				cancel();
			}
		} );

		// Close on click outside of balloon panel element.
		clickoutsidehandler_clickOutsideHandler( {
			emitter: this.formView,
			activator: () => this._isUIInPanel,
			contextElements: [ this._balloon.view.element ],
			callback: () => this._hideUI()
		} );
	}

	/**
	 * Adds the {@link #actionsView} to the {@link #_balloon}.
	 *
	 * @protected
	 */
	_addActionsView() {
		if ( this._areActionsInPanel ) {
			return;
		}

		this._balloon.add( {
			view: this.actionsView,
			position: this._getBalloonPositionData()
		} );
	}

	/**
	 * Adds the {@link #formView} to the {@link #_balloon}.
	 *
	 * @protected
	 */
	_addFormView() {
		if ( this._isFormInPanel ) {
			return;
		}

		const editor = this.editor;
		const linkCommand = editor.commands.get( 'link' );

		this.formView.disableCssTransitions();

		this._balloon.add( {
			view: this.formView,
			position: this._getBalloonPositionData()
		} );

		// Select input when form view is currently visible.
		if ( this._balloon.visibleView === this.formView ) {
			this.formView.urlInputView.fieldView.select();
		}

		this.formView.enableCssTransitions();

		// Make sure that each time the panel shows up, the URL field remains in sync with the value of
		// the command. If the user typed in the input, then canceled the balloon (`urlInputView.fieldView#value` stays
		// unaltered) and re-opened it without changing the value of the link command (e.g. because they
		// clicked the same link), they would see the old value instead of the actual value of the command.
		// https://github.com/ckeditor/ckeditor5-link/issues/78
		// https://github.com/ckeditor/ckeditor5-link/issues/123
		this.formView.urlInputView.fieldView.element.value = linkCommand.value || '';
	}

	/**
	 * Closes the form view. Decides whether the balloon should be hidden completely or if the action view should be shown. This is
	 * decided upon the link command value (which has a value if the document selection is in the link).
	 *
	 * Additionally, if any {@link module:link/link~LinkConfig#decorators} are defined in the editor configuration, the state of
	 * switch buttons responsible for manual decorator handling is restored.
	 *
	 * @private
	 */
	_closeFormView() {
		const linkCommand = this.editor.commands.get( 'link' );

		// Restore manual decorator states to represent the current model state. This case is important to reset the switch buttons
		// when the user cancels the editing form.
		linkCommand.restoreManualDecoratorStates();

		if ( linkCommand.value !== undefined ) {
			this._removeFormView();
		} else {
			this._hideUI();
		}
	}

	/**
	 * Removes the {@link #formView} from the {@link #_balloon}.
	 *
	 * @protected
	 */
	_removeFormView() {
		if ( this._isFormInPanel ) {
			// Blur the input element before removing it from DOM to prevent issues in some browsers.
			// See https://github.com/ckeditor/ckeditor5/issues/1501.
			this.formView.saveButtonView.focus();

			this._balloon.remove( this.formView );

			// Because the form has an input which has focus, the focus must be brought back
			// to the editor. Otherwise, it would be lost.
			this.editor.editing.view.focus();

			this._hideFakeVisualSelection();
		}
	}

	/**
	 * Shows the correct UI type. It is either {@link #formView} or {@link #actionsView}.
	 *
	 * @param {Boolean} forceVisible
	 * @private
	 */
	_showUI( forceVisible = false ) {
		// When there's no link under the selection, go straight to the editing UI.
		if ( !this._getSelectedLinkElement() ) {
			// Show visual selection on a text without a link when the contextual balloon is displayed.
			// See https://github.com/ckeditor/ckeditor5/issues/4721.
			this._showFakeVisualSelection();

			this._addActionsView();

			// Be sure panel with link is visible.
			if ( forceVisible ) {
				this._balloon.showStack( 'main' );
			}

			this._addFormView();
		}
		// If there's a link under the selection...
		else {
			// Go to the editing UI if actions are already visible.
			if ( this._areActionsVisible ) {
				this._addFormView();
			}
			// Otherwise display just the actions UI.
			else {
				this._addActionsView();
			}

			// Be sure panel with link is visible.
			if ( forceVisible ) {
				this._balloon.showStack( 'main' );
			}
		}

		// Begin responding to ui#update once the UI is added.
		this._startUpdatingUI();
	}

	/**
	 * Removes the {@link #formView} from the {@link #_balloon}.
	 *
	 * See {@link #_addFormView}, {@link #_addActionsView}.
	 *
	 * @protected
	 */
	_hideUI() {
		if ( !this._isUIInPanel ) {
			return;
		}

		const editor = this.editor;

		this.stopListening( editor.ui, 'update' );
		this.stopListening( this._balloon, 'change:visibleView' );

		// Make sure the focus always gets back to the editable _before_ removing the focused form view.
		// Doing otherwise causes issues in some browsers. See https://github.com/ckeditor/ckeditor5-link/issues/193.
		editor.editing.view.focus();

		// Remove form first because it's on top of the stack.
		this._removeFormView();

		// Then remove the actions view because it's beneath the form.
		this._balloon.remove( this.actionsView );

		this._hideFakeVisualSelection();
	}

	/**
	 * Makes the UI react to the {@link module:core/editor/editorui~EditorUI#event:update} event to
	 * reposition itself when the editor UI should be refreshed.
	 *
	 * See: {@link #_hideUI} to learn when the UI stops reacting to the `update` event.
	 *
	 * @protected
	 */
	_startUpdatingUI() {
		const editor = this.editor;
		const viewDocument = editor.editing.view.document;

		let prevSelectedLink = this._getSelectedLinkElement();
		let prevSelectionParent = getSelectionParent();

		const update = () => {
			const selectedLink = this._getSelectedLinkElement();
			const selectionParent = getSelectionParent();

			// Hide the panel if:
			//
			// * the selection went out of the EXISTING link element. E.g. user moved the caret out
			//   of the link,
			// * the selection went to a different parent when creating a NEW link. E.g. someone
			//   else modified the document.
			// * the selection has expanded (e.g. displaying link actions then pressing SHIFT+Right arrow).
			//
			// Note: #_getSelectedLinkElement will return a link for a non-collapsed selection only
			// when fully selected.
			if ( ( prevSelectedLink && !selectedLink ) ||
				( !prevSelectedLink && selectionParent !== prevSelectionParent ) ) {
				this._hideUI();
			}
			// Update the position of the panel when:
			//  * link panel is in the visible stack
			//  * the selection remains in the original link element,
			//  * there was no link element in the first place, i.e. creating a new link
			else if ( this._isUIVisible ) {
				// If still in a link element, simply update the position of the balloon.
				// If there was no link (e.g. inserting one), the balloon must be moved
				// to the new position in the editing view (a new native DOM range).
				this._balloon.updatePosition( this._getBalloonPositionData() );
			}

			prevSelectedLink = selectedLink;
			prevSelectionParent = selectionParent;
		};

		function getSelectionParent() {
			return viewDocument.selection.focus.getAncestors()
				.reverse()
				.find( node => node.is( 'element' ) );
		}

		this.listenTo( editor.ui, 'update', update );
		this.listenTo( this._balloon, 'change:visibleView', update );
	}

	/**
	 * Returns `true` when {@link #formView} is in the {@link #_balloon}.
	 *
	 * @readonly
	 * @protected
	 * @type {Boolean}
	 */
	get _isFormInPanel() {
		return this._balloon.hasView( this.formView );
	}

	/**
	 * Returns `true` when {@link #actionsView} is in the {@link #_balloon}.
	 *
	 * @readonly
	 * @protected
	 * @type {Boolean}
	 */
	get _areActionsInPanel() {
		return this._balloon.hasView( this.actionsView );
	}

	/**
	 * Returns `true` when {@link #actionsView} is in the {@link #_balloon} and it is
	 * currently visible.
	 *
	 * @readonly
	 * @protected
	 * @type {Boolean}
	 */
	get _areActionsVisible() {
		return this._balloon.visibleView === this.actionsView;
	}

	/**
	 * Returns `true` when {@link #actionsView} or {@link #formView} is in the {@link #_balloon}.
	 *
	 * @readonly
	 * @protected
	 * @type {Boolean}
	 */
	get _isUIInPanel() {
		return this._isFormInPanel || this._areActionsInPanel;
	}

	/**
	 * Returns `true` when {@link #actionsView} or {@link #formView} is in the {@link #_balloon} and it is
	 * currently visible.
	 *
	 * @readonly
	 * @protected
	 * @type {Boolean}
	 */
	get _isUIVisible() {
		const visibleView = this._balloon.visibleView;

		return visibleView == this.formView || this._areActionsVisible;
	}

	/**
	 * Returns positioning options for the {@link #_balloon}. They control the way the balloon is attached
	 * to the target element or selection.
	 *
	 * If the selection is collapsed and inside a link element, the panel will be attached to the
	 * entire link element. Otherwise, it will be attached to the selection.
	 *
	 * @private
	 * @returns {module:utils/dom/position~Options}
	 */
	_getBalloonPositionData() {
		const view = this.editor.editing.view;
		const model = this.editor.model;
		const viewDocument = view.document;
		let target = null;

		if ( model.markers.has( VISUAL_SELECTION_MARKER_NAME ) ) {
			// There are cases when we highlight selection using a marker (#7705, #4721).
			const markerViewElements = Array.from( this.editor.editing.mapper.markerNameToElements( VISUAL_SELECTION_MARKER_NAME ) );
			const newRange = view.createRange(
				view.createPositionBefore( markerViewElements[ 0 ] ),
				view.createPositionAfter( markerViewElements[ markerViewElements.length - 1 ] )
			);

			target = view.domConverter.viewRangeToDom( newRange );
		} else {
			// Make sure the target is calculated on demand at the last moment because a cached DOM range
			// (which is very fragile) can desynchronize with the state of the editing view if there was
			// any rendering done in the meantime. This can happen, for instance, when an inline widget
			// gets unlinked.
			target = () => {
				const targetLink = this._getSelectedLinkElement();

				return targetLink ?
					// When selection is inside link element, then attach panel to this element.
					view.domConverter.mapViewToDom( targetLink ) :
					// Otherwise attach panel to the selection.
					view.domConverter.viewRangeToDom( viewDocument.selection.getFirstRange() );
			};
		}

		return { target };
	}

	/**
	 * Returns the link {@link module:engine/view/attributeelement~AttributeElement} under
	 * the {@link module:engine/view/document~Document editing view's} selection or `null`
	 * if there is none.
	 *
	 * **Note**: For a non–collapsed selection, the link element is returned when **fully**
	 * selected and the **only** element within the selection boundaries, or when
	 * a linked widget is selected.
	 *
	 * @private
	 * @returns {module:engine/view/attributeelement~AttributeElement|null}
	 */
	_getSelectedLinkElement() {
		const view = this.editor.editing.view;
		const selection = view.document.selection;
		const selectedElement = selection.getSelectedElement();

		// The selection is collapsed or some widget is selected (especially inline widget).
		if ( selection.isCollapsed || selectedElement && isWidget( selectedElement ) ) {
			return findLinkElementAncestor( selection.getFirstPosition() );
		} else {
			// The range for fully selected link is usually anchored in adjacent text nodes.
			// Trim it to get closer to the actual link element.
			const range = selection.getFirstRange().getTrimmed();
			const startLink = findLinkElementAncestor( range.start );
			const endLink = findLinkElementAncestor( range.end );

			if ( !startLink || startLink != endLink ) {
				return null;
			}

			// Check if the link element is fully selected.
			if ( view.createRangeIn( startLink ).getTrimmed().isEqual( range ) ) {
				return startLink;
			} else {
				return null;
			}
		}
	}

	/**
	 * Displays a fake visual selection when the contextual balloon is displayed.
	 *
	 * This adds a 'link-ui' marker into the document that is rendered as a highlight on selected text fragment.
	 *
	 * @private
	 */
	_showFakeVisualSelection() {
		const model = this.editor.model;

		model.change( writer => {
			const range = model.document.selection.getFirstRange();

			if ( model.markers.has( VISUAL_SELECTION_MARKER_NAME ) ) {
				writer.updateMarker( VISUAL_SELECTION_MARKER_NAME, { range } );
			} else {
				if ( range.start.isAtEnd ) {
					const startPosition = range.start.getLastMatchingPosition(
						( { item } ) => !model.schema.isContent( item ),
						{ boundaries: range }
					);

					writer.addMarker( VISUAL_SELECTION_MARKER_NAME, {
						usingOperation: false,
						affectsData: false,
						range: writer.createRange( startPosition, range.end )
					} );
				} else {
					writer.addMarker( VISUAL_SELECTION_MARKER_NAME, {
						usingOperation: false,
						affectsData: false,
						range
					} );
				}
			}
		} );
	}

	/**
	 * Hides the fake visual selection created in {@link #_showFakeVisualSelection}.
	 *
	 * @private
	 */
	_hideFakeVisualSelection() {
		const model = this.editor.model;

		if ( model.markers.has( VISUAL_SELECTION_MARKER_NAME ) ) {
			model.change( writer => {
				writer.removeMarker( VISUAL_SELECTION_MARKER_NAME );
			} );
		}
	}
}

// Returns a link element if there's one among the ancestors of the provided `Position`.
//
// @private
// @param {module:engine/view/position~Position} View position to analyze.
// @returns {module:engine/view/attributeelement~AttributeElement|null} Link element at the position or null.
function findLinkElementAncestor( position ) {
	return position.getAncestors().find( ancestor => isLinkElement( ancestor ) );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/link.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/link
 */






/**
 * The link plugin.
 *
 * This is a "glue" plugin that loads the {@link module:link/linkediting~LinkEditing link editing feature}
 * and {@link module:link/linkui~LinkUI link UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class Link extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ LinkEditing, LinkUI, AutoLink ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Link';
	}
}

/**
 * The configuration of the {@link module:link/link~Link} feature.
 *
 * Read more in {@link module:link/link~LinkConfig}.
 *
 * @member {module:link/link~LinkConfig} module:core/editor/editorconfig~EditorConfig#link
 */

/**
 * The configuration of the {@link module:link/link~Link link feature}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				link:  ... // Link feature configuration.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 * @interface LinkConfig
 */

/**
 * When set, the editor will add the given protocol to the link when the user creates a link without one.
 * For example, when the user is creating a link and types `ckeditor.com` in the link form input, during link submission
 * the editor will automatically add the `http://` protocol, so the link will look as follows: `http://ckeditor.com`.
 *
 * The feature also provides email address auto-detection. When you submit `hello@example.com`,
 * the plugin will automatically change it to `mailto:hello@example.com`.
 *
 * 		ClassicEditor
 *			.create( editorElement, {
 * 				link: {
 * 					defaultProtocol: 'http://'
 * 				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * **NOTE:** If no configuration is provided, the editor will not auto-fix the links.
 *
 * @member {String} module:link/link~LinkConfig#defaultProtocol
 */

/**
 * When set to `true`, the `target="blank"` and `rel="noopener noreferrer"` attributes are automatically added to all external links
 * in the editor. "External links" are all links in the editor content starting with `http`, `https`, or `//`.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				link: {
 *					addTargetToExternalLinks: true
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * Internally, this option activates a predefined {@link module:link/link~LinkConfig#decorators automatic link decorator}
 * that extends all external links with the `target` and `rel` attributes.
 *
 * **Note**: To control the `target` and `rel` attributes of specific links in the edited content, a dedicated
 * {@link module:link/link~LinkDecoratorManualDefinition manual} decorator must be defined in the
 * {@link module:link/link~LinkConfig#decorators `config.link.decorators`} array. In such scenario,
 * the `config.link.addTargetToExternalLinks` option should remain `undefined` or `false` to not interfere with the manual decorator.
 *
 * It is possible to add other {@link module:link/link~LinkDecoratorAutomaticDefinition automatic}
 * or {@link module:link/link~LinkDecoratorManualDefinition manual} link decorators when this option is active.
 *
 * More information about decorators can be found in the {@link module:link/link~LinkConfig#decorators decorators configuration}
 * reference.
 *
 * @default false
 * @member {Boolean} module:link/link~LinkConfig#addTargetToExternalLinks
 */

/**
 * Decorators provide an easy way to configure and manage additional link attributes in the editor content. There are
 * two types of link decorators:
 *
 * * {@link module:link/link~LinkDecoratorAutomaticDefinition Automatic} &ndash; They match links against pre–defined rules and
 * manage their attributes based on the results.
 * * {@link module:link/link~LinkDecoratorManualDefinition Manual} &ndash; They allow users to control link attributes individually,
 *  using the editor UI.
 *
 * Link decorators are defined as objects with key-value pairs, where the key is the name provided for a given decorator and the
 * value is the decorator definition.
 *
 * The name of the decorator also corresponds to the {@glink framework/guides/architecture/editing-engine#text-attributes text attribute}
 * in the model. For instance, the `isExternal` decorator below is represented as a `linkIsExternal` attribute in the model.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				link: {
 *					decorators: {
 *						isExternal: {
 *							mode: 'automatic',
 *							callback: url => url.startsWith( 'http://' ),
 *							attributes: {
 *								target: '_blank',
 *								rel: 'noopener noreferrer'
 *							}
 *						},
 *						isDownloadable: {
 *							mode: 'manual',
 *							label: 'Downloadable',
 *							attributes: {
 *								download: 'file.png',
 *							}
 *						},
 *						// ...
 *					}
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * To learn more about the configuration syntax, check out the {@link module:link/link~LinkDecoratorAutomaticDefinition automatic}
 * and {@link module:link/link~LinkDecoratorManualDefinition manual} decorator option reference.
 *
 * **Warning:** Currently, link decorators work independently of one another and no conflict resolution mechanism exists.
 * For example, configuring the `target` attribute using both an automatic and a manual decorator at the same time could end up with
 * quirky results. The same applies if multiple manual or automatic decorators were defined for the same attribute.
 *
 * **Note**: Since the `target` attribute management for external links is a common use case, there is a predefined automatic decorator
 * dedicated for that purpose which can be enabled by turning a single option on. Check out the
 * {@link module:link/link~LinkConfig#addTargetToExternalLinks `config.link.addTargetToExternalLinks`}
 * configuration description to learn more.
 *
 * See also the {@glink features/link#custom-link-attributes-decorators link feature guide} for more information.
 *
 * @member {Object.<String, module:link/link~LinkDecoratorDefinition>} module:link/link~LinkConfig#decorators
 */

/**
 * A link decorator definition. Two types implement this defition:
 *
 * * {@link module:link/link~LinkDecoratorManualDefinition}
 * * {@link module:link/link~LinkDecoratorAutomaticDefinition}
 *
 * Refer to their document for more information about available options or to the
 * {@glink features/link#custom-link-attributes-decorators link feature guide} for general information.
 *
 * @interface LinkDecoratorDefinition
 */

/**
 * Link decorator type.
 *
 * Check out the {@glink features/link#custom-link-attributes-decorators link feature guide} for more information.
 *
 * @member {'manual'|'automatic'} module:link/link~LinkDecoratorDefinition#mode
 */

/**
 * Describes an automatic {@link module:link/link~LinkConfig#decorators link decorator}. This decorator type matches
 * all links in the editor content against a function that decides whether the link should receive a pre–defined set of attributes.
 *
 * It takes an object with key-value pairs of attributes and a callback function that must return a Boolean value based on the link's
 * `href` (URL). When the callback returns `true`, attributes are applied to the link.
 *
 * For example, to add the `target="_blank"` attribute to all links in the editor starting with `http://`, the
 * configuration could look like this:
 *
 *		{
 *			mode: 'automatic',
 *			callback: url => url.startsWith( 'http://' ),
 *			attributes: {
 *				target: '_blank'
 *			}
 *		}
 *
 * **Note**: Since the `target` attribute management for external links is a common use case, there is a predefined automatic decorator
 * dedicated for that purpose that can be enabled by turning a single option on. Check out the
 * {@link module:link/link~LinkConfig#addTargetToExternalLinks `config.link.addTargetToExternalLinks`}
 * configuration description to learn more.
 *
 * @typedef {Object} module:link/link~LinkDecoratorAutomaticDefinition
 * @property {'automatic'} mode Link decorator type. It is `'automatic'` for all automatic decorators.
 * @property {Function} callback Takes a `url` as a parameter and returns `true` if the `attributes` should be applied to the link.
 * @property {Object} [attributes] Key-value pairs used as link attributes added to the output during the
 * {@glink framework/guides/architecture/editing-engine#conversion downcasting}.
 * Attributes should follow the {@link module:engine/view/elementdefinition~ElementDefinition} syntax.
 * @property {Object} [styles] Key-value pairs used as link styles added to the output during the
 * {@glink framework/guides/architecture/editing-engine#conversion downcasting}.
 * Styles should follow the {@link module:engine/view/elementdefinition~ElementDefinition} syntax.
 * @property {String|Array.<String>} [classes] Class names used as link classes added to the output during the
 * {@glink framework/guides/architecture/editing-engine#conversion downcasting}.
 * Classes should follow the {@link module:engine/view/elementdefinition~ElementDefinition} syntax.
 */

/**
 * Describes a manual {@link module:link/link~LinkConfig#decorators link decorator}. This decorator type is represented in
 * the link feature's {@link module:link/linkui user interface} as a switch that the user can use to control the presence
 * of a predefined set of attributes.
 *
 * For instance, to allow the users to manually control the presence of the `target="_blank"` and
 * `rel="noopener noreferrer"` attributes on specific links, the decorator could look as follows:
 *
 *		{
 *			mode: 'manual',
 *			label: 'Open in a new tab',
 *			defaultValue: true,
 *			attributes: {
 *				target: '_blank',
 *				rel: 'noopener noreferrer'
 *			}
 *		}
 *
 * @typedef {Object} module:link/link~LinkDecoratorManualDefinition
 * @property {'manual'} mode Link decorator type. It is `'manual'` for all manual decorators.
 * @property {String} label The label of the UI button that the user can use to control the presence of link attributes.
 * @property {Object} [attributes] Key-value pairs used as link attributes added to the output during the
 * {@glink framework/guides/architecture/editing-engine#conversion downcasting}.
 * Attributes should follow the {@link module:engine/view/elementdefinition~ElementDefinition} syntax.
 * @property {Object} [styles] Key-value pairs used as link styles added to the output during the
 * {@glink framework/guides/architecture/editing-engine#conversion downcasting}.
 * Styles should follow the {@link module:engine/view/elementdefinition~ElementDefinition} syntax.
 * @property {String|Array.<String>} [classes] Class names used as link classes added to the output during the
 * {@glink framework/guides/architecture/editing-engine#conversion downcasting}.
 * Classes should follow the {@link module:engine/view/elementdefinition~ElementDefinition} syntax.
 * @property {Boolean} [defaultValue] Controls whether the decorator is "on" by default.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/linkimageediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/linkimageediting
 */







/**
 * The link image engine feature.
 *
 * It accepts the `linkHref="url"` attribute in the model for the {@link module:image/image~Image `<imageBlock>`} element
 * which allows linking images.
 *
 * @extends module:core/plugin~Plugin
 */
class LinkImageEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ 'ImageEditing', 'ImageUtils', LinkEditing ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'LinkImageEditing';
	}

	init() {
		const editor = this.editor;
		const schema = editor.model.schema;

		if ( editor.plugins.has( 'ImageBlockEditing' ) ) {
			schema.extend( 'imageBlock', { allowAttributes: [ 'linkHref' ] } );
		}

		editor.conversion.for( 'upcast' ).add( upcastLink( editor ) );
		editor.conversion.for( 'downcast' ).add( downcastImageLink( editor ) );

		// Definitions for decorators are provided by the `link` command and the `LinkEditing` plugin.
		this._enableAutomaticDecorators();
		this._enableManualDecorators();
	}

	/**
	 * Processes {@link module:link/link~LinkDecoratorAutomaticDefinition automatic decorators} definitions and
	 * attaches proper converters that will work when linking an image.`
	 *
	 * @private
	 */
	_enableAutomaticDecorators() {
		const editor = this.editor;
		const command = editor.commands.get( 'link' );
		const automaticDecorators = command.automaticDecorators;

		if ( automaticDecorators.length ) {
			editor.conversion.for( 'downcast' ).add( automaticDecorators.getDispatcherForLinkedImage() );
		}
	}

	/**
	 * Processes transformed {@link module:link/utils~ManualDecorator} instances and attaches proper converters
	 * that will work when linking an image.
	 *
	 * @private
	 */
	_enableManualDecorators() {
		const editor = this.editor;
		const command = editor.commands.get( 'link' );

		for ( const decorator of command.manualDecorators ) {
			if ( editor.plugins.has( 'ImageBlockEditing' ) ) {
				editor.model.schema.extend( 'imageBlock', { allowAttributes: decorator.id } );
			}

			if ( editor.plugins.has( 'ImageInlineEditing' ) ) {
				editor.model.schema.extend( 'imageInline', { allowAttributes: decorator.id } );
			}

			editor.conversion.for( 'downcast' ).add( downcastImageLinkManualDecorator( decorator ) );
			editor.conversion.for( 'upcast' ).add( upcastImageLinkManualDecorator( editor, decorator ) );
		}
	}
}

// Returns a converter for linked block images that consumes the "href" attribute
// if a link contains an image.
//
// @private
// @param {module:core/editor/editor~Editor} editor The editor instance.
// @returns {Function}
function upcastLink( editor ) {
	const isImageInlinePluginLoaded = editor.plugins.has( 'ImageInlineEditing' );
	const imageUtils = editor.plugins.get( 'ImageUtils' );

	return dispatcher => {
		dispatcher.on( 'element:a', ( evt, data, conversionApi ) => {
			const viewLink = data.viewItem;
			const imageInLink = imageUtils.findViewImgElement( viewLink );

			if ( !imageInLink ) {
				return;
			}

			const blockImageView = imageInLink.findAncestor( element => imageUtils.isBlockImageView( element ) );

			// There are four possible cases to consider here
			//
			// 1. A "root > ... > figure.image > a > img" structure.
			// 2. A "root > ... > figure.image > a > picture > img" structure.
			// 3. A "root > ... > block > a > img" structure.
			// 4. A "root > ... > block > a > picture > img" structure.
			//
			// but the last 2 cases should only be considered by this converter when the inline image plugin
			// is NOT loaded in the editor (because otherwise, that would be a plain, linked inline image).
			if ( isImageInlinePluginLoaded && !blockImageView ) {
				return;
			}

			// There's an image inside an <a> element - we consume it so it won't be picked up by the Link plugin.
			const consumableAttributes = { attributes: [ 'href' ] };

			// Consume the `href` attribute so the default one will not convert it to $text attribute.
			if ( !conversionApi.consumable.consume( viewLink, consumableAttributes ) ) {
				// Might be consumed by something else - i.e. other converter with priority=highest - a standard check.
				return;
			}

			const linkHref = viewLink.getAttribute( 'href' );

			// Missing the 'href' attribute.
			if ( !linkHref ) {
				return;
			}

			// A full definition of the image feature.
			// figure > a > img: parent of the view link element is an image element (figure).
			let modelElement = data.modelCursor.parent;

			if ( !modelElement.is( 'element', 'imageBlock' ) ) {
				// a > img: parent of the view link is not the image (figure) element. We need to convert it manually.
				const conversionResult = conversionApi.convertItem( imageInLink, data.modelCursor );

				// Set image range as conversion result.
				data.modelRange = conversionResult.modelRange;

				// Continue conversion where image conversion ends.
				data.modelCursor = conversionResult.modelCursor;

				modelElement = data.modelCursor.nodeBefore;
			}

			if ( modelElement && modelElement.is( 'element', 'imageBlock' ) ) {
				// Set the linkHref attribute from link element on model image element.
				conversionApi.writer.setAttribute( 'linkHref', linkHref, modelElement );
			}
		}, { priority: 'high' } );
		// Using the same priority that `upcastImageLinkManualDecorator()` converter guarantees
		// that manual decorators will decorate the proper element.
	};
}

// Creates a converter that adds `<a>` to linked block image view elements.
//
// @private
function downcastImageLink( editor ) {
	const imageUtils = editor.plugins.get( 'ImageUtils' );

	return dispatcher => {
		dispatcher.on( 'attribute:linkHref:imageBlock', ( evt, data, conversionApi ) => {
			if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
				return;
			}

			// The image will be already converted - so it will be present in the view.
			const viewFigure = conversionApi.mapper.toViewElement( data.item );
			const writer = conversionApi.writer;

			// But we need to check whether the link element exists.
			const linkInImage = Array.from( viewFigure.getChildren() ).find( child => child.name === 'a' );
			const viewImage = imageUtils.findViewImgElement( viewFigure );
			// <picture>...<img/></picture> or <img/>
			const viewImgOrPicture = viewImage.parent.is( 'element', 'picture' ) ? viewImage.parent : viewImage;

			// If so, update the attribute if it's defined or remove the entire link if the attribute is empty.
			if ( linkInImage ) {
				if ( data.attributeNewValue ) {
					writer.setAttribute( 'href', data.attributeNewValue, linkInImage );
				} else {
					writer.move( writer.createRangeOn( viewImgOrPicture ), writer.createPositionAt( viewFigure, 0 ) );
					writer.remove( linkInImage );
				}
			} else {
				// But if it does not exist. Let's wrap already converted image by newly created link element.
				// 1. Create an empty link element.
				const linkElement = writer.createContainerElement( 'a', { href: data.attributeNewValue } );

				// 2. Insert link inside the associated image.
				writer.insert( writer.createPositionAt( viewFigure, 0 ), linkElement );

				// 3. Move the image to the link.
				writer.move( writer.createRangeOn( viewImgOrPicture ), writer.createPositionAt( linkElement, 0 ) );
			}
		}, { priority: 'high' } );
	};
}

// Returns a converter that decorates the `<a>` element when the image is the link label.
//
// @private
// @returns {Function}
function downcastImageLinkManualDecorator( decorator ) {
	return dispatcher => {
		dispatcher.on( `attribute:${ decorator.id }:imageBlock`, ( evt, data, conversionApi ) => {
			const viewFigure = conversionApi.mapper.toViewElement( data.item );
			const linkInImage = Array.from( viewFigure.getChildren() ).find( child => child.name === 'a' );

			// The <a> element was removed by the time this converter is executed.
			// It may happen when the base `linkHref` and decorator attributes are removed
			// at the same time (see #8401).
			if ( !linkInImage ) {
				return;
			}

			for ( const [ key, val ] of toMap( decorator.attributes ) ) {
				conversionApi.writer.setAttribute( key, val, linkInImage );
			}

			if ( decorator.classes ) {
				conversionApi.writer.addClass( decorator.classes, linkInImage );
			}

			for ( const key in decorator.styles ) {
				conversionApi.writer.setStyle( key, decorator.styles[ key ], linkInImage );
			}
		} );
	};
}

// Returns a converter that checks whether manual decorators should be applied to the link.
//
// @private
// @returns {Function}
function upcastImageLinkManualDecorator( editor, decorator ) {
	const isImageInlinePluginLoaded = editor.plugins.has( 'ImageInlineEditing' );
	const imageUtils = editor.plugins.get( 'ImageUtils' );

	return dispatcher => {
		dispatcher.on( 'element:a', ( evt, data, conversionApi ) => {
			const viewLink = data.viewItem;
			const imageInLink = imageUtils.findViewImgElement( viewLink );

			// We need to check whether an image is inside a link because the converter handles
			// only manual decorators for linked images. See #7975.
			if ( !imageInLink ) {
				return;
			}

			const blockImageView = imageInLink.findAncestor( element => imageUtils.isBlockImageView( element ) );

			if ( isImageInlinePluginLoaded && !blockImageView ) {
				return;
			}

			const matcher = new Matcher( decorator._createPattern() );
			const result = matcher.match( viewLink );

			// The link element does not have required attributes or/and proper values.
			if ( !result ) {
				return;
			}

			// Check whether we can consume those attributes.
			if ( !conversionApi.consumable.consume( viewLink, result.match ) ) {
				return;
			}

			// At this stage we can assume that we have the `<imageBlock>` element.
			// `nodeBefore` comes after conversion: `<a><img></a>`.
			// `parent` comes with full image definition: `<figure><a><img></a></figure>.
			// See the body of the `upcastLink()` function.
			const modelElement = data.modelCursor.nodeBefore || data.modelCursor.parent;

			conversionApi.writer.setAttribute( decorator.id, true, modelElement );
		}, { priority: 'high' } );
		// Using the same priority that `upcastLink()` converter guarantees that the linked image was properly converted.
	};
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/linkimageui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/linkimageui
 */











/**
 * The link image UI plugin.
 *
 * This plugin provides the `'linkImage'` button that can be displayed in the {@link module:image/imagetoolbar~ImageToolbar}.
 * It can be used to wrap images in links.
 *
 * @extends module:core/plugin~Plugin
 */
class LinkImageUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ LinkEditing, LinkUI, 'ImageBlockEditing' ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'LinkImageUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const viewDocument = editor.editing.view.document;

		this.listenTo( viewDocument, 'click', ( evt, data ) => {
			if ( this._isSelectedLinkedImage( editor.model.document.selection ) ) {
				// Prevent browser navigation when clicking a linked image.
				data.preventDefault();

				// Block the `LinkUI` plugin when an image was clicked.
				// In such a case, we'd like to display the image toolbar.
				evt.stop();
			}
		}, { priority: 'high' } );

		this._createToolbarLinkImageButton();
	}

	/**
	 * Creates a `LinkImageUI` button view.
	 *
	 * Clicking this button shows a {@link module:link/linkui~LinkUI#_balloon} attached to the selection.
	 * When an image is already linked, the view shows {@link module:link/linkui~LinkUI#actionsView} or
	 * {@link module:link/linkui~LinkUI#formView} if it is not.
	 *
	 * @private
	 */
	_createToolbarLinkImageButton() {
		const editor = this.editor;
		const t = editor.t;

		editor.ui.componentFactory.add( 'linkImage', locale => {
			const button = new buttonview_ButtonView( locale );
			const plugin = editor.plugins.get( 'LinkUI' );
			const linkCommand = editor.commands.get( 'link' );

			button.set( {
				isEnabled: true,
				label: t( 'Link image' ),
				icon: icons_link,
				keystroke: LINK_KEYSTROKE,
				tooltip: true,
				isToggleable: true
			} );

			// Bind button to the command.
			button.bind( 'isEnabled' ).to( linkCommand, 'isEnabled' );
			button.bind( 'isOn' ).to( linkCommand, 'value', value => !!value );

			// Show the actionsView or formView (both from LinkUI) on button click depending on whether the image is linked already.
			this.listenTo( button, 'execute', () => {
				if ( this._isSelectedLinkedImage( editor.model.document.selection ) ) {
					plugin._addActionsView();
				} else {
					plugin._showUI( true );
				}
			} );

			return button;
		} );
	}

	/**
	 * Returns true if a linked image (either block or inline) is the only selected element
	 * in the model document.
	 *
	 * @private
	 * @param {module:engine/model/selection~Selection} selection
	 * @returns {Boolean}
	 */
	_isSelectedLinkedImage( selection ) {
		const selectedModelElement = selection.getSelectedElement();
		const imageUtils = this.editor.plugins.get( 'ImageUtils' );

		return imageUtils.isImage( selectedModelElement ) && selectedModelElement.hasAttribute( 'linkHref' );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-link/theme/linkimage.css
var linkimage = __webpack_require__(3858);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/theme/linkimage.css

            

var linkimage_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

linkimage_options.insert = "head";
linkimage_options.singleton = true;

var linkimage_update = injectStylesIntoStyleTag_default()(linkimage/* default */.Z, linkimage_options);



/* harmony default export */ const theme_linkimage = (linkimage/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-link/src/linkimage.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module link/linkimage
 */







/**
 * The `LinkImage` plugin.
 *
 * This is a "glue" plugin that loads the {@link module:link/linkimageediting~LinkImageEditing link image editing feature}
 * and {@link module:link/linkimageui~LinkImageUI link image UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class LinkImage extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ LinkImageEditing, LinkImageUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'LinkImage';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/utils/listwalker.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/utils/listwalker
 */




/**
 * Document list blocks iterator.
 */
class ListWalker {
	/**
	 * Creates a document list iterator.
	 *
	 * @param {module:engine/model/element~Element} startElement The start list item block element.
	 * @param {Object} options
	 * @param {'forward'|'backward'} [options.direction='backward'] The iterating direction.
	 * @param {Boolean} [options.includeSelf=false] Whether start block should be included in the result (if it's matching other criteria).
	 * @param {Array.<String>|String} [options.sameAttributes=[]] Additional attributes that must be the same for each block.
	 * @param {Boolean} [options.sameIndent=false] Whether blocks with the same indent level as the start block should be included
	 * in the result.
	 * @param {Boolean} [options.lowerIndent=false] Whether blocks with a lower indent level than the start block should be included
	 * in the result.
	 * @param {Boolean} [options.higherIndent=false] Whether blocks with a higher indent level than the start block should be included
	 * in the result.
	 */
	constructor( startElement, options ) {
		/**
		 * The start list item block element.
		 *
		 * @private
		 * @type {module:engine/model/element~Element}
		 */
		this._startElement = startElement;

		/**
		 * The reference indent. Initialized by the indent of the start block.
		 *
		 * @private
		 * @type {Number}
		 */
		this._referenceIndent = startElement.getAttribute( 'listIndent' );

		/**
		 * The iterating direction.
		 *
		 * @private
		 * @type {Boolean}
		 */
		this._isForward = options.direction == 'forward';

		/**
		 * Whether start block should be included in the result (if it's matching other criteria).
		 *
		 * @private
		 * @type {Boolean}
		 */
		this._includeSelf = !!options.includeSelf;

		/**
		 * Additional attributes that must be the same for each block.
		 *
		 * @private
		 * @type {Array.<String>}
		 */
		this._sameAttributes = toArray( options.sameAttributes || [] );

		/**
		 * Whether blocks with the same indent level as the start block should be included in the result.
		 *
		 * @private
		 * @type {Boolean}
		 */
		this._sameIndent = !!options.sameIndent;

		/**
		 * Whether blocks with a lower indent level than the start block should be included in the result.
		 *
		 * @private
		 * @type {Boolean}
		 */
		this._lowerIndent = !!options.lowerIndent;

		/**
		 * Whether blocks with a higher indent level than the start block should be included in the result.
		 *
		 * @private
		 * @type {Boolean}
		 */
		this._higherIndent = !!options.higherIndent;
	}

	/**
	 * Performs only first step of iteration and returns the result.
	 *
	 * @param {module:engine/model/element~Element} startElement The start list item block element.
	 * @param {Object} options
	 * @param {'forward'|'backward'} [options.direction='backward'] The iterating direction.
	 * @param {Boolean} [options.includeSelf=false] Whether start block should be included in the result (if it's matching other criteria).
	 * @param {Array.<String>|String} [options.sameAttributes=[]] Additional attributes that must be the same for each block.
	 * @param {Boolean} [options.sameIndent=false] Whether blocks with the same indent level as the start block should be included
	 * in the result.
	 * @param {Boolean} [options.lowerIndent=false] Whether blocks with a lower indent level than the start block should be included
	 * in the result.
	 * @param {Boolean} [options.higherIndent=false] Whether blocks with a higher indent level than the start block should be included
	 * in the result.
	 * @returns {module:engine/model/element~Element|null}
	 */
	static first( startElement, options ) {
		const walker = new this( startElement, options );
		const iterator = walker[ Symbol.iterator ]();

		return first_first( iterator );
	}

	/**
	 * Iterable interface.
	 *
	 * @returns {Iterable.<module:engine/model/element~Element>}
	 */
	* [ Symbol.iterator ]() {
		const nestedItems = [];

		for ( const { node } of iterateSiblingListBlocks( this._getStartNode(), this._isForward ? 'forward' : 'backward' ) ) {
			const indent = node.getAttribute( 'listIndent' );

			// Leaving a nested list.
			if ( indent < this._referenceIndent ) {
				// Abort searching blocks.
				if ( !this._lowerIndent ) {
					break;
				}

				// While searching for lower indents, update the reference indent to find another parent in the next step.
				this._referenceIndent = indent;
			}
			// Entering a nested list.
			else if ( indent > this._referenceIndent ) {
				// Ignore nested blocks.
				if ( !this._higherIndent ) {
					continue;
				}

				// Collect nested blocks to verify if they are really nested, or it's a different item.
				if ( !this._isForward ) {
					nestedItems.push( node );

					continue;
				}
			}
			// Same indent level block.
			else {
				// Ignore same indent block.
				if ( !this._sameIndent ) {
					// While looking for nested blocks, stop iterating while encountering first same indent block.
					if ( this._higherIndent ) {
						// No more nested blocks so yield nested items.
						if ( nestedItems.length ) {
							yield* nestedItems;
							nestedItems.length = 0;
						}

						break;
					}

					continue;
				}

				// Abort if item has any additionally specified attribute different.
				if ( this._sameAttributes.some( attr => node.getAttribute( attr ) !== this._startElement.getAttribute( attr ) ) ) {
					break;
				}
			}

			// There is another block for the same list item so the nested items were in the same list item.
			if ( nestedItems.length ) {
				yield* nestedItems;
				nestedItems.length = 0;
			}

			yield node;
		}
	}

	/**
	 * Returns the model element to start iterating.
	 *
	 * @private
	 * @returns {module:engine/model/element~Element}
	 */
	_getStartNode() {
		if ( this._includeSelf ) {
			return this._startElement;
		}

		return this._isForward ?
			this._startElement.nextSibling :
			this._startElement.previousSibling;
	}
}

/**
 * Iterates sibling list blocks starting from the given node.
 *
 * @protected
 * @param {module:engine/model/node~Node} node The model node.
 * @param {'backward'|'forward'} [direction='forward'] Iteration direction.
 * @returns {Iterator.<module:list/documentlist/utils/listwalker~ListIteratorValue>} The object with `node` and `previous`
 * {@link module:engine/model/element~Element blocks}.
 */
function* iterateSiblingListBlocks( node, direction = 'forward' ) {
	const isForward = direction == 'forward';
	let previous = null;

	while ( isListItemBlock( node ) ) {
		yield { node, previous };

		previous = node;
		node = isForward ? node.nextSibling : node.previousSibling;
	}
}

/**
 * The iterable protocol over the list elements.
 *
 * @protected
 */
class ListBlocksIterable {
	/**
	 * @param {module:engine/model/element~Element} listHead The head element of a list.
	 */
	constructor( listHead ) {
		this._listHead = listHead;
	}

	/**
	 * List blocks iterator.
	 *
	 * Iterates over all blocks of a list.
	 *
	 * @returns {Iterator.<module:list/documentlist/utils/listwalker~ListIteratorValue>}
	 */
	[ Symbol.iterator ]() {
		return iterateSiblingListBlocks( this._listHead, 'forward' );
	}
}

/**
 * Object returned by `iterateSiblingListBlocks()` when traversing a list.
 *
 * @protected
 * @typedef {Object} module:list/documentlist/utils/listwalker~ListIteratorValue
 * @property {module:engine/model/node~Node} node The current list node.
 * @property {module:engine/model/node~Node} previous The previous list node.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/utils/model.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/utils/model
 */




/**
 * The list item ID generator.
 *
 * @protected
 */
class ListItemUid {
	/**
	 * Returns the next ID.
	 *
	 * @protected
	 * @returns {String}
	 */
	/* istanbul ignore next: static function definition */
	static next() {
		return uid();
	}
}

/**
 * Returns true if the given model node is a list item block.
 *
 * @protected
 * @param {module:engine/model/node~Node} node A model node.
 * @returns {Boolean}
 */
function isListItemBlock( node ) {
	return !!node && node.is( 'element' ) && node.hasAttribute( 'listItemId' );
}

/**
 * Returns an array with all elements that represents the same list item.
 *
 * It means that values for `listIndent`, and `listItemId` for all items are equal.
 *
 * @protected
 * @param {module:engine/model/element~Element} listItem Starting list item element.
 * @param {Object} [options]
 * @param {Boolean} [options.higherIndent=false] Whether blocks with a higher indent level than the start block should be included
 * in the result.
 * @return {Array.<module:engine/model/element~Element>}
 */
function getAllListItemBlocks( listItem, options = {} ) {
	return [
		...getListItemBlocks( listItem, { ...options, direction: 'backward' } ),
		...getListItemBlocks( listItem, { ...options, direction: 'forward' } )
	];
}

/**
 * Returns an array with elements that represents the same list item in the specified direction.
 *
 * It means that values for `listIndent` and `listItemId` for all items are equal.
 *
 * **Note**: For backward search the provided item is not included, but for forward search it is included in the result.
 *
 * @protected
 * @param {module:engine/model/element~Element} listItem Starting list item element.
 * @param {Object} [options]
 * @param {'forward'|'backward'} [options.direction='backward'] Walking direction.
 * @param {Boolean} [options.higherIndent=false] Whether blocks with a higher indent level than the start block should be included
 * in the result.
 * @returns {Array.<module:engine/model/element~Element>}
 */
function getListItemBlocks( listItem, options = {} ) {
	const isForward = options.direction == 'forward';

	const items = Array.from( new ListWalker( listItem, {
		...options,
		includeSelf: isForward,
		sameIndent: true,
		sameAttributes: 'listItemId'
	} ) );

	return isForward ? items : items.reverse();
}

/**
 * Returns a list items nested inside the given list item.
 *
 * @protected
 * @param {module:engine/model/element~Element} listItem Starting list item element.
 * @returns {Array.<module:engine/model/element~Element>}
 */
function getNestedListBlocks( listItem ) {
	return Array.from( new ListWalker( listItem, {
		direction: 'forward',
		higherIndent: true
	} ) );
}

/**
 * Returns array of all blocks/items of the same list as given block (same indent, same type and properties).
 *
 * @protected
 * @param {module:engine/model/element~Element} listItem Starting list item element.
 * @returns {Array.<module:engine/model/element~Element>}
 */
function getListItems( listItem ) {
	const backwardBlocks = new ListWalker( listItem, {
		sameIndent: true,
		sameAttributes: 'listType'
	} );

	const forwardBlocks = new ListWalker( listItem, {
		sameIndent: true,
		sameAttributes: 'listType',
		includeSelf: true,
		direction: 'forward'
	} );

	return [
		...Array.from( backwardBlocks ).reverse(),
		...forwardBlocks
	];
}

/**
 * Check if the given block is the first in the list item.
 *
 * @protected
 * @param {module:engine/model/element~Element} listBlock The list block element.
 * @returns {Boolean}
 */
function isFirstBlockOfListItem( listBlock ) {
	const previousSibling = ListWalker.first( listBlock, {
		sameIndent: true,
		sameAttributes: 'listItemId'
	} );

	if ( !previousSibling ) {
		return true;
	}

	return false;
}

/**
 * Check if the given block is the last in the list item.
 *
 * @protected
 * @param {module:engine/model/element~Element} listBlock The list block element.
 * @returns {Boolean}
 */
function isLastBlockOfListItem( listBlock ) {
	const nextSibling = ListWalker.first( listBlock, {
		direction: 'forward',
		sameIndent: true,
		sameAttributes: 'listItemId'
	} );

	if ( !nextSibling ) {
		return true;
	}

	return false;
}

/**
 * Expands the given list of selected blocks to include the leading and tailing blocks of partially selected list items.
 *
 * @protected
 * @param {module:engine/model/element~Element|Array.<module:engine/model/element~Element>} blocks The list of selected blocks.
 * @param {Object} [options]
 * @param {Boolean} [options.withNested=true] Whether should include nested list items.
 * @returns {Array.<module:engine/model/element~Element>}
 */
function expandListBlocksToCompleteItems( blocks, options = {} ) {
	blocks = toArray( blocks );

	const higherIndent = options.withNested !== false;
	const allBlocks = new Set();

	for ( const block of blocks ) {
		for ( const itemBlock of getAllListItemBlocks( block, { higherIndent } ) ) {
			allBlocks.add( itemBlock );
		}
	}

	return sortBlocks( allBlocks );
}

/**
 * Expands the given list of selected blocks to include all the items of the lists they're in.
 *
 * @protected
 * @param {module:engine/model/element~Element|Array.<module:engine/model/element~Element>} blocks The list of selected blocks.
 * @returns {Array.<module:engine/model/element~Element>}
 */
function expandListBlocksToCompleteList( blocks ) {
	blocks = toArray( blocks );

	const allBlocks = new Set();

	for ( const block of blocks ) {
		for ( const itemBlock of getListItems( block ) ) {
			allBlocks.add( itemBlock );
		}
	}

	return sortBlocks( allBlocks );
}

/**
 * Splits the list item just before the provided list block.
 *
 * @protected
 * @param {module:engine/model/element~Element} listBlock The list block element.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {Array.<module:engine/model/element~Element>} The array of updated blocks.
 */
function splitListItemBefore( listBlock, writer ) {
	const blocks = getListItemBlocks( listBlock, { direction: 'forward' } );
	const id = ListItemUid.next();

	for ( const block of blocks ) {
		writer.setAttribute( 'listItemId', id, block );
	}

	return blocks;
}

/**
 * Merges the list item with the parent list item.
 *
 * @protected
 * @param {module:engine/model/element~Element} listBlock The list block element.
 * @param {module:engine/model/element~Element} parentBlock The list block element to merge with.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {Array.<module:engine/model/element~Element>} The array of updated blocks.
 */
function mergeListItemBefore( listBlock, parentBlock, writer ) {
	const attributes = {};

	for ( const [ key, value ] of parentBlock.getAttributes() ) {
		if ( key.startsWith( 'list' ) ) {
			attributes[ key ] = value;
		}
	}

	const blocks = getListItemBlocks( listBlock, { direction: 'forward' } );

	for ( const block of blocks ) {
		writer.setAttributes( attributes, block );
	}

	return blocks;
}

/**
 * Increases indentation of given list blocks.
 *
 * @protected
 * @param {module:engine/model/element~Element|Iterable.<module:engine/model/element~Element>} blocks The block or iterable of blocks.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @param {Object} [options]
 * @param {Boolean} [options.expand=false] Whether should expand the list of blocks to include complete list items.
 * @param {Number} [options.indentBy=1] The number of levels the indentation should change (could be negative).
 */
function indentBlocks( blocks, writer, { expand, indentBy = 1 } = {} ) {
	blocks = toArray( blocks );

	// Expand the selected blocks to contain the whole list items.
	const allBlocks = expand ? expandListBlocksToCompleteItems( blocks ) : blocks;

	for ( const block of allBlocks ) {
		const blockIndent = block.getAttribute( 'listIndent' ) + indentBy;

		if ( blockIndent < 0 ) {
			removeListAttributes( block, writer );
		} else {
			writer.setAttribute( 'listIndent', blockIndent, block );
		}
	}

	return allBlocks;
}

/**
 * Decreases indentation of given list of blocks. If the indentation of some blocks matches the indentation
 * of surrounding blocks, they get merged together.
 *
 * @protected
 * @param {module:engine/model/element~Element|Iterable.<module:engine/model/element~Element>} blocks The block or iterable of blocks.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 */
function outdentBlocksWithMerge( blocks, writer ) {
	blocks = toArray( blocks );

	// Expand the selected blocks to contain the whole list items.
	const allBlocks = expandListBlocksToCompleteItems( blocks );
	const visited = new Set();

	const referenceIndent = Math.min( ...allBlocks.map( block => block.getAttribute( 'listIndent' ) ) );
	const parentBlocks = new Map();

	// Collect parent blocks before the list structure gets altered.
	for ( const block of allBlocks ) {
		parentBlocks.set( block, ListWalker.first( block, { lowerIndent: true } ) );
	}

	for ( const block of allBlocks ) {
		if ( visited.has( block ) ) {
			continue;
		}

		visited.add( block );

		const blockIndent = block.getAttribute( 'listIndent' ) - 1;

		if ( blockIndent < 0 ) {
			removeListAttributes( block, writer );

			continue;
		}

		// Merge with parent list item while outdenting and indent matches reference indent.
		if ( block.getAttribute( 'listIndent' ) == referenceIndent ) {
			const mergedBlocks = mergeListItemIfNotLast( block, parentBlocks.get( block ), writer );

			// All list item blocks are updated while merging so add those to visited set.
			for ( const mergedBlock of mergedBlocks ) {
				visited.add( mergedBlock );
			}

			// The indent level was updated while merging so continue to next block.
			if ( mergedBlocks.length ) {
				continue;
			}
		}

		writer.setAttribute( 'listIndent', blockIndent, block );
	}

	return sortBlocks( visited );
}

/**
 * Removes all list attributes from the given blocks.
 *
 * @protected
 * @param {module:engine/model/element~Element|Iterable.<module:engine/model/element~Element>} blocks The block or iterable of blocks.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {Array.<module:engine/model/element~Element>} Array of altered blocks.
 */
function removeListAttributes( blocks, writer ) {
	blocks = toArray( blocks );

	for ( const block of blocks ) {
		for ( const attributeKey of block.getAttributeKeys() ) {
			if ( attributeKey.startsWith( 'list' ) ) {
				writer.removeAttribute( attributeKey, block );
			}
		}
	}

	return blocks;
}

/**
 * Checks whether the given blocks are related to a single list item.
 *
 * @protected
 * @param {Array.<module:engine/model/element~Element>} blocks The list block elements.
 * @returns {Boolean}
 */
function isSingleListItem( blocks ) {
	if ( !blocks.length ) {
		return false;
	}

	const firstItemId = blocks[ 0 ].getAttribute( 'listItemId' );

	if ( !firstItemId ) {
		return false;
	}

	return !blocks.some( item => item.getAttribute( 'listItemId' ) != firstItemId );
}

/**
 * Modifies the indents of list blocks following the given list block so the indentation is valid after
 * the given block is no longer a list item.
 *
 * @protected
 * @param {module:engine/model/element~Element} lastBlock The last list block that has become a non-list element.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {Array.<module:engine/model/element~Element>} Array of altered blocks.
 */
function outdentFollowingItems( lastBlock, writer ) {
	const changedBlocks = [];

	// Start from the model item that is just after the last turned-off item.
	let currentIndent = Number.POSITIVE_INFINITY;

	// Correct indent of all items after the last turned off item.
	// Rules that should be followed:
	// 1. All direct sub-items of turned-off item should become indent 0, because the first item after it
	//    will be the first item of a new list. Other items are at the same level, so should have same 0 index.
	// 2. All items with indent lower than indent of turned-off item should become indent 0, because they
	//    should not end up as a child of any of list items that they were not children of before.
	// 3. All other items should have their indent changed relatively to it's parent.
	//
	// For example:
	// 1  * --------
	// 2     * --------
	// 3        * --------			<-- this is turned off.
	// 4           * --------		<-- this has to become indent = 0, because it will be first item on a new list.
	// 5              * --------	<-- this should be still be a child of item above, so indent = 1.
	// 6        * --------			<-- this has to become indent = 0, because it should not be a child of any of items above.
	// 7           * --------		<-- this should be still be a child of item above, so indent = 1.
	// 8     * --------				<-- this has to become indent = 0.
	// 9        * --------			<-- this should still be a child of item above, so indent = 1.
	// 10          * --------		<-- this should still be a child of item above, so indent = 2.
	// 11          * --------		<-- this should still be at the same level as item above, so indent = 2.
	// 12 * --------				<-- this and all below are left unchanged.
	// 13    * --------
	// 14       * --------
	//
	// After turning off 3 the list becomes:
	//
	// 1  * --------
	// 2     * --------
	//
	// 3  --------
	//
	// 4  * --------
	// 5     * --------
	// 6  * --------
	// 7     * --------
	// 8  * --------
	// 9     * --------
	// 10       * --------
	// 11       * --------
	// 12 * --------
	// 13    * --------
	// 14       * --------
	//
	// Thanks to this algorithm no lists are mismatched and no items get unexpected children/parent, while
	// those parent-child connection which are possible to maintain are still maintained. It's worth noting
	// that this is the same effect that we would be get by multiple use of outdent command. However doing
	// it like this is much more efficient because it's less operation (less memory usage, easier OT) and
	// less conversion (faster).
	for ( const { node } of iterateSiblingListBlocks( lastBlock.nextSibling, 'forward' ) ) {
		// Check each next list item, as long as its indent is higher than 0.
		const indent = node.getAttribute( 'listIndent' );

		// If the indent is 0 we are not going to change anything anyway.
		if ( indent == 0 ) {
			break;
		}

		// We check if that's item indent is lower than current relative indent.
		if ( indent < currentIndent ) {
			// If it is, current relative indent becomes that indent.
			currentIndent = indent;
		}

		// Fix indent relatively to current relative indent.
		// Note, that if we just changed the current relative indent, the newIndent will be equal to 0.
		const newIndent = indent - currentIndent;

		writer.setAttribute( 'listIndent', newIndent, node );
		changedBlocks.push( node );
	}

	return changedBlocks;
}

/**
 * Returns the array of given blocks sorted by model indexes (document order).
 *
 * @protected
 * @param {Iterable.<module:engine/model/element~Element>} blocks The array of blocks.
 * @returns {Array.<module:engine/model/element~Element>} The sorted array of blocks.
 */
function sortBlocks( blocks ) {
	return Array.from( blocks )
		.filter( block => block.root.rootName !== '$graveyard' )
		.sort( ( a, b ) => a.index - b.index );
}

/**
 * Returns a selected block object. If a selected object is inline or when there is no selected
 * object, `null` is returned.
 *
 * @protected
 * @param {module:engine/model/model~Model} model The instance of editor model.
 * @returns {module:engine/model/element~Element|null} Selected block object or `null`.
 */
function getSelectedBlockObject( model ) {
	const selectedElement = model.document.selection.getSelectedElement();

	if ( !selectedElement ) {
		return null;
	}

	if ( model.schema.isObject( selectedElement ) && model.schema.isBlock( selectedElement ) ) {
		return selectedElement;
	}

	return null;
}

// Merges a given block to the given parent block if parent is a list item and there is no more blocks in the same item.
function mergeListItemIfNotLast( block, parentBlock, writer ) {
	const parentItemBlocks = getListItemBlocks( parentBlock, { direction: 'forward' } );

	// Merge with parent only if outdented item wasn't the last one in its parent.
	// Merge:
	// * a			->		* a
	//   * [b]		->		  b
	//   c			->		  c
	// Don't merge:
	// * a			->		* a
	//   * [b]		-> 		* b
	// * c			->		* c
	if ( parentItemBlocks.pop().index > block.index ) {
		return mergeListItemBefore( block, parentBlock, writer );
	}

	return [];
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/documentlistindentcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/documentlistindentcommand
 */





/**
 * The document list indent command. It is used by the {@link module:list/documentlist~DocumentList list feature}.
 *
 * @extends module:core/command~Command
 */
class DocumentListIndentCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {'forward'|'backward'} indentDirection The direction of indent. If it is equal to `backward`, the command
	 * will outdent a list item.
	 */
	constructor( editor, indentDirection ) {
		super( editor );

		/**
		 * Determines by how much the command will change the list item's indent attribute.
		 *
		 * @readonly
		 * @private
		 * @member {'forward'|'backward'}
		 */
		this._direction = indentDirection;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Indents or outdents (depending on the {@link #constructor}'s `indentDirection` parameter) selected list items.
	 *
	 * @fires execute
	 * @fires afterExecute
	 */
	execute() {
		const model = this.editor.model;
		const blocks = getSelectedListBlocks( model.document.selection );

		model.change( writer => {
			const changedBlocks = [];

			// Handle selection contained in the single list item and starting in the following blocks.
			if ( isSingleListItem( blocks ) && !isFirstBlockOfListItem( blocks[ 0 ] ) ) {
				// Allow increasing indent of following list item blocks.
				if ( this._direction == 'forward' ) {
					changedBlocks.push( ...indentBlocks( blocks, writer ) );
				}

				// For indent make sure that indented blocks have a new ID.
				// For outdent just split blocks from the list item (give them a new IDs).
				changedBlocks.push( ...splitListItemBefore( blocks[ 0 ], writer ) );
			}
			// More than a single list item is selected, or the first block of list item is selected.
			else {
				// Now just update the attributes of blocks.
				if ( this._direction == 'forward' ) {
					changedBlocks.push( ...indentBlocks( blocks, writer, { expand: true } ) );
				} else {
					changedBlocks.push( ...outdentBlocksWithMerge( blocks, writer ) );
				}
			}

			// Align the list item type to match the previous list item (from the same list).
			for ( const block of changedBlocks ) {
				// This block become a plain block (for example a paragraph).
				if ( !block.hasAttribute( 'listType' ) ) {
					continue;
				}

				const previousItemBlock = ListWalker.first( block, { sameIndent: true } );

				if ( previousItemBlock ) {
					writer.setAttribute( 'listType', previousItemBlock.getAttribute( 'listType' ), block );
				}
			}

			this._fireAfterExecute( changedBlocks );
		} );
	}

	/**
	 * Fires the `afterExecute` event.
	 *
	 * @private
	 * @param {Array.<module:engine/model/element~Element>} changedBlocks The changed list elements.
	 */
	_fireAfterExecute( changedBlocks ) {
		/**
		 * Event fired by the {@link #execute} method.
		 *
		 * It allows to execute an action after executing the {@link ~DocumentListIndentCommand#execute} method,
		 * for example adjusting attributes of changed list items.
		 *
		 * @protected
		 * @event afterExecute
		 */
		this.fire( 'afterExecute', sortBlocks( new Set( changedBlocks ) ) );
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		// Check whether any of position's ancestor is a list item.
		let blocks = getSelectedListBlocks( this.editor.model.document.selection );
		let firstBlock = blocks[ 0 ];

		// If selection is not in a list item, the command is disabled.
		if ( !firstBlock ) {
			return false;
		}

		// If we are outdenting it is enough to be in list item. Every list item can always be outdented.
		if ( this._direction == 'backward' ) {
			return true;
		}

		// A single block of a list item is selected, so it could be indented as a sublist.
		if ( isSingleListItem( blocks ) && !isFirstBlockOfListItem( blocks[ 0 ] ) ) {
			return true;
		}

		blocks = expandListBlocksToCompleteItems( blocks );
		firstBlock = blocks[ 0 ];

		// Check if there is any list item before selected items that could become a parent of selected items.
		const siblingItem = ListWalker.first( firstBlock, { sameIndent: true } );

		if ( !siblingItem ) {
			return false;
		}

		if ( siblingItem.getAttribute( 'listType' ) == firstBlock.getAttribute( 'listType' ) ) {
			return true;
		}

		return false;
	}
}

// Returns an array of selected blocks truncated to the first non list block element.
function getSelectedListBlocks( selection ) {
	const blocks = Array.from( selection.getSelectedBlocks() );
	const firstNonListBlockIndex = blocks.findIndex( block => !isListItemBlock( block ) );

	if ( firstNonListBlockIndex != -1 ) {
		blocks.length = firstNonListBlockIndex;
	}

	return blocks;
}


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/documentlistcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/documentlistcommand
 */




/**
 * The list command. It is used by the {@link module:list/documentlist~DocumentList document list feature}.
 *
 * @extends module:core/command~Command
 */
class DocumentListCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {'numbered'|'bulleted'} type List type that will be handled by this command.
	 */
	constructor( editor, type ) {
		super( editor );

		/**
		 * The type of the list created by the command.
		 *
		 * @readonly
		 * @member {'numbered'|'bulleted'}
		 */
		this.type = type;

		/**
		 * A flag indicating whether the command is active, which means that the selection starts in a list of the same type.
		 *
		 * @observable
		 * @readonly
		 * @member {Boolean} #value
		 */
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.value = this._getValue();
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Executes the list command.
	 *
	 * @fires execute
	 * @fires afterExecute
	 * @param {Object} [options] Command options.
	 * @param {Boolean} [options.forceValue] If set, it will force the command behavior. If `true`, the command will try to convert the
	 * selected items and potentially the neighbor elements to the proper list items. If set to `false` it will convert selected elements
	 * to paragraphs. If not set, the command will toggle selected elements to list items or paragraphs, depending on the selection.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;
		const selectedBlockObject = getSelectedBlockObject( model );

		const blocks = Array.from( document.selection.getSelectedBlocks() )
			.filter( block => model.schema.checkAttribute( block, 'listType' ) );

		// Whether we are turning off some items.
		const turnOff = options.forceValue !== undefined ? !options.forceValue : this.value;

		model.change( writer => {
			if ( turnOff ) {
				const lastBlock = blocks[ blocks.length - 1 ];

				// Split the first block from the list item.
				const itemBlocks = getListItemBlocks( lastBlock, { direction: 'forward' } );
				const changedBlocks = [];

				if ( itemBlocks.length > 1 ) {
					changedBlocks.push( ...splitListItemBefore( itemBlocks[ 1 ], writer ) );
				}

				// Convert list blocks to plain blocks.
				changedBlocks.push( ...removeListAttributes( blocks, writer ) );

				// Outdent items following the selected list item.
				changedBlocks.push( ...outdentFollowingItems( lastBlock, writer ) );

				this._fireAfterExecute( changedBlocks );
			}
			// Turning on the list items for a collapsed selection inside a list item.
			else if ( ( selectedBlockObject || document.selection.isCollapsed ) && isListItemBlock( blocks[ 0 ] ) ) {
				const changedBlocks = getListItems( selectedBlockObject || blocks[ 0 ] );

				for ( const block of changedBlocks ) {
					writer.setAttribute( 'listType', this.type, block );
				}

				this._fireAfterExecute( changedBlocks );
			}
			// Turning on the list items for a non-collapsed selection.
			else {
				const changedBlocks = [];

				for ( const block of blocks ) {
					// Promote the given block to the list item.
					if ( !block.hasAttribute( 'listType' ) ) {
						writer.setAttributes( {
							listIndent: 0,
							listItemId: ListItemUid.next(),
							listType: this.type
						}, block );

						changedBlocks.push( block );
					}
					// Change the type of list item.
					else {
						for ( const node of expandListBlocksToCompleteItems( block, { withNested: false } ) ) {
							if ( node.getAttribute( 'listType' ) != this.type ) {
								writer.setAttribute( 'listType', this.type, node );
								changedBlocks.push( node );
							}
						}
					}
				}

				this._fireAfterExecute( changedBlocks );
			}
		} );
	}

	/**
	 * Fires the `afterExecute` event.
	 *
	 * @private
	 * @param {Array.<module:engine/model/element~Element>} changedBlocks The changed list elements.
	 */
	_fireAfterExecute( changedBlocks ) {
		/**
		 * Event fired by the {@link #execute} method.
		 *
		 * It allows to execute an action after executing the {@link ~DocumentListCommand#execute} method,
		 * for example adjusting attributes of changed list items.
		 *
		 * @protected
		 * @event afterExecute
		 */
		this.fire( 'afterExecute', sortBlocks( new Set( changedBlocks ) ) );
	}

	/**
	 * Checks the command's {@link #value}.
	 *
	 * @private
	 * @returns {Boolean} The current value.
	 */
	_getValue() {
		const selection = this.editor.model.document.selection;
		const blocks = Array.from( selection.getSelectedBlocks() );

		if ( !blocks.length ) {
			return false;
		}

		for ( const block of blocks ) {
			if ( block.getAttribute( 'listType' ) != this.type ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		const selection = this.editor.model.document.selection;
		const schema = this.editor.model.schema;
		const blocks = Array.from( selection.getSelectedBlocks() );

		if ( !blocks.length ) {
			return false;
		}

		// If command value is true it means that we are in list item, so the command should be enabled.
		if ( this.value ) {
			return true;
		}

		for ( const block of blocks ) {
			if ( schema.checkAttribute( block, 'listType' ) ) {
				return true;
			}
		}

		return false;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/documentlistmergecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/documentlistmergecommand
 */





/**
 * The document list merge command. It is used by the {@link module:list/documentlist~DocumentList list feature}.
 *
 * @extends module:core/command~Command
 */
class DocumentListMergeCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {'backward'|'forward'} direction Whether list item should be merged before or after the selected block.
	 */
	constructor( editor, direction ) {
		super( editor );

		/**
		 * Whether list item should be merged before or after the selected block.
		 *
		 * @readonly
		 * @private
		 * @member {'backward'|'forward'}
		 */
		this._direction = direction;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Merges list blocks together (depending on the {@link #constructor}'s `direction` parameter).
	 *
	 * @fires execute
	 * @fires afterExecute
	 * @param {Object} [options] Command options.
	 * @param {String|Boolean} [options.shouldMergeOnBlocksContentLevel=false] When set `true`, merging will be performed together
	 * with {@link module:engine/model/model~Model#deleteContent} to get rid of the inline content in the selection or take advantage
	 * of the heuristics in `deleteContent()` that helps convert lists into paragraphs in certain cases.
	 */
	execute( { shouldMergeOnBlocksContentLevel = false } = {} ) {
		const model = this.editor.model;
		const selection = model.document.selection;
		const changedBlocks = [];

		model.change( writer => {
			const { firstElement, lastElement } = this._getMergeSubjectElements( selection, shouldMergeOnBlocksContentLevel );

			const firstIndent = firstElement.getAttribute( 'listIndent' ) || 0;
			const lastIndent = lastElement.getAttribute( 'listIndent' );
			const lastElementId = lastElement.getAttribute( 'listItemId' );

			if ( firstIndent != lastIndent ) {
				const nestedLastElementBlocks = getNestedListBlocks( lastElement );

				changedBlocks.push( ...indentBlocks( [ lastElement, ...nestedLastElementBlocks ], writer, {
					indentBy: firstIndent - lastIndent,

					// If outdenting, the entire sub-tree that follows must be included.
					expand: firstIndent < lastIndent
				} ) );
			}

			if ( shouldMergeOnBlocksContentLevel ) {
				let sel = selection;

				if ( selection.isCollapsed ) {
					sel = writer.createSelection( writer.createRange(
						writer.createPositionAt( firstElement, 'end' ),
						writer.createPositionAt( lastElement, 0 )
					) );
				}

				// Delete selected content. Replace entire content only for non-collapsed selection.
				model.deleteContent( sel, { doNotResetEntireContent: selection.isCollapsed } );

				// Get the last "touched" element after deleteContent call (can't use the lastElement because
				// it could get merged into the firstElement while deleting content).
				const lastElementAfterDelete = sel.getLastPosition().parent;

				// Check if the element after it was in the same list item and adjust it if needed.
				const nextSibling = lastElementAfterDelete.nextSibling;

				changedBlocks.push( lastElementAfterDelete );

				if ( nextSibling && nextSibling !== lastElement && nextSibling.getAttribute( 'listItemId' ) == lastElementId ) {
					changedBlocks.push( ...mergeListItemBefore( nextSibling, lastElementAfterDelete, writer ) );
				}
			} else {
				changedBlocks.push( ...mergeListItemBefore( lastElement, firstElement, writer ) );
			}

			this._fireAfterExecute( changedBlocks );
		} );
	}

	/**
	 * Fires the `afterExecute` event.
	 *
	 * @private
	 * @param {Array.<module:engine/model/element~Element>} changedBlocks The changed list elements.
	 */
	_fireAfterExecute( changedBlocks ) {
		/**
		 * Event fired by the {@link #execute} method.
		 *
		 * It allows to execute an action after executing the {@link ~DocumentListMergeCommand#execute} method,
		 * for example adjusting attributes of changed list items.
		 *
		 * @protected
		 * @event afterExecute
		 */
		this.fire( 'afterExecute', sortBlocks( new Set( changedBlocks ) ) );
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		const model = this.editor.model;
		const selection = model.document.selection;
		const selectedBlockObject = getSelectedBlockObject( model );

		if ( selection.isCollapsed || selectedBlockObject ) {
			const positionParent = selectedBlockObject || selection.getFirstPosition().parent;

			if ( !isListItemBlock( positionParent ) ) {
				return false;
			}

			const siblingNode = this._direction == 'backward' ?
				positionParent.previousSibling :
				positionParent.nextSibling;

			if ( !siblingNode ) {
				return false;
			}

			if ( isSingleListItem( [ positionParent, siblingNode ] ) ) {
				return false;
			}
		} else {
			const lastPosition = selection.getLastPosition();
			const firstPosition = selection.getFirstPosition();

			// If deleting within a single block of a list item, there's no need to merge anything.
			// The default delete should be executed instead.
			if ( lastPosition.parent === firstPosition.parent ) {
				return false;
			}

			if ( !isListItemBlock( lastPosition.parent ) ) {
				return false;
			}
		}

		return true;
	}

	/**
	 * Returns the boundary elements the merge should be executed for. These are not necessarily selection's first
	 * and last position parents but sometimes sibling or even further blocks depending on the context.
	 *
	 * @param {module:engine/model/selection~Selection} selection The selection the merge is executed for.
	 * @param {Boolean} shouldMergeOnBlocksContentLevel When `true`, merge is performed together with
	 * {@link module:engine/model/model~Model#deleteContent} to remove the inline content within the selection.
	 * @returns {Object} elements
	 * @returns {module:engine/model/element~Element} elements.firstElement
	 * @returns {module:engine/model/element~Element} elements.lastElement
	 */
	_getMergeSubjectElements( selection, shouldMergeOnBlocksContentLevel ) {
		const model = this.editor.model;
		const selectedBlockObject = getSelectedBlockObject( model );
		let firstElement, lastElement;

		if ( selection.isCollapsed || selectedBlockObject ) {
			const positionParent = selectedBlockObject || selection.getFirstPosition().parent;
			const isFirstBlock = isFirstBlockOfListItem( positionParent );

			if ( this._direction == 'backward' ) {
				lastElement = positionParent;

				if ( isFirstBlock && !shouldMergeOnBlocksContentLevel ) {
					// For the "c" as an anchorElement:
					//  * a
					//    * b
					//  * [c]  <-- this block should be merged with "a"
					// It should find "a" element to merge with:
					//  * a
					//    * b
					//    c
					firstElement = ListWalker.first( positionParent, { sameIndent: true, lowerIndent: true } );
				} else {
					firstElement = positionParent.previousSibling;
				}
			} else {
				// In case of the forward merge there is no case as above, just merge with next sibling.
				firstElement = positionParent;
				lastElement = positionParent.nextSibling;
			}
		} else {
			firstElement = selection.getFirstPosition().parent;
			lastElement = selection.getLastPosition().parent;
		}

		return { firstElement, lastElement };
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/documentlistsplitcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/documentlistsplitcommand
 */




/**
 * The document list split command that splits the list item at the selection.
 *
 * It is used by the {@link module:list/documentlist~DocumentList document list feature}.
 *
 * @extends module:core/command~Command
 */
class DocumentListSplitCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {'before'|'after'} direction Whether list item should be split before or after the selected block.
	 */
	constructor( editor, direction ) {
		super( editor );

		/**
		 * Whether list item should be split before or after the selected block.
		 *
		 * @readonly
		 * @private
		 * @member {'before'|'after'}
		 */
		this._direction = direction;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Splits the list item at the selection.
	 *
	 * @fires execute
	 * @fires afterExecute
	 */
	execute() {
		const editor = this.editor;

		editor.model.change( writer => {
			const changedBlocks = splitListItemBefore( this._getStartBlock(), writer );

			this._fireAfterExecute( changedBlocks );
		} );
	}

	/**
	 * Fires the `afterExecute` event.
	 *
	 * @private
	 * @param {Array.<module:engine/model/element~Element>} changedBlocks The changed list elements.
	 */
	_fireAfterExecute( changedBlocks ) {
		/**
		 * Event fired by the {@link #execute} method.
		 *
		 * It allows to execute an action after executing the {@link ~DocumentListSplitCommand#execute} method,
		 * for example adjusting attributes of changed list items.
		 *
		 * @protected
		 * @event afterExecute
		 */
		this.fire( 'afterExecute', sortBlocks( new Set( changedBlocks ) ) );
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		const selection = this.editor.model.document.selection;
		const block = this._getStartBlock();

		return selection.isCollapsed &&
			isListItemBlock( block ) &&
			!isFirstBlockOfListItem( block );
	}

	/**
	 * Returns the model element that is the main focus of the command (according to the current selection and command direction).
	 *
	 * @private
	 * @returns {module:engine/model/element~Element}
	 */
	_getStartBlock() {
		const doc = this.editor.model.document;
		const positionParent = doc.selection.getFirstPosition().parent;

		return this._direction == 'before' ? positionParent : positionParent.nextSibling;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/utils/view.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/utils/view
 */

/**
 * Checks if view element is a list type (ul or ol).
 *
 * @protected
 * @param {module:engine/view/element~Element} viewElement
 * @returns {Boolean}
 */
function isListView( viewElement ) {
	return viewElement.is( 'element', 'ol' ) || viewElement.is( 'element', 'ul' );
}

/**
 * Checks if view element is a list item (li).
 *
 * @protected
 * @param {module:engine/view/element~Element} viewElement
 * @returns {Boolean}
 */
function isListItemView( viewElement ) {
	return viewElement.is( 'element', 'li' );
}

/**
 * Calculates the indent value for a list item. Handles HTML compliant and non-compliant lists.
 *
 * Also, fixes non HTML compliant lists indents:
 *
 * 		before:                                     fixed list:
 * 		OL                                          OL
 * 		|-> LI (parent LIs: 0)                      |-> LI     (indent: 0)
 * 		    |-> OL                                  |-> OL
 * 		        |-> OL                                  |
 * 		        |   |-> OL                              |
 * 		        |       |-> OL                          |
 * 		        |           |-> LI (parent LIs: 1)      |-> LI (indent: 1)
 * 		        |-> LI (parent LIs: 1)                  |-> LI (indent: 1)
 *
 * 		before:                                     fixed list:
 * 		OL                                          OL
 * 		|-> OL                                      |
 * 		    |-> OL                                  |
 * 		         |-> OL                             |
 * 		             |-> LI (parent LIs: 0)         |-> LI        (indent: 0)
 *
 * 		before:                                     fixed list:
 * 		OL                                          OL
 * 		|-> LI (parent LIs: 0)                      |-> LI         (indent: 0)
 * 		|-> OL                                          |-> OL
 * 		    |-> LI (parent LIs: 0)                          |-> LI (indent: 1)
 *
 * @protected
 * @param {module:engine/view/element~Element} listItem
 * @returns {Number}
 */
function getIndent( listItem ) {
	let indent = 0;
	let parent = listItem.parent;

	while ( parent ) {
		// Each LI in the tree will result in an increased indent for HTML compliant lists.
		if ( isListItemView( parent ) ) {
			indent++;
		} else {
			// If however the list is nested in other list we should check previous sibling of any of the list elements...
			const previousSibling = parent.previousSibling;

			// ...because the we might need increase its indent:
			//		before:                           fixed list:
			//		OL                                OL
			//		|-> LI (parent LIs: 0)            |-> LI         (indent: 0)
			//		|-> OL                                |-> OL
			//		    |-> LI (parent LIs: 0)                |-> LI (indent: 1)
			if ( previousSibling && isListItemView( previousSibling ) ) {
				indent++;
			}
		}

		parent = parent.parent;
	}

	return indent;
}

/**
 * Creates a list attribute element (ol or ul).
 *
 * @protected
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The downcast writer.
 * @param {Number} indent The list item indent.
 * @param {'bulleted'|'numbered'} type The list type.
 * @returns {module:engine/view/attributeelement~AttributeElement}
 */
function createListElement( writer, indent, type, id = getViewElementIdForListType( type, indent ) ) {
	// Negative priorities so that restricted editing attribute won't wrap lists.
	return writer.createAttributeElement( getViewElementNameForListType( type ), null, {
		priority: 2 * indent / 100 - 100,
		id
	} );
}

/**
 * Creates a list item attribute element (li).
 *
 * @protected
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The downcast writer.
 * @param {Number} indent The list item indent.
 * @param {String} id The list item ID.
 * @returns {module:engine/view/attributeelement~AttributeElement}
 */
function createListItemElement( writer, indent, id ) {
	// Negative priorities so that restricted editing attribute won't wrap list items.
	return writer.createAttributeElement( 'li', null, {
		priority: ( 2 * indent + 1 ) / 100 - 100,
		id
	} );
}

/**
 * Returns a view element name for the given list type.
 *
 * @protected
 * @param {'bulleted'|'numbered'} type The list type.
 * @returns {String}
 */
function getViewElementNameForListType( type ) {
	return type == 'numbered' ? 'ol' : 'ul';
}

/**
 * Returns a view element ID for the given list type and indent.
 *
 * @protected
 * @param {'bulleted'|'numbered'} type The list type.
 * @param {Number} indent The list indent level.
 * @returns {String}
 */
function getViewElementIdForListType( type, indent ) {
	return `list-${ type }-${ indent }`;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/utils/postfixers.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/utils/postfixers
 */




/**
 * Based on the provided positions looks for the list head and stores it in the provided map.
 *
 * @protected
 * @param {module:engine/model/position~Position} position The search starting position.
 * @param {Map.<module:engine/model/element~Element,module:engine/model/element~Element>} itemToListHead The map from list item element
 * to the list head element.
 */
function findAndAddListHeadToMap( position, itemToListHead ) {
	const previousNode = position.nodeBefore;

	if ( !isListItemBlock( previousNode ) ) {
		const item = position.nodeAfter;

		if ( isListItemBlock( item ) ) {
			itemToListHead.set( item, item );
		}
	} else {
		let listHead = previousNode;

		for ( { node: listHead } of iterateSiblingListBlocks( listHead, 'backward' ) ) {
			if ( itemToListHead.has( listHead ) ) {
				return;
			}
		}

		itemToListHead.set( previousNode, listHead );
	}
}

/**
 * Scans the list starting from the given list head element and fixes items' indentation.
 *
 * @protected
 * @param {Iterable.<module:list/documentlist/utils/listwalker~ListIteratorValue>} listNodes The iterable of list nodes.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {Boolean} Whether the model was modified.
 */
function fixListIndents( listNodes, writer ) {
	let maxIndent = 0; // Guards local sublist max indents that need fixing.
	let prevIndent = -1; // Previous item indent.
	let fixBy = null;
	let applied = false;

	for ( const { node } of listNodes ) {
		const itemIndent = node.getAttribute( 'listIndent' );

		if ( itemIndent > maxIndent ) {
			let newIndent;

			if ( fixBy === null ) {
				fixBy = itemIndent - maxIndent;
				newIndent = maxIndent;
			} else {
				if ( fixBy > itemIndent ) {
					fixBy = itemIndent;
				}

				newIndent = itemIndent - fixBy;
			}

			if ( newIndent > prevIndent + 1 ) {
				newIndent = prevIndent + 1;
			}

			writer.setAttribute( 'listIndent', newIndent, node );

			applied = true;
			prevIndent = newIndent;
		} else {
			fixBy = null;
			maxIndent = itemIndent + 1;
			prevIndent = itemIndent;
		}
	}

	return applied;
}

/**
 * Scans the list starting from the given list head element and fixes items' types.
 *
 * @protected
 * @param {Iterable.<module:list/documentlist/utils/listwalker~ListIteratorValue>} listNodes The iterable of list nodes.
 * @param {Set.<String>} seenIds The set of already known IDs.
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @returns {Boolean} Whether the model was modified.
 */
function fixListItemIds( listNodes, seenIds, writer ) {
	const visited = new Set();
	let applied = false;

	for ( const { node } of listNodes ) {
		if ( visited.has( node ) ) {
			continue;
		}

		let listType = node.getAttribute( 'listType' );
		let listItemId = node.getAttribute( 'listItemId' );

		// Use a new ID if this one was spot earlier (even in other list).
		if ( seenIds.has( listItemId ) ) {
			listItemId = ListItemUid.next();
		}

		seenIds.add( listItemId );

		for ( const block of getListItemBlocks( node, { direction: 'forward' } ) ) {
			visited.add( block );

			// Use a new ID if a block of a bigger list item has different type.
			if ( block.getAttribute( 'listType' ) != listType ) {
				listItemId = ListItemUid.next();
				listType = block.getAttribute( 'listType' );
			}

			if ( block.getAttribute( 'listItemId' ) != listItemId ) {
				writer.setAttribute( 'listItemId', listItemId, block );

				applied = true;
			}
		}
	}

	return applied;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/converters
 */








/**
 * Returns the upcast converter for list items. It's supposed to work after the block converters (content inside list items) is converted.
 *
 * @protected
 * @returns {Function}
 */
function listItemUpcastConverter() {
	return ( evt, data, conversionApi ) => {
		const { writer, schema } = conversionApi;

		if ( !data.modelRange ) {
			return;
		}

		const items = Array.from( data.modelRange.getItems( { shallow: true } ) )
			.filter( item => schema.checkAttribute( item, 'listItemId' ) );

		if ( !items.length ) {
			return;
		}

		const attributes = {
			listItemId: ListItemUid.next(),
			listIndent: getIndent( data.viewItem ),
			listType: data.viewItem.parent && data.viewItem.parent.name == 'ol' ? 'numbered' : 'bulleted'
		};

		for ( const item of items ) {
			// Set list attributes only on same level items, those nested deeper are already handled by the recursive conversion.
			if ( !isListItemBlock( item ) ) {
				writer.setAttributes( attributes, item );
			}
		}

		if ( items.length > 1 ) {
			// Make sure that list item that contain only nested list will preserve paragraph for itself:
			//	<ul>
			//		<li>
			//			<p></p>  <-- this one must be kept
			//			<ul>
			//				<li></li>
			//			</ul>
			//		</li>
			//	</ul>
			if ( items[ 1 ].getAttribute( 'listItemId' ) != attributes.listItemId ) {
				conversionApi.keepEmptyElement( items[ 0 ] );
			}
		}
	};
}

/**
 * Returns the upcast converter for the `<ul>` and `<ol>` view elements that cleans the input view of garbage.
 * This is mostly to clean whitespaces from between the `<li>` view elements inside the view list element, however, also
 * incorrect data can be cleared if the view was incorrect.
 *
 * @protected
 * @returns {Function}
 */
function listUpcastCleanList() {
	return ( evt, data, conversionApi ) => {
		if ( !conversionApi.consumable.test( data.viewItem, { name: true } ) ) {
			return;
		}

		const viewWriter = new UpcastWriter( data.viewItem.document );

		for ( const child of Array.from( data.viewItem.getChildren() ) ) {
			if ( !isListItemView( child ) && !isListView( child ) ) {
				viewWriter.remove( child );
			}
		}
	};
}

/**
 * Returns a model document change:data event listener that triggers conversion of related items if needed.
 *
 * @protected
 * @param {module:engine/model/model~Model} model The editor model.
 * @param {module:engine/controller/editingcontroller~EditingController} editing The editing controller.
 * @param {Array.<String>} attributeNames The list of all model list attributes (including registered strategies).
 * @param {module:list/documentlist/documentlistediting~DocumentListEditing} documentListEditing The document list editing plugin.
 * @return {Function}
 */
function reconvertItemsOnDataChange( model, editing, attributeNames, documentListEditing ) {
	return () => {
		const changes = model.document.differ.getChanges();
		const itemsToRefresh = [];
		const itemToListHead = new Map();
		const changedItems = new Set();

		for ( const entry of changes ) {
			if ( entry.type == 'insert' && entry.name != '$text' ) {
				findAndAddListHeadToMap( entry.position, itemToListHead );

				// Insert of a non-list item.
				if ( !entry.attributes.has( 'listItemId' ) ) {
					findAndAddListHeadToMap( entry.position.getShiftedBy( entry.length ), itemToListHead );
				} else {
					changedItems.add( entry.position.nodeAfter );
				}
			}
			// Removed list item.
			else if ( entry.type == 'remove' && entry.attributes.has( 'listItemId' ) ) {
				findAndAddListHeadToMap( entry.position, itemToListHead );
			}
			// Changed list attribute.
			else if ( entry.type == 'attribute' ) {
				const item = entry.range.start.nodeAfter;

				if ( attributeNames.includes( entry.attributeKey ) ) {
					findAndAddListHeadToMap( entry.range.start, itemToListHead );

					if ( entry.attributeNewValue === null ) {
						findAndAddListHeadToMap( entry.range.start.getShiftedBy( 1 ), itemToListHead );

						// Check if paragraph should be converted from bogus to plain paragraph.
						if ( doesItemParagraphRequiresRefresh( item ) ) {
							itemsToRefresh.push( item );
						}
					} else {
						changedItems.add( item );
					}
				} else if ( isListItemBlock( item ) ) {
					// Some other attribute was changed on the list item,
					// check if paragraph does not need to be converted to bogus or back.
					if ( doesItemParagraphRequiresRefresh( item ) ) {
						itemsToRefresh.push( item );
					}
				}
			}
		}

		for ( const listHead of itemToListHead.values() ) {
			itemsToRefresh.push( ...collectListItemsToRefresh( listHead, changedItems ) );
		}

		for ( const item of new Set( itemsToRefresh ) ) {
			editing.reconvertItem( item );
		}
	};

	function collectListItemsToRefresh( listHead, changedItems ) {
		const itemsToRefresh = [];
		const visited = new Set();
		const stack = [];

		for ( const { node, previous } of iterateSiblingListBlocks( listHead, 'forward' ) ) {
			if ( visited.has( node ) ) {
				continue;
			}

			const itemIndent = node.getAttribute( 'listIndent' );

			// Current node is at the lower indent so trim the stack.
			if ( previous && itemIndent < previous.getAttribute( 'listIndent' ) ) {
				stack.length = itemIndent + 1;
			}

			// Update the stack for the current indent level.
			stack[ itemIndent ] = Object.fromEntries(
				Array.from( node.getAttributes() )
					.filter( ( [ key ] ) => attributeNames.includes( key ) )
			);

			// Find all blocks of the current node.
			const blocks = getListItemBlocks( node, { direction: 'forward' } );

			for ( const block of blocks ) {
				visited.add( block );

				// Check if bogus vs plain paragraph needs refresh.
				if ( doesItemParagraphRequiresRefresh( block, blocks ) ) {
					itemsToRefresh.push( block );
				}
				// Check if wrapping with UL, OL, LIs needs refresh.
				else if ( doesItemWrappingRequiresRefresh( block, stack, changedItems ) ) {
					itemsToRefresh.push( block );
				}
			}
		}

		return itemsToRefresh;
	}

	function doesItemParagraphRequiresRefresh( item, blocks ) {
		if ( !item.is( 'element', 'paragraph' ) ) {
			return false;
		}

		const viewElement = editing.mapper.toViewElement( item );

		if ( !viewElement ) {
			return false;
		}

		const useBogus = shouldUseBogusParagraph( item, attributeNames, blocks );

		if ( useBogus && viewElement.is( 'element', 'p' ) ) {
			return true;
		} else if ( !useBogus && viewElement.is( 'element', 'span' ) ) {
			return true;
		}

		return false;
	}

	function doesItemWrappingRequiresRefresh( item, stack, changedItems ) {
		// Items directly affected by some "change" don't need a refresh, they will be converted by their own changes.
		if ( changedItems.has( item ) ) {
			return false;
		}

		const viewElement = editing.mapper.toViewElement( item );
		let indent = stack.length - 1;

		// Traverse down the stack to the root to verify if all ULs, OLs, and LIs are as expected.
		for (
			let element = viewElement.parent;
			!element.is( 'editableElement' );
			element = element.parent
		) {
			const isListItemElement = isListItemView( element );
			const isListElement = isListView( element );

			if ( !isListElement && !isListItemElement ) {
				continue;
			}

			/**
			 * Event fired on changes detected on the model list element to verify if the view representation of a list element
			 * is representing those attributes.
			 *
			 * It allows triggering a re-wrapping of a list item.
			 *
			 * **Note**: For convenience this event is namespaced and could be captured as `checkAttributes:list` or `checkAttributes:item`.
			 *
			 * @protected
			 * @event module:list/documentlist/documentlistediting~DocumentListEditing#event:checkAttributes
			 * @param {module:engine/view/element~Element} viewElement
			 * @param {Object} modelAttributes
			 */
			const eventName = `checkAttributes:${ isListItemElement ? 'item' : 'list' }`;
			const needsRefresh = documentListEditing.fire( eventName, {
				viewElement: element,
				modelAttributes: stack[ indent ]
			} );

			if ( needsRefresh ) {
				break;
			}

			if ( isListElement ) {
				indent--;

				// Don't need to iterate further if we already know that the item is wrapped appropriately.
				if ( indent < 0 ) {
					return false;
				}
			}
		}

		return true;
	}
}

/**
 * Returns the list item downcast converter.
 *
 * @protected
 * @param {Array.<String>} attributeNames A list of attribute names that should be converted if are set.
 * @param {Array.<module:list/documentlistproperties/documentlistpropertiesediting~AttributeStrategy>} strategies The strategies.
 * @param {module:engine/model/model~Model} model The model.
 * @returns {Function}
 */
function listItemDowncastConverter( attributeNames, strategies, model ) {
	const consumer = createAttributesConsumer( attributeNames );

	return ( evt, data, conversionApi ) => {
		const { writer, mapper, consumable } = conversionApi;

		const listItem = data.item;

		if ( !attributeNames.includes( data.attributeKey ) ) {
			return;
		}

		// Test if attributes on the converted items are not consumed.
		if ( !consumer( listItem, consumable ) ) {
			return;
		}

		// Use positions mapping instead of mapper.toViewElement( listItem ) to find outermost view element.
		// This is for cases when mapping is using inner view element like in the code blocks (pre > code).
		const viewElement = findMappedViewElement( listItem, mapper, model );

		// Unwrap element from current list wrappers.
		unwrapListItemBlock( viewElement, writer );

		// Then wrap them with the new list wrappers.
		wrapListItemBlock( listItem, writer.createRangeOn( viewElement ), strategies, writer );
	};
}

/**
 * Returns the bogus paragraph view element creator. A bogus paragraph is used if a list item contains only a single block or nested list.
 *
 * @protected
 * @param {Array.<String>} attributeNames The list of all model list attributes (including registered strategies).
 * @param {Object} [options]
 * @param {Boolean} [options.dataPipeline=false]
 * @returns {Function}
 */
function bogusParagraphCreator( attributeNames, { dataPipeline } = {} ) {
	return ( modelElement, { writer } ) => {
		// Convert only if a bogus paragraph should be used.
		if ( !shouldUseBogusParagraph( modelElement, attributeNames ) ) {
			return;
		}

		const viewElement = writer.createContainerElement( 'span', { class: 'ck-list-bogus-paragraph' } );

		if ( dataPipeline ) {
			writer.setCustomProperty( 'dataPipeline:transparentRendering', true, viewElement );
		}

		return viewElement;
	};
}

/**
 * Helper for mapping mode to view elements. It's using positions mapping instead of mapper.toViewElement( element )
 * to find outermost view element. This is for cases when mapping is using inner view element like in the code blocks (pre > code).
 *
 * @protected
 * @param {module:engine/model/element~Element} element The model element.
 * @param {module:engine/conversion/mapper~Mapper} mapper The mapper instance.
 * @param {module:engine/model/model~Model} model The model.
 * @returns {module:engine/view/element~Element|null}
 */
function findMappedViewElement( element, mapper, model ) {
	const modelRange = model.createRangeOn( element );
	const viewRange = mapper.toViewRange( modelRange ).getTrimmed();

	return viewRange.getContainedElement();
}

// Unwraps all ol, ul, and li attribute elements that are wrapping the provided view element.
function unwrapListItemBlock( viewElement, viewWriter ) {
	let attributeElement = viewElement.parent;

	while ( attributeElement.is( 'attributeElement' ) && [ 'ul', 'ol', 'li' ].includes( attributeElement.name ) ) {
		const parentElement = attributeElement.parent;

		viewWriter.unwrap( viewWriter.createRangeOn( viewElement ), attributeElement );

		attributeElement = parentElement;
	}
}

// Wraps the given list item with appropriate attribute elements for ul, ol, and li.
function wrapListItemBlock( listItem, viewRange, strategies, writer ) {
	if ( !listItem.hasAttribute( 'listIndent' ) ) {
		return;
	}

	const listItemIndent = listItem.getAttribute( 'listIndent' );
	let currentListItem = listItem;

	for ( let indent = listItemIndent; indent >= 0; indent-- ) {
		const listItemViewElement = createListItemElement( writer, indent, currentListItem.getAttribute( 'listItemId' ) );
		const listViewElement = createListElement( writer, indent, currentListItem.getAttribute( 'listType' ) );

		for ( const strategy of strategies ) {
			if ( currentListItem.hasAttribute( strategy.attributeName ) ) {
				strategy.setAttributeOnDowncast(
					writer,
					currentListItem.getAttribute( strategy.attributeName ),
					strategy.scope == 'list' ? listViewElement : listItemViewElement
				);
			}
		}

		viewRange = writer.wrap( viewRange, listItemViewElement );
		viewRange = writer.wrap( viewRange, listViewElement );

		if ( indent == 0 ) {
			break;
		}

		currentListItem = ListWalker.first( currentListItem, { lowerIndent: true } );

		// There is no list item with lower indent, this means this is a document fragment containing
		// only a part of nested list (like copy to clipboard) so we don't need to try to wrap it further.
		if ( !currentListItem ) {
			break;
		}
	}
}

// Returns the function that is responsible for consuming attributes that are set on the model node.
function createAttributesConsumer( attributeNames ) {
	return ( node, consumable ) => {
		const events = [];

		// Collect all set attributes that are triggering conversion.
		for ( const attributeName of attributeNames ) {
			if ( node.hasAttribute( attributeName ) ) {
				events.push( `attribute:${ attributeName }` );
			}
		}

		if ( !events.every( event => consumable.test( node, event ) !== false ) ) {
			return false;
		}

		events.forEach( event => consumable.consume( node, event ) );

		return true;
	};
}

// Whether the given item should be rendered as a bogus paragraph.
function shouldUseBogusParagraph( item, attributeNames, blocks = getAllListItemBlocks( item ) ) {
	if ( !isListItemBlock( item ) ) {
		return false;
	}

	for ( const attributeKey of item.getAttributeKeys() ) {
		// Ignore selection attributes stored on block elements.
		if ( attributeKey.startsWith( 'selection:' ) ) {
			continue;
		}

		// Don't use bogus paragraph if there are attributes from other features.
		if ( !attributeNames.includes( attributeKey ) ) {
			return false;
		}
	}

	return blocks.length < 2;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-list/theme/documentlist.css
var documentlist = __webpack_require__(8676);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/documentlist.css

            

var documentlist_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

documentlist_options.insert = "head";
documentlist_options.singleton = true;

var documentlist_update = injectStylesIntoStyleTag_default()(documentlist/* default */.Z, documentlist_options);



/* harmony default export */ const theme_documentlist = (documentlist/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist/documentlistediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist/documentlistediting
 */


















/**
 * A list of base list model attributes.
 *
 * @private
 */
const LIST_BASE_ATTRIBUTES = [ 'listType', 'listIndent', 'listItemId' ];

/**
 * The editing part of the document-list feature. It handles creating, editing and removing lists and list items.
 *
 * @extends module:core/plugin~Plugin
 */
class DocumentListEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DocumentListEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ enter_Enter, delete_Delete ];
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * The list of registered downcast strategies.
		 *
		 * @private
		 * @type {Array.<module:list/documentlist/documentlistediting~DowncastStrategy>}
		 */
		this._downcastStrategies = [];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const model = editor.model;

		if ( editor.plugins.has( 'ListEditing' ) ) {
			/**
			 * The `DocumentList` feature can not be loaded together with the `List` plugin.
			 *
			 * @error document-list-feature-conflict
			 * @param {String} conflictPlugin Name of the plugin.
			 */
			throw new CKEditorError( 'document-list-feature-conflict', this, { conflictPlugin: 'ListEditing' } );
		}

		model.schema.extend( '$container', { allowAttributes: LIST_BASE_ATTRIBUTES } );
		model.schema.extend( '$block', { allowAttributes: LIST_BASE_ATTRIBUTES } );
		model.schema.extend( '$blockObject', { allowAttributes: LIST_BASE_ATTRIBUTES } );

		for ( const attribute of LIST_BASE_ATTRIBUTES ) {
			model.schema.setAttributeProperties( attribute, {
				copyOnReplace: true
			} );
		}

		// Register commands.
		editor.commands.add( 'numberedList', new DocumentListCommand( editor, 'numbered' ) );
		editor.commands.add( 'bulletedList', new DocumentListCommand( editor, 'bulleted' ) );

		editor.commands.add( 'indentList', new DocumentListIndentCommand( editor, 'forward' ) );
		editor.commands.add( 'outdentList', new DocumentListIndentCommand( editor, 'backward' ) );

		editor.commands.add( 'mergeListItemBackward', new DocumentListMergeCommand( editor, 'backward' ) );
		editor.commands.add( 'mergeListItemForward', new DocumentListMergeCommand( editor, 'forward' ) );

		editor.commands.add( 'splitListItemBefore', new DocumentListSplitCommand( editor, 'before' ) );
		editor.commands.add( 'splitListItemAfter', new DocumentListSplitCommand( editor, 'after' ) );

		this._setupDeleteIntegration();
		this._setupEnterIntegration();
		this._setupTabIntegration();
		this._setupClipboardIntegration();
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const editor = this.editor;
		const commands = editor.commands;
		const indent = commands.get( 'indent' );
		const outdent = commands.get( 'outdent' );

		if ( indent ) {
			// Priority is high due to integration with `IndentBlock` plugin. We want to indent list first and if it's not possible
			// user can indent content with `IndentBlock` plugin.
			indent.registerChildCommand( commands.get( 'indentList' ), { priority: 'high' } );
		}

		if ( outdent ) {
			// Priority is lowest due to integration with `IndentBlock` and `IndentCode` plugins.
			// First we want to allow user to outdent all indendations from other features then he can oudent list item.
			outdent.registerChildCommand( commands.get( 'outdentList' ), { priority: 'lowest' } );
		}

		// Register conversion and model post-fixer after other plugins had a chance to register their attribute strategies.
		this._setupModelPostFixing();
		this._setupConversion();
	}

	/**
	 * Registers a downcast strategy.
	 *
	 * **Note**: Strategies must be registered in the `Plugin#init()` phase so that it can be applied
	 * in the `DocumentListEditing#afterInit()`.
	 *
	 * @param {module:list/documentlist/documentlistediting~DowncastStrategy} strategy The downcast strategy to register.
	 */
	registerDowncastStrategy( strategy ) {
		this._downcastStrategies.push( strategy );
	}

	/**
	 * Returns list of model attribute names that should affect downcast conversion.
	 *
	 * @private
	 */
	_getListAttributeNames() {
		return [
			...LIST_BASE_ATTRIBUTES,
			...this._downcastStrategies.map( strategy => strategy.attributeName )
		];
	}

	/**
	 * Attaches the listener to the {@link module:engine/view/document~Document#event:delete} event and handles backspace/delete
	 * keys in and around document lists.
	 *
	 * @private
	 */
	_setupDeleteIntegration() {
		const editor = this.editor;
		const mergeBackwardCommand = editor.commands.get( 'mergeListItemBackward' );
		const mergeForwardCommand = editor.commands.get( 'mergeListItemForward' );

		this.listenTo( editor.editing.view.document, 'delete', ( evt, data ) => {
			const selection = editor.model.document.selection;

			// Let the Widget plugin take care of block widgets while deleting (https://github.com/ckeditor/ckeditor5/issues/11346).
			if ( getSelectedBlockObject( editor.model ) ) {
				return;
			}

			editor.model.change( () => {
				const firstPosition = selection.getFirstPosition();

				if ( selection.isCollapsed && data.direction == 'backward' ) {
					if ( !firstPosition.isAtStart ) {
						return;
					}

					const positionParent = firstPosition.parent;

					if ( !isListItemBlock( positionParent ) ) {
						return;
					}

					const previousBlock = ListWalker.first( positionParent, {
						sameAttributes: 'listType',
						sameIndent: true
					} );

					// Outdent the first block of a first list item.
					if ( !previousBlock && positionParent.getAttribute( 'listIndent' ) === 0 ) {
						if ( !isLastBlockOfListItem( positionParent ) ) {
							editor.execute( 'splitListItemAfter' );
						}

						editor.execute( 'outdentList' );
					}
					// Merge block with previous one (on the block level or on the content level).
					else {
						if ( !mergeBackwardCommand.isEnabled ) {
							return;
						}

						mergeBackwardCommand.execute( {
							shouldMergeOnBlocksContentLevel: shouldMergeOnBlocksContentLevel( editor.model, 'backward' )
						} );
					}

					data.preventDefault();
					evt.stop();
				}
				// Non-collapsed selection or forward delete.
				else {
					// Collapsed selection should trigger forward merging only if at the end of a block.
					if ( selection.isCollapsed && !selection.getLastPosition().isAtEnd ) {
						return;
					}

					if ( !mergeForwardCommand.isEnabled ) {
						return;
					}

					mergeForwardCommand.execute( {
						shouldMergeOnBlocksContentLevel: shouldMergeOnBlocksContentLevel( editor.model, 'forward' )
					} );

					data.preventDefault();
					evt.stop();
				}
			} );
		}, { context: 'li' } );
	}

	/**
	 * Attaches a listener to the {@link module:engine/view/document~Document#event:enter} event and handles enter key press
	 * in document lists.
	 *
	 * @private
	 */
	_setupEnterIntegration() {
		const editor = this.editor;
		const model = editor.model;
		const commands = editor.commands;
		const enterCommand = commands.get( 'enter' );

		// Overwrite the default Enter key behavior: outdent or split the list in certain cases.
		this.listenTo( editor.editing.view.document, 'enter', ( evt, data ) => {
			const doc = model.document;
			const positionParent = doc.selection.getFirstPosition().parent;

			if (
				doc.selection.isCollapsed &&
				isListItemBlock( positionParent ) &&
				positionParent.isEmpty &&
				!data.isSoft
			) {
				const isFirstBlock = isFirstBlockOfListItem( positionParent );
				const isLastBlock = isLastBlockOfListItem( positionParent );

				// * a      →      * a
				// * []     →      []
				if ( isFirstBlock && isLastBlock ) {
					editor.execute( 'outdentList' );

					data.preventDefault();
					evt.stop();
				}
				// * []     →      * []
				//   a      →      * a
				else if ( isFirstBlock && !isLastBlock ) {
					editor.execute( 'splitListItemAfter' );

					data.preventDefault();
					evt.stop();
				}
				// * a      →      * a
				//   []     →      * []
				else if ( isLastBlock ) {
					editor.execute( 'splitListItemBefore' );

					data.preventDefault();
					evt.stop();
				}
			}
		}, { context: 'li' } );

		// In some cases, after the default block splitting, we want to modify the new block to become a new list item
		// instead of an additional block in the same list item.
		this.listenTo( enterCommand, 'afterExecute', () => {
			const splitCommand = commands.get( 'splitListItemBefore' );

			// The command has not refreshed because the change block related to EnterCommand#execute() is not over yet.
			// Let's keep it up to date and take advantage of DocumentListSplitCommand#isEnabled.
			splitCommand.refresh();

			if ( !splitCommand.isEnabled ) {
				return;
			}

			const doc = editor.model.document;
			const positionParent = doc.selection.getLastPosition().parent;
			const listItemBlocks = getAllListItemBlocks( positionParent );

			// Keep in mind this split happens after the default enter handler was executed. For instance:
			//
			// │       Initial state       │    After default enter    │   Here in #afterExecute   │
			// ├───────────────────────────┼───────────────────────────┼───────────────────────────┤
			// │          * a[]            │           * a             │           * a             │
			// │                           │             []            │           * []            │
			if ( listItemBlocks.length === 2 ) {
				splitCommand.execute();
			}
		} );
	}

	/**
	 * Attaches a listener to the {@link module:engine/view/document~Document#event:tab} event and handles tab key and tab+shift keys
	 * presses in document lists.
	 *
	 * @private
	 */
	_setupTabIntegration() {
		const editor = this.editor;

		this.listenTo( editor.editing.view.document, 'tab', ( evt, data ) => {
			const commandName = data.shiftKey ? 'outdentList' : 'indentList';
			const command = this.editor.commands.get( commandName );

			if ( command.isEnabled ) {
				editor.execute( commandName );

				data.stopPropagation();
				data.preventDefault();
				evt.stop();
			}
		}, { context: 'li' } );
	}

	/**
	 * Registers the conversion helpers for the document-list feature.
	 * @private
	 */
	_setupConversion() {
		const editor = this.editor;
		const model = editor.model;
		const attributeNames = this._getListAttributeNames();

		editor.conversion.for( 'upcast' )
			.elementToElement( { view: 'li', model: 'paragraph' } )
			.add( dispatcher => {
				dispatcher.on( 'element:li', listItemUpcastConverter() );
				dispatcher.on( 'element:ul', listUpcastCleanList(), { priority: 'high' } );
				dispatcher.on( 'element:ol', listUpcastCleanList(), { priority: 'high' } );
			} );

		editor.conversion.for( 'editingDowncast' )
			.elementToElement( {
				model: 'paragraph',
				view: bogusParagraphCreator( attributeNames ),
				converterPriority: 'high'
			} );

		editor.conversion.for( 'dataDowncast' )
			.elementToElement( {
				model: 'paragraph',
				view: bogusParagraphCreator( attributeNames, { dataPipeline: true } ),
				converterPriority: 'high'
			} );

		editor.conversion.for( 'downcast' )
			.add( dispatcher => {
				dispatcher.on( 'attribute', listItemDowncastConverter( attributeNames, this._downcastStrategies, model ) );
			} );

		this.listenTo( model.document, 'change:data', reconvertItemsOnDataChange( model, editor.editing, attributeNames, this ) );

		// For LI verify if an ID of the attribute element is correct.
		this.on( 'checkAttributes:item', ( evt, { viewElement, modelAttributes } ) => {
			if ( viewElement.id != modelAttributes.listItemId ) {
				evt.return = true;
				evt.stop();
			}
		} );

		// For UL and OL check if the name and ID of element is correct.
		this.on( 'checkAttributes:list', ( evt, { viewElement, modelAttributes } ) => {
			if (
				viewElement.name != getViewElementNameForListType( modelAttributes.listType ) ||
				viewElement.id != getViewElementIdForListType( modelAttributes.listType, modelAttributes.listIndent )
			) {
				evt.return = true;
				evt.stop();
			}
		} );
	}

	/**
	 * Registers model post-fixers.
	 *
	 * @private
	 */
	_setupModelPostFixing() {
		const model = this.editor.model;
		const attributeNames = this._getListAttributeNames();

		// Register list fixing.
		// First the low level handler.
		model.document.registerPostFixer( writer => documentlistediting_modelChangePostFixer( model, writer, attributeNames, this ) );

		// Then the callbacks for the specific lists.
		// The indentation fixing must be the first one...
		this.on( 'postFixer', ( evt, { listNodes, writer } ) => {
			evt.return = fixListIndents( listNodes, writer ) || evt.return;
		}, { priority: 'high' } );

		// ...then the item ids... and after that other fixers that rely on the correct indentation and ids.
		this.on( 'postFixer', ( evt, { listNodes, writer, seenIds } ) => {
			evt.return = fixListItemIds( listNodes, seenIds, writer ) || evt.return;
		}, { priority: 'high' } );
	}

	/**
	 * Integrates the feature with the clipboard via {@link module:engine/model/model~Model#insertContent} and
	 * {@link module:engine/model/model~Model#getSelectedContent}.
	 *
	 * @private
	 */
	_setupClipboardIntegration() {
		const model = this.editor.model;

		this.listenTo( model, 'insertContent', createModelIndentPasteFixer( model ), { priority: 'high' } );

		// To enhance the UX, the editor should not copy list attributes to the clipboard if the selection
		// started and ended in the same list item.
		//
		// If the selection was enclosed in a single list item, there is a good chance the user did not want it
		// copied as a list item but plain blocks.
		//
		// This avoids pasting orphaned list items instead of paragraphs, for instance, straight into the root.
		//
		//	                       ┌─────────────────────┬───────────────────┐
		//	                       │ Selection           │ Clipboard content │
		//	                       ├─────────────────────┼───────────────────┤
		//	                       │ [* <Widget />]      │ <Widget />        │
		//	                       ├─────────────────────┼───────────────────┤
		//	                       │ [* Foo]             │ Foo               │
		//	                       ├─────────────────────┼───────────────────┤
		//	                       │ * Foo [bar] baz     │ bar               │
		//	                       ├─────────────────────┼───────────────────┤
		//	                       │ * Fo[o              │ o                 │
		//	                       │   ba]r              │ ba                │
		//	                       ├─────────────────────┼───────────────────┤
		//	                       │ * Fo[o              │ * o               │
		//	                       │ * ba]r              │ * ba              │
		//	                       ├─────────────────────┼───────────────────┤
		//	                       │ [* Foo              │ * Foo             │
		//	                       │  * bar]             │ * bar             │
		//	                       └─────────────────────┴───────────────────┘
		//
		// See https://github.com/ckeditor/ckeditor5/issues/11608.
		this.listenTo( model, 'getSelectedContent', ( evt, [ selection ] ) => {
			const isSingleListItemSelected = isSingleListItem( Array.from( selection.getSelectedBlocks() ) );

			if ( isSingleListItemSelected ) {
				model.change( writer => removeListAttributes( Array.from( evt.return.getChildren() ), writer ) );
			}
		} );
	}
}

/**
 * @typedef {Object} module:list/documentlist/documentlistediting~DowncastStrategy
 * @property {'list'|'item'} scope The scope of the downcast (whether it applies to LI or OL/UL).
 * @property {String} attributeName The model attribute name.
 * @property {Function} setAttributeOnDowncast Sets the property on the view element.
 */

// Post-fixer that reacts to changes on document and fixes incorrect model states (invalid `listItemId` and `listIndent` values).
//
// In the example below, there is a correct list structure.
// Then the middle element is removed so the list structure will become incorrect:
//
//		<paragraph listType="bulleted" listItemId="a" listIndent=0>Item 1</paragraph>
//		<paragraph listType="bulleted" listItemId="b" listIndent=1>Item 2</paragraph>   <--- this is removed.
//		<paragraph listType="bulleted" listItemId="c" listIndent=2>Item 3</paragraph>
//
// The list structure after the middle element is removed:
//
// 		<paragraph listType="bulleted" listItemId="a" listIndent=0>Item 1</paragraph>
//		<paragraph listType="bulleted" listItemId="c" listIndent=2>Item 3</paragraph>
//
// Should become:
//
//		<paragraph listType="bulleted" listItemId="a" listIndent=0>Item 1</paragraph>
//		<paragraph listType="bulleted" listItemId="c" listIndent=1>Item 3</paragraph>   <--- note that indent got post-fixed.
//
// @param {module:engine/model/model~Model} model The data model.
// @param {module:engine/model/writer~Writer} writer The writer to do changes with.
// @param {Array.<String>} attributeNames The list of all model list attributes (including registered strategies).
// @param {module:list/documentlist/documentlistediting~DocumentListEditing} documentListEditing The document list editing plugin.
// @returns {Boolean} `true` if any change has been applied, `false` otherwise.
function documentlistediting_modelChangePostFixer( model, writer, attributeNames, documentListEditing ) {
	const changes = model.document.differ.getChanges();
	const itemToListHead = new Map();

	let applied = false;

	for ( const entry of changes ) {
		if ( entry.type == 'insert' && entry.name != '$text' ) {
			const item = entry.position.nodeAfter;

			// Remove attributes in case of renamed element.
			if ( !model.schema.checkAttribute( item, 'listItemId' ) ) {
				for ( const attributeName of Array.from( item.getAttributeKeys() ) ) {
					if ( attributeNames.includes( attributeName ) ) {
						writer.removeAttribute( attributeName, item );

						applied = true;
					}
				}
			}

			findAndAddListHeadToMap( entry.position, itemToListHead );

			// Insert of a non-list item - check if there is a list after it.
			if ( !entry.attributes.has( 'listItemId' ) ) {
				findAndAddListHeadToMap( entry.position.getShiftedBy( entry.length ), itemToListHead );
			}

			// Check if there is no nested list.
			for ( const { item: innerItem, previousPosition } of model.createRangeIn( item ) ) {
				if ( isListItemBlock( innerItem ) ) {
					findAndAddListHeadToMap( previousPosition, itemToListHead );
				}
			}
		}
		// Removed list item or block adjacent to a list.
		else if ( entry.type == 'remove' ) {
			findAndAddListHeadToMap( entry.position, itemToListHead );
		}
		// Changed list item indent or type.
		else if ( entry.type == 'attribute' && attributeNames.includes( entry.attributeKey ) ) {
			findAndAddListHeadToMap( entry.range.start, itemToListHead );

			if ( entry.attributeNewValue === null ) {
				findAndAddListHeadToMap( entry.range.start.getShiftedBy( 1 ), itemToListHead );
			}
		}
	}

	// Make sure that IDs are not shared by split list.
	const seenIds = new Set();

	for ( const listHead of itemToListHead.values() ) {
		/**
		 * Event fired on changes detected on the model list element to verify if the view representation of a list element
		 * is representing those attributes.
		 *
		 * It allows triggering a re-wrapping of a list item.
		 *
		 * **Note**: For convenience this event is namespaced and could be captured as `checkAttributes:list` or `checkAttributes:item`.
		 *
		 * @protected
		 * @event module:list/documentlist/documentlistediting~DocumentListEditing#event:postFixer
		 * @param {module:engine/model/element~Element} listHead The head element of a list.
		 * @param {module:engine/model/writer~Writer} writer The writer to do changes with.
		 * @param {Set.<String>} seenIds The set of already known IDs.
		 * @param {Object} modelAttributes
		 * @returns {Boolean} If a post-fixer made a change of the model tree, it should return `true`.
		 */
		applied = documentListEditing.fire( 'postFixer', {
			listNodes: new ListBlocksIterable( listHead ),
			listHead,
			writer,
			seenIds
		} ) || applied;
	}

	return applied;
}

// A fixer for pasted content that includes list items.
//
// It fixes indentation of pasted list items so the pasted items match correctly to the context they are pasted into.
//
// Example:
//
//		<paragraph listType="bulleted" listItemId="a" listIndent=0>A</paragraph>
//		<paragraph listType="bulleted" listItemId="b" listIndent=1>B^</paragraph>
//		// At ^ paste:  <paragraph listType="bulleted" listItemId="x" listIndent=4>X</paragraph>
//		//              <paragraph listType="bulleted" listItemId="y" listIndent=5>Y</paragraph>
//		<paragraph listType="bulleted" listItemId="c" listIndent=2>C</paragraph>
//
// Should become:
//
//		<paragraph listType="bulleted" listItemId="a" listIndent=0>A</paragraph>
//		<paragraph listType="bulleted" listItemId="b" listIndent=1>BX</paragraph>
//		<paragraph listType="bulleted" listItemId="y" listIndent=2>Y/paragraph>
//		<paragraph listType="bulleted" listItemId="c" listIndent=2>C</paragraph>
//
function createModelIndentPasteFixer( model ) {
	return ( evt, [ content, selectable ] ) => {
		// Check whether inserted content starts from a `listItem`. If it does not, it means that there are some other
		// elements before it and there is no need to fix indents, because even if we insert that content into a list,
		// that list will be broken.
		// Note: we also need to handle singular elements because inserting item with indent 0 into 0,1,[],2
		// would create incorrect model.
		const item = content.is( 'documentFragment' ) ? content.getChild( 0 ) : content;

		if ( !isListItemBlock( item ) ) {
			return;
		}

		let selection;

		if ( !selectable ) {
			selection = model.document.selection;
		} else {
			selection = model.createSelection( selectable );
		}

		// Get a reference list item. Inserted list items will be fixed according to that item.
		const pos = selection.getFirstPosition();
		let refItem = null;

		if ( isListItemBlock( pos.parent ) ) {
			refItem = pos.parent;
		} else if ( isListItemBlock( pos.nodeBefore ) ) {
			refItem = pos.nodeBefore;
		}

		// If there is `refItem` it means that we do insert list items into an existing list.
		if ( !refItem ) {
			return;
		}

		// First list item in `data` has indent equal to 0 (it is a first list item). It should have indent equal
		// to the indent of reference item. We have to fix the first item and all of it's children and following siblings.
		// Indent of all those items has to be adjusted to reference item.
		const indentChange = refItem.getAttribute( 'listIndent' ) - item.getAttribute( 'listIndent' );

		// Fix only if there is anything to fix.
		if ( indentChange <= 0 ) {
			return;
		}

		model.change( writer => {
			// Adjust indent of all "first" list items in inserted data.
			for ( const { node } of iterateSiblingListBlocks( item, 'forward' ) ) {
				writer.setAttribute( 'listIndent', node.getAttribute( 'listIndent' ) + indentChange, node );
			}
		} );
	};
}

// Decides whether the merge should be accompanied by the model's `deleteContent()`, for instance, to get rid of the inline
// content in the selection or take advantage of the heuristics in `deleteContent()` that helps convert lists into paragraphs
// in certain cases.
//
// @param {module:engine/model/model~Model} model
// @param {'backward'|'forward'} direction
// @returns {Boolean}
function shouldMergeOnBlocksContentLevel( model, direction ) {
	const selection = model.document.selection;

	if ( !selection.isCollapsed ) {
		return !getSelectedBlockObject( model );
	}

	if ( direction === 'forward' ) {
		return true;
	}

	const firstPosition = selection.getFirstPosition();
	const positionParent = firstPosition.parent;
	const previousSibling = positionParent.previousSibling;

	if ( model.schema.isObject( previousSibling ) ) {
		return false;
	}

	if ( previousSibling.isEmpty ) {
		return true;
	}

	return isSingleListItem( [ positionParent, previousSibling ] );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/list/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/list/utils
 */




/**
 * Creates a list item {@link module:engine/view/containerelement~ContainerElement}.
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The writer instance.
 * @returns {module:engine/view/containerelement~ContainerElement}
 */
function createViewListItemElement( writer ) {
	const viewItem = writer.createContainerElement( 'li' );

	viewItem.getFillerOffset = getListItemFillerOffset;

	return viewItem;
}

/**
 * Helper function that creates a `<ul><li></li></ul>` or (`<ol>`) structure out of the given `modelItem` model `listItem` element.
 * Then, it binds the created view list item (`<li>`) with the model `listItem` element.
 * The function then returns the created view list item (`<li>`).
 *
 * @param {module:engine/model/item~Item} modelItem Model list item.
 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion interface.
 * @returns {module:engine/view/containerelement~ContainerElement} View list element.
 */
function utils_generateLiInUl( modelItem, conversionApi ) {
	const mapper = conversionApi.mapper;
	const viewWriter = conversionApi.writer;
	const listType = modelItem.getAttribute( 'listType' ) == 'numbered' ? 'ol' : 'ul';
	const viewItem = createViewListItemElement( viewWriter );

	const viewList = viewWriter.createContainerElement( listType, null );

	viewWriter.insert( viewWriter.createPositionAt( viewList, 0 ), viewItem );

	mapper.bindElements( modelItem, viewItem );

	return viewItem;
}

/**
 * Helper function that inserts a view list at a correct place and merges it with its siblings.
 * It takes a model list item element (`modelItem`) and a corresponding view list item element (`injectedItem`). The view list item
 * should be in a view list element (`<ul>` or `<ol>`) and should be its only child.
 * See comments below to better understand the algorithm.
 *
 * @param {module:engine/view/item~Item} modelItem Model list item.
 * @param {module:engine/view/containerelement~ContainerElement} injectedItem
 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion interface.
 * @param {module:engine/model/model~Model} model The model instance.
 */
function utils_injectViewList( modelItem, injectedItem, conversionApi, model ) {
	const injectedList = injectedItem.parent;
	const mapper = conversionApi.mapper;
	const viewWriter = conversionApi.writer;

	// The position where the view list will be inserted.
	let insertPosition = mapper.toViewPosition( model.createPositionBefore( modelItem ) );

	// 1. Find the previous list item that has the same or smaller indent. Basically we are looking for the first model item
	// that is a "parent" or "sibling" of the injected model item.
	// If there is no such list item, it means that the injected list item is the first item in "its list".
	const refItem = utils_getSiblingListItem( modelItem.previousSibling, {
		sameIndent: true,
		smallerIndent: true,
		listIndent: modelItem.getAttribute( 'listIndent' )
	} );
	const prevItem = modelItem.previousSibling;

	if ( refItem && refItem.getAttribute( 'listIndent' ) == modelItem.getAttribute( 'listIndent' ) ) {
		// There is a list item with the same indent - we found the same-level sibling.
		// Break the list after it. The inserted view item will be added in the broken space.
		const viewItem = mapper.toViewElement( refItem );
		insertPosition = viewWriter.breakContainer( viewWriter.createPositionAfter( viewItem ) );
	} else {
		// There is no list item with the same indent. Check the previous model item.
		if ( prevItem && prevItem.name == 'listItem' ) {
			// If it is a list item, it has to have a lower indent.
			// It means that the inserted item should be added to it as its nested item.
			insertPosition = mapper.toViewPosition( model.createPositionAt( prevItem, 'end' ) );

			// There could be some not mapped elements (eg. span in to-do list) but we need to insert
			// a nested list directly inside the li element.
			const mappedViewAncestor = mapper.findMappedViewAncestor( insertPosition );
			const nestedList = findNestedList( mappedViewAncestor );

			// If there already is some nested list, then use it's position.
			if ( nestedList ) {
				insertPosition = viewWriter.createPositionBefore( nestedList );
			} else {
				// Else just put new list on the end of list item content.
				insertPosition = viewWriter.createPositionAt( mappedViewAncestor, 'end' );
			}
		} else {
			// The previous item is not a list item (or does not exist at all).
			// Just map the position and insert the view item at the mapped position.
			insertPosition = mapper.toViewPosition( model.createPositionBefore( modelItem ) );
		}
	}

	insertPosition = utils_positionAfterUiElements( insertPosition );

	// Insert the view item.
	viewWriter.insert( insertPosition, injectedList );

	// 2. Handle possible children of the injected model item.
	if ( prevItem && prevItem.name == 'listItem' ) {
		const prevView = mapper.toViewElement( prevItem );

		const walkerBoundaries = viewWriter.createRange( viewWriter.createPositionAt( prevView, 0 ), insertPosition );
		const walker = walkerBoundaries.getWalker( { ignoreElementEnd: true } );

		for ( const value of walker ) {
			if ( value.item.is( 'element', 'li' ) ) {
				const breakPosition = viewWriter.breakContainer( viewWriter.createPositionBefore( value.item ) );
				const viewList = value.item.parent;

				const targetPosition = viewWriter.createPositionAt( injectedItem, 'end' );
				utils_mergeViewLists( viewWriter, targetPosition.nodeBefore, targetPosition.nodeAfter );
				viewWriter.move( viewWriter.createRangeOn( viewList ), targetPosition );

				walker.position = breakPosition;
			}
		}
	} else {
		const nextViewList = injectedList.nextSibling;

		if ( nextViewList && ( nextViewList.is( 'element', 'ul' ) || nextViewList.is( 'element', 'ol' ) ) ) {
			let lastSubChild = null;

			for ( const child of nextViewList.getChildren() ) {
				const modelChild = mapper.toModelElement( child );

				if ( modelChild && modelChild.getAttribute( 'listIndent' ) > modelItem.getAttribute( 'listIndent' ) ) {
					lastSubChild = child;
				} else {
					break;
				}
			}

			if ( lastSubChild ) {
				viewWriter.breakContainer( viewWriter.createPositionAfter( lastSubChild ) );
				viewWriter.move( viewWriter.createRangeOn( lastSubChild.parent ), viewWriter.createPositionAt( injectedItem, 'end' ) );
			}
		}
	}

	// Merge the inserted view list with its possible neighbor lists.
	utils_mergeViewLists( viewWriter, injectedList, injectedList.nextSibling );
	utils_mergeViewLists( viewWriter, injectedList.previousSibling, injectedList );
}

/**
 * Helper function that takes two parameters that are expected to be view list elements, and merges them.
 * The merge happens only if both parameters are list elements of the same type (the same element name and the same class attributes).
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter The writer instance.
 * @param {module:engine/view/item~Item} firstList The first element to compare.
 * @param {module:engine/view/item~Item} secondList The second element to compare.
 * @returns {module:engine/view/position~Position|null} The position after merge or `null` when there was no merge.
 */
function utils_mergeViewLists( viewWriter, firstList, secondList ) {
	// Check if two lists are going to be merged.
	if ( !firstList || !secondList || ( firstList.name != 'ul' && firstList.name != 'ol' ) ) {
		return null;
	}

	// Both parameters are list elements, so compare types now.
	if ( firstList.name != secondList.name || firstList.getAttribute( 'class' ) !== secondList.getAttribute( 'class' ) ) {
		return null;
	}

	return viewWriter.mergeContainers( viewWriter.createPositionAfter( firstList ) );
}

/**
 * Helper function that for a given `view.Position`, returns a `view.Position` that is after all `view.UIElement`s that
 * are after the given position.
 *
 * For example:
 * `<container:p>foo^<ui:span></ui:span><ui:span></ui:span>bar</container:p>`
 * For position ^, the position before "bar" will be returned.
 *
 * @param {module:engine/view/position~Position} viewPosition
 * @returns {module:engine/view/position~Position}
 */
function utils_positionAfterUiElements( viewPosition ) {
	return viewPosition.getLastMatchingPosition( value => value.item.is( 'uiElement' ) );
}

/**
 * Helper function that searches for a previous list item sibling of a given model item that meets the given criteria
 * passed by the options object.
 *
 * @param {module:engine/model/item~Item} modelItem
 * @param {Object} options Search criteria.
 * @param {Boolean} [options.sameIndent=false] Whether the sought sibling should have the same indentation.
 * @param {Boolean} [options.smallerIndent=false] Whether the sought sibling should have a smaller indentation.
 * @param {Number} [options.listIndent] The reference indentation.
 * @param {'forward'|'backward'} [options.direction='backward'] Walking direction.
 * @returns {module:engine/model/item~Item|null}
 */
function utils_getSiblingListItem( modelItem, options ) {
	const sameIndent = !!options.sameIndent;
	const smallerIndent = !!options.smallerIndent;
	const indent = options.listIndent;

	let item = modelItem;

	while ( item && item.name == 'listItem' ) {
		const itemIndent = item.getAttribute( 'listIndent' );

		if ( ( sameIndent && indent == itemIndent ) || ( smallerIndent && indent > itemIndent ) ) {
			return item;
		}

		if ( options.direction === 'forward' ) {
			item = item.nextSibling;
		} else {
			item = item.previousSibling;
		}
	}

	return null;
}

/**
 * Helper method for creating a UI button and linking it with an appropriate command.
 *
 * @private
 * @param {module:core/editor/editor~Editor} editor The editor instance to which the UI component will be added.
 * @param {String} commandName The name of the command.
 * @param {String} label The button label.
 * @param {String} icon The source of the icon.
 */
function createUIComponent( editor, commandName, label, icon ) {
	editor.ui.componentFactory.add( commandName, locale => {
		const command = editor.commands.get( commandName );
		const buttonView = new buttonview_ButtonView( locale );

		buttonView.set( {
			label,
			icon,
			tooltip: true,
			isToggleable: true
		} );

		// Bind button model to command.
		buttonView.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

		// Execute command.
		buttonView.on( 'execute', () => {
			editor.execute( commandName );
			editor.editing.view.focus();
		} );

		return buttonView;
	} );
}

/**
 * Returns a first list view element that is direct child of the given view element.
 *
 * @param {module:engine/view/element~Element} viewElement
 * @return {module:engine/view/element~Element|null}
 */
function findNestedList( viewElement ) {
	for ( const node of viewElement.getChildren() ) {
		if ( node.name == 'ul' || node.name == 'ol' ) {
			return node;
		}
	}

	return null;
}

/**
 * Returns an array with all `listItem` elements that represent the same list.
 *
 * It means that values of `listIndent`, `listType`, `listStyle`, `listReversed` and `listStart` for all items are equal.
 *
 * Additionally, if the `position` is inside a list item, that list item will be returned as well.
 *
 * @param {module:engine/model/position~Position} position Starting position.
 * @param {'forward'|'backward'} direction Walking direction.
 * @returns {Array.<module:engine/model/element~Element>}
 */
function getSiblingNodes( position, direction ) {
	const items = [];
	const listItem = position.parent;
	const walkerOptions = {
		ignoreElementEnd: false,
		startPosition: position,
		shallow: true,
		direction
	};
	const limitIndent = listItem.getAttribute( 'listIndent' );
	const nodes = [ ...new TreeWalker( walkerOptions ) ]
		.filter( value => value.item.is( 'element' ) )
		.map( value => value.item );

	for ( const element of nodes ) {
		// If found something else than `listItem`, we're out of the list scope.
		if ( !element.is( 'element', 'listItem' ) ) {
			break;
		}

		// If current parsed item has lower indent that element that the element that was a starting point,
		// it means we left a nested list. Abort searching items.
		//
		// ■ List item 1.       [listIndent=0]
		//     ○ List item 2.[] [listIndent=1], limitIndent = 1,
		//     ○ List item 3.   [listIndent=1]
		// ■ List item 4.       [listIndent=0]
		//
		// Abort searching when leave nested list.
		if ( element.getAttribute( 'listIndent' ) < limitIndent ) {
			break;
		}

		// ■ List item 1.[]     [listIndent=0] limitIndent = 0,
		//     ○ List item 2.   [listIndent=1]
		//     ○ List item 3.   [listIndent=1]
		// ■ List item 4.       [listIndent=0]
		//
		// Ignore nested lists.
		if ( element.getAttribute( 'listIndent' ) > limitIndent ) {
			continue;
		}

		// ■ List item 1.[]  [listType=bulleted]
		// 1. List item 2.   [listType=numbered]
		// 2.List item 3.    [listType=numbered]
		//
		// Abort searching when found a different kind of a list.
		if ( element.getAttribute( 'listType' ) !== listItem.getAttribute( 'listType' ) ) {
			break;
		}

		// ■ List item 1.[]  [listType=bulleted]
		// ■ List item 2.    [listType=bulleted]
		// ○ List item 3.    [listType=bulleted]
		// ○ List item 4.    [listType=bulleted]
		//
		// Abort searching when found a different list style,
		if ( element.getAttribute( 'listStyle' ) !== listItem.getAttribute( 'listStyle' ) ) {
			break;
		}

		// ... different direction
		if ( element.getAttribute( 'listReversed' ) !== listItem.getAttribute( 'listReversed' ) ) {
			break;
		}

		// ... and different start index
		if ( element.getAttribute( 'listStart' ) !== listItem.getAttribute( 'listStart' ) ) {
			break;
		}

		if ( direction === 'backward' ) {
			items.unshift( element );
		} else {
			items.push( element );
		}
	}

	return items;
}

/**
 * Returns an array with all `listItem` elements in the model selection.
 *
 * It returns all the items even if only a part of the list is selected, including items that belong to nested lists.
 * If no list is selected, it returns an empty array.
 * The order of the elements is not specified.
 *
 * @protected
 * @param {module:engine/model/model~Model} model
 * @returns {Array.<module:engine/model/element~Element>}
 */
function getSelectedListItems( model ) {
	const document = model.document;

	// For all selected blocks find all list items that are being selected
	// and update the `listStyle` attribute in those lists.
	let listItems = [ ...document.selection.getSelectedBlocks() ]
		.filter( element => element.is( 'element', 'listItem' ) )
		.map( element => {
			const position = model.change( writer => writer.createPositionAt( element, 0 ) );

			return [
				...getSiblingNodes( position, 'backward' ),
				...getSiblingNodes( position, 'forward' )
			];
		} )
		.flat();

	// Since `getSelectedBlocks()` can return items that belong to the same list, and
	// `getSiblingNodes()` returns the entire list, we need to remove duplicated items.
	listItems = [ ...new Set( listItems ) ];

	return listItems;
}

const BULLETED_LIST_STYLE_TYPES = (/* unused pure expression or super */ null && ([ 'disc', 'circle', 'square' ]));

// There's a lot of them (https://www.w3.org/TR/css-counter-styles-3/#typedef-counter-style).
// Let's support only those that can be selected by ListPropertiesUI.
const NUMBERED_LIST_STYLE_TYPES = (/* unused pure expression or super */ null && ([
	'decimal',
	'decimal-leading-zero',
	'lower-roman',
	'upper-roman',
	'lower-latin',
	'upper-latin'
]));

/**
 * Checks whether the given list-style-type is supported by numbered or bulleted list.
 *
 * @param {String} listStyleType
 * @returns {'bulleted'|'numbered'|null}
 */
function getListTypeFromListStyleType( listStyleType ) {
	if ( BULLETED_LIST_STYLE_TYPES.includes( listStyleType ) ) {
		return 'bulleted';
	}

	if ( NUMBERED_LIST_STYLE_TYPES.includes( listStyleType ) ) {
		return 'numbered';
	}

	return null;
}

// Implementation of getFillerOffset for view list item element.
//
// @returns {Number|null} Block filler offset or `null` if block filler is not needed.
function getListItemFillerOffset() {
	const hasOnlyLists = !this.isEmpty && ( this.getChild( 0 ).name == 'ul' || this.getChild( 0 ).name == 'ol' );

	if ( this.isEmpty || hasOnlyLists ) {
		return 0;
	}

	return getFillerOffset.call( this );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/numberedlist.svg
/* harmony default export */ const numberedlist = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M7 5.75c0 .414.336.75.75.75h9.5a.75.75 0 1 0 0-1.5h-9.5a.75.75 0 0 0-.75.75zM3.5 3v5H2V3.7H1v-1h2.5V3zM.343 17.857l2.59-3.257H2.92a.6.6 0 1 0-1.04 0H.302a2 2 0 1 1 3.995 0h-.001c-.048.405-.16.734-.333.988-.175.254-.59.692-1.244 1.312H4.3v1h-4l.043-.043zM7 14.75a.75.75 0 0 1 .75-.75h9.5a.75.75 0 1 1 0 1.5h-9.5a.75.75 0 0 1-.75-.75z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/bulletedlist.svg
/* harmony default export */ const bulletedlist = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M7 5.75c0 .414.336.75.75.75h9.5a.75.75 0 1 0 0-1.5h-9.5a.75.75 0 0 0-.75.75zm-6 0C1 4.784 1.777 4 2.75 4c.966 0 1.75.777 1.75 1.75 0 .966-.777 1.75-1.75 1.75C1.784 7.5 1 6.723 1 5.75zm6 9c0 .414.336.75.75.75h9.5a.75.75 0 1 0 0-1.5h-9.5a.75.75 0 0 0-.75.75zm-6 0c0-.966.777-1.75 1.75-1.75.966 0 1.75.777 1.75 1.75 0 .966-.777 1.75-1.75 1.75-.966 0-1.75-.777-1.75-1.75z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/list/listui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/list/listui
 */








/**
 * The list UI feature. It introduces the `'numberedList'` and `'bulletedList'` buttons that
 * allow to convert paragraphs to and from list items and indent or outdent them.
 *
 * @extends module:core/plugin~Plugin
 */
class listui_ListUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ListUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const t = this.editor.t;

		// Create two buttons and link them with numberedList and bulletedList commands.
		createUIComponent( this.editor, 'numberedList', t( 'Numbered List' ), numberedlist );
		createUIComponent( this.editor, 'bulletedList', t( 'Bulleted List' ), bulletedlist );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlist.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlist
 */





/**
 * The document list feature.
 *
 * This is a "glue" plugin that loads the {@link module:list/documentlist/documentlistediting~DocumentListEditing document list
 * editing feature} and {@link module:list/list/listui~ListUI list UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class DocumentList extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DocumentListEditing, listui_ListUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DocumentList';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlistproperties/documentliststartcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlistproperties/documentliststartcommand
 */





/**
 * The list start index command. It changes the `listStart` attribute of the selected list items,
 * letting the user to choose the starting point of an ordered list.
 * It is used by the {@link module:list/documentlistproperties~DocumentListProperties list properties feature}.
 *
 * @extends module:core/command~Command
 */
class DocumentListStartCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const value = this._getValue();

		this.value = value;
		this.isEnabled = value != null;
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} [options]
	 * @param {Number} [options.startIndex=1] The list start index.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;

		let blocks = Array.from( document.selection.getSelectedBlocks() )
			.filter( block => isListItemBlock( block ) && block.getAttribute( 'listType' ) == 'numbered' );

		blocks = expandListBlocksToCompleteList( blocks );

		model.change( writer => {
			for ( const block of blocks ) {
				writer.setAttribute( 'listStart', options.startIndex || 1, block );
			}
		} );
	}

	/**
	 * Checks the command's {@link #value}.
	 *
	 * @private
	 * @returns {Number|null} The current value.
	 */
	_getValue() {
		const model = this.editor.model;
		const document = model.document;

		const block = first_first( document.selection.getSelectedBlocks() );

		if ( block && isListItemBlock( block ) && block.getAttribute( 'listType' ) == 'numbered' ) {
			return block.getAttribute( 'listStart' );
		}

		return null;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlistproperties/utils/style.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
* @module list/documentlistproperties/utils/style
*/

const LIST_STYLE_TO_LIST_TYPE = {};
const LIST_STYLE_TO_TYPE_ATTRIBUTE = {};
const TYPE_ATTRIBUTE_TO_LIST_STYLE = {};

const LIST_STYLE_TYPES = [
	{ listStyle: 'disc', typeAttribute: 'disc', listType: 'bulleted' },
	{ listStyle: 'circle', typeAttribute: 'circle', listType: 'bulleted' },
	{ listStyle: 'square', typeAttribute: 'square', listType: 'bulleted' },
	{ listStyle: 'decimal', typeAttribute: '1', listType: 'numbered' },
	{ listStyle: 'decimal-leading-zero', typeAttribute: null, listType: 'numbered' },
	{ listStyle: 'lower-roman', typeAttribute: 'i', listType: 'numbered' },
	{ listStyle: 'upper-roman', typeAttribute: 'I', listType: 'numbered' },
	{ listStyle: 'lower-alpha', typeAttribute: 'a', listType: 'numbered' },
	{ listStyle: 'upper-alpha', typeAttribute: 'A', listType: 'numbered' },
	{ listStyle: 'lower-latin', typeAttribute: 'a', listType: 'numbered' },
	{ listStyle: 'upper-latin', typeAttribute: 'A', listType: 'numbered' }
];

for ( const { listStyle, typeAttribute, listType } of LIST_STYLE_TYPES ) {
	LIST_STYLE_TO_LIST_TYPE[ listStyle ] = listType;
	LIST_STYLE_TO_TYPE_ATTRIBUTE[ listStyle ] = typeAttribute;

	if ( typeAttribute ) {
		TYPE_ATTRIBUTE_TO_LIST_STYLE[ typeAttribute ] = listStyle;
	}
}

/**
 * Gets all the style types supported by given list type.
 *
 * @returns {Array.<String>}
 */
function getAllSupportedStyleTypes() {
	return LIST_STYLE_TYPES.map( x => x.listStyle );
}

/**
* Checks whether the given list-style-type is supported by numbered or bulleted list.
*
* @param {String} listStyleType
* @returns {'bulleted'|'numbered'|null}
*/
function style_getListTypeFromListStyleType( listStyleType ) {
	return LIST_STYLE_TO_LIST_TYPE[ listStyleType ] || null;
}

/**
 * Converts `type` attribute of `<ul>` or `<ol>` elements to `list-style-type` equivalent.
 *
 * @param {String} value
 * @returns {String|null}
 */
function getListStyleTypeFromTypeAttribute( value ) {
	return TYPE_ATTRIBUTE_TO_LIST_STYLE[ value ] || null;
}

/**
 * Converts `list-style-type` style to `type` attribute of `<ul>` or `<ol>` elements.
 *
 * @param {String} value
 * @returns {String|null}
 */
function getTypeAttributeFromListStyleType( value ) {
	return LIST_STYLE_TO_TYPE_ATTRIBUTE[ value ] || null;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlistproperties/documentliststylecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlistproperties/documentliststylecommand
 */






/**
 * The list style command. It changes `listStyle` attribute of the selected list items,
 * letting the user choose styles for the list item markers.
 * It is used by the {@link module:list/documentlistproperties~DocumentListProperties list properties feature}.
 *
 * @extends module:core/command~Command
 */
class DocumentListStyleCommand extends command_Command {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {String} defaultType The list type that will be used by default if the value was not specified during
	 * the command execution.
	 * @param {Array.<String>} [supportedTypes] The list of supported style types by this command.
	 */
	constructor( editor, defaultType, supportedTypes ) {
		super( editor );

		/**
		 * The default type of the list style.
		 *
		 * @protected
		 * @member {String}
		 */
		this._defaultType = defaultType;

		/**
		 * The list of supported style types by this command.
		 *
		 * @private
		 * @member {Array.<String>|undefined}
		 */
		this._supportedTypes = supportedTypes;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.value = this._getValue();
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} options
	 * @param {String|null} [options.type] The type of the list style, e.g. `'disc'` or `'square'`. If `null` is specified, the default
	 * style will be applied.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;

		model.change( writer => {
			this._tryToConvertItemsToList( options );

			let blocks = Array.from( document.selection.getSelectedBlocks() )
				.filter( block => block.hasAttribute( 'listType' ) );

			if ( !blocks.length ) {
				return;
			}

			blocks = expandListBlocksToCompleteList( blocks );

			for ( const block of blocks ) {
				writer.setAttribute( 'listStyle', options.type || this._defaultType, block );
			}
		} );
	}

	/**
	 * Checks if the given style type is supported by this plugin.
	 *
	 * @param {String} value
	 * @returns {Boolean}
	 */
	isStyleTypeSupported( value ) {
		if ( !this._supportedTypes ) {
			return true;
		}

		return this._supportedTypes.includes( value );
	}

	/**
	 * Checks the command's {@link #value}.
	 *
	 * @private
	 * @returns {String|null} The current value.
	 */
	_getValue() {
		const listItem = first_first( this.editor.model.document.selection.getSelectedBlocks() );

		if ( isListItemBlock( listItem ) ) {
			return listItem.getAttribute( 'listStyle' );
		}

		return null;
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		const editor = this.editor;

		const numberedList = editor.commands.get( 'numberedList' );
		const bulletedList = editor.commands.get( 'bulletedList' );

		return numberedList.isEnabled || bulletedList.isEnabled;
	}

	/**
	 * Check if the provided list style is valid. Also change the selection to a list if it's not set yet.
	 *
	 * @private
	 * @param {Object} options
	 * @param {String|null} [options.type] The type of the list style. If `null` is specified, the function does nothing.
	*/
	_tryToConvertItemsToList( options ) {
		if ( !options.type ) {
			return;
		}

		const listType = style_getListTypeFromListStyleType( options.type );

		if ( !listType ) {
			return;
		}

		const editor = this.editor;
		const commandName = listType + 'List';
		const command = editor.commands.get( commandName );

		if ( !command.value ) {
			editor.execute( commandName );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlistproperties/documentlistreversedcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlistproperties/documentlistreversedcommand
 */





/**
 * The list reversed command. It changes the `listReversed` attribute of the selected list items,
 * letting the user to choose the order of an ordered list.
 * It is used by the {@link module:list/documentlistproperties~DocumentListProperties list properties feature}.
 *
 * @extends module:core/command~Command
 */
class DocumentListReversedCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const value = this._getValue();

		this.value = value;
		this.isEnabled = value != null;
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} [options]
	 * @param {Boolean} [options.reversed=false] Whether the list should be reversed.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;

		let blocks = Array.from( document.selection.getSelectedBlocks() )
			.filter( block => isListItemBlock( block ) && block.getAttribute( 'listType' ) == 'numbered' );

		blocks = expandListBlocksToCompleteList( blocks );

		model.change( writer => {
			for ( const block of blocks ) {
				writer.setAttribute( 'listReversed', !!options.reversed, block );
			}
		} );
	}

	/**
	 * Checks the command's {@link #value}.
	 *
	 * @private
	 * @returns {Boolean|null} The current value.
	 */
	_getValue() {
		const model = this.editor.model;
		const document = model.document;

		const block = first_first( document.selection.getSelectedBlocks() );

		if ( isListItemBlock( block ) && block.getAttribute( 'listType' ) == 'numbered' ) {
			return block.getAttribute( 'listReversed' );
		}

		return null;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlistproperties/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlistproperties/converters
 */

/**
 * Returns a converter that consumes the `style`, `reversed`, and `start` attributes.
 * In `style`, it searches for the `list-style-type` definition.
 * If not found, the `"default"` value will be used.
 *
 * @protected
 * @param {module:list/documentlistproperties/documentlistpropertiesediting~AttributeStrategy} strategy
 * @returns {Function}
 */
function listPropertiesUpcastConverter( strategy ) {
	return ( evt, data, conversionApi ) => {
		const { writer, schema, consumable } = conversionApi;

		// If there is no view consumable to consume, set the default attribute value to be able to reconvert nested lists on parent change.
		// So abort converting if attribute was directly consumed.
		if ( consumable.test( data.viewItem, strategy.viewConsumables ) === false ) {
			return;
		}

		if ( !data.modelRange ) {
			Object.assign( data, conversionApi.convertChildren( data.viewItem, data.modelCursor ) );
		}

		let applied = false;

		for ( const item of data.modelRange.getItems( { shallow: true } ) ) {
			if ( !schema.checkAttribute( item, strategy.attributeName ) ) {
				continue;
			}

			if ( !strategy.appliesToListItem( item ) ) {
				continue;
			}

			// Set list attributes only on same level items, those nested deeper are already handled by the recursive conversion.
			if ( item.hasAttribute( strategy.attributeName ) ) {
				continue;
			}

			writer.setAttribute( strategy.attributeName, strategy.getAttributeOnUpcast( data.viewItem ), item );
			applied = true;
		}

		if ( applied ) {
			consumable.consume( data.viewItem, strategy.viewConsumables );
		}
	};
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlistproperties/documentlistpropertiesediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlistproperties/documentlistpropertiesediting
 */










const DEFAULT_LIST_TYPE = 'default';

/**
 * The document list properties engine feature.
 *
 * It registers the `'listStyle'`, `'listReversed'` and `'listStart'` commands if they are enabled in the configuration.
 * Read more in {@link module:list/listproperties~ListPropertiesConfig}.
 *
 * @extends module:core/plugin~Plugin
 */
class DocumentListPropertiesEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DocumentListEditing ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DocumentListPropertiesEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'list', {
			properties: {
				styles: true,
				startIndex: false,
				reversed: false
			}
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const model = editor.model;
		const documentListEditing = editor.plugins.get( DocumentListEditing );

		const enabledProperties = editor.config.get( 'list.properties' );
		const strategies = createAttributeStrategies( enabledProperties );

		for ( const strategy of strategies ) {
			strategy.addCommand( editor );

			model.schema.extend( '$container', { allowAttributes: strategy.attributeName } );
			model.schema.extend( '$block', { allowAttributes: strategy.attributeName } );
			model.schema.extend( '$blockObject', { allowAttributes: strategy.attributeName } );

			// Register downcast strategy.
			documentListEditing.registerDowncastStrategy( {
				scope: 'list',
				attributeName: strategy.attributeName,

				setAttributeOnDowncast( writer, attributeValue, viewElement ) {
					strategy.setAttributeOnDowncast( writer, attributeValue, viewElement );
				}
			} );
		}

		// Set up conversion.
		editor.conversion.for( 'upcast' ).add( dispatcher => {
			for ( const strategy of strategies ) {
				dispatcher.on( 'element:ol', listPropertiesUpcastConverter( strategy ) );
				dispatcher.on( 'element:ul', listPropertiesUpcastConverter( strategy ) );
			}
		} );

		// Verify if the list view element (ul or ol) requires refreshing.
		documentListEditing.on( 'checkAttributes:list', ( evt, { viewElement, modelAttributes } ) => {
			for ( const strategy of strategies ) {
				if ( strategy.getAttributeOnUpcast( viewElement ) != modelAttributes[ strategy.attributeName ] ) {
					evt.return = true;
					evt.stop();
				}
			}
		} );

		// Reset list properties after indenting list items.
		this.listenTo( editor.commands.get( 'indentList' ), 'afterExecute', ( evt, changedBlocks ) => {
			model.change( writer => {
				for ( const node of changedBlocks ) {
					for ( const strategy of strategies ) {
						if ( strategy.appliesToListItem( node ) ) {
							// Just reset the attribute.
							// If there is a previous indented list that this node should be merged into,
							// the postfixer will unify all the attributes of both sub-lists.
							writer.setAttribute( strategy.attributeName, strategy.defaultValue, node );
						}
					}
				}
			} );
		} );

		// Add or remove list properties attributes depending on the list type.
		documentListEditing.on( 'postFixer', ( evt, { listNodes, writer } ) => {
			for ( const { node } of listNodes ) {
				for ( const strategy of strategies ) {
					// Check if attribute is valid.
					if ( strategy.hasValidAttribute( node ) ) {
						continue;
					}

					// Add missing default property attributes...
					if ( strategy.appliesToListItem( node ) ) {
						writer.setAttribute( strategy.attributeName, strategy.defaultValue, node );
					}
					// ...or remove invalid property attributes.
					else {
						writer.removeAttribute( strategy.attributeName, node );
					}

					evt.return = true;
				}
			}
		} );

		// Make sure that all items in a single list (items at the same level & listType) have the same properties.
		documentListEditing.on( 'postFixer', ( evt, { listNodes, writer } ) => {
			const previousNodesByIndent = []; // Last seen nodes of lower indented lists.

			for ( const { node, previous } of listNodes ) {
				// For the first list block there is nothing to compare with.
				if ( !previous ) {
					continue;
				}

				const nodeIndent = node.getAttribute( 'listIndent' );
				const previousNodeIndent = previous.getAttribute( 'listIndent' );

				let previousNodeInList = null; // It's like `previous` but has the same indent as current node.

				// Let's find previous node for the same indent.
				// We're going to need that when we get back to previous indent.
				if ( nodeIndent > previousNodeIndent ) {
					previousNodesByIndent[ previousNodeIndent ] = previous;
				}
				// Restore the one for given indent.
				else if ( nodeIndent < previousNodeIndent ) {
					previousNodeInList = previousNodesByIndent[ nodeIndent ];
					previousNodesByIndent.length = nodeIndent;
				}
				// Same indent.
				else {
					previousNodeInList = previous;
				}

				// This is a first item of a nested list.
				if ( !previousNodeInList ) {
					continue;
				}

				// This is a first block of a list of a different type.
				if ( previousNodeInList.getAttribute( 'listType' ) != node.getAttribute( 'listType' ) ) {
					continue;
				}

				// Copy properties from the previous one.
				for ( const strategy of strategies ) {
					const { attributeName } = strategy;

					if ( !strategy.appliesToListItem( node ) ) {
						continue;
					}

					const value = previousNodeInList.getAttribute( attributeName );

					if ( node.getAttribute( attributeName ) != value ) {
						writer.setAttribute( attributeName, value, node );
						evt.return = true;
					}
				}
			}
		} );
	}
}

/**
 * Strategy for dealing with `listItem` attributes supported by this plugin.
 *
 * @typedef {Object} module:list/documentlistproperties/documentlistpropertiesediting~AttributeStrategy
 * @protected
 * @property {String} attributeName The model attribute name.
 * @property {*} defaultValue The model attribute default value.
 * @property {Object} viewConsumables The view consumable as expected by
 * {@link module:engine/conversion/viewconsumable~ViewConsumable#consume `ViewConsumable`}.
 * @property {Function} addCommand Registers an editor command.
 * @property {Function} appliesToListItem Verifies whether the strategy is applicable for the specified model element.
 * @property {Function} hasValidAttribute Verifies whether the model attribute value is valid.
 * @property {Function} setAttributeOnDowncast Sets the property on the view element.
 * @property {Function} getAttributeOnUpcast Retrieves the property value from the view element.
 */

// Creates an array of strategies for dealing with enabled listItem attributes.
//
// @param {Object} enabledProperties
// @param {Boolean|Object} enabledProperties.styles
// @param {Boolean} [enabledProperties.styles.useAttribute]
// @param {Boolean} enabledProperties.reversed
// @param {Boolean} enabledProperties.startIndex
// @returns {Array.<module:list/documentlistproperties/documentlistpropertiesediting~AttributeStrategy>}
function createAttributeStrategies( enabledProperties ) {
	const strategies = [];

	if ( enabledProperties.styles ) {
		const useAttribute = typeof enabledProperties.styles == 'object' && enabledProperties.styles.useAttribute;

		strategies.push( {
			attributeName: 'listStyle',
			defaultValue: DEFAULT_LIST_TYPE,
			viewConsumables: { styles: 'list-style-type' },

			addCommand( editor ) {
				let supportedTypes = getAllSupportedStyleTypes();

				if ( useAttribute ) {
					supportedTypes = supportedTypes.filter( styleType => !!getTypeAttributeFromListStyleType( styleType ) );
				}

				editor.commands.add( 'listStyle', new DocumentListStyleCommand( editor, DEFAULT_LIST_TYPE, supportedTypes ) );
			},

			appliesToListItem() {
				return true;
			},

			hasValidAttribute( item ) {
				if ( !item.hasAttribute( 'listStyle' ) ) {
					return false;
				}

				const value = item.getAttribute( 'listStyle' );

				if ( value == DEFAULT_LIST_TYPE ) {
					return true;
				}

				return style_getListTypeFromListStyleType( value ) == item.getAttribute( 'listType' );
			},

			setAttributeOnDowncast( writer, listStyle, element ) {
				if ( listStyle && listStyle !== DEFAULT_LIST_TYPE ) {
					if ( useAttribute ) {
						const value = getTypeAttributeFromListStyleType( listStyle );

						if ( value ) {
							writer.setAttribute( 'type', value, element );

							return;
						}
					} else {
						writer.setStyle( 'list-style-type', listStyle, element );

						return;
					}
				}

				writer.removeStyle( 'list-style-type', element );
				writer.removeAttribute( 'type', element );
			},

			getAttributeOnUpcast( listParent ) {
				const style = listParent.getStyle( 'list-style-type' );

				if ( style ) {
					return style;
				}

				const attribute = listParent.getAttribute( 'type' );

				if ( attribute ) {
					return getListStyleTypeFromTypeAttribute( attribute );
				}

				return DEFAULT_LIST_TYPE;
			}
		} );
	}

	if ( enabledProperties.reversed ) {
		strategies.push( {
			attributeName: 'listReversed',
			defaultValue: false,
			viewConsumables: { attributes: 'reversed' },

			addCommand( editor ) {
				editor.commands.add( 'listReversed', new DocumentListReversedCommand( editor ) );
			},

			appliesToListItem( item ) {
				return item.getAttribute( 'listType' ) == 'numbered';
			},

			hasValidAttribute( item ) {
				return this.appliesToListItem( item ) == item.hasAttribute( 'listReversed' );
			},

			setAttributeOnDowncast( writer, listReversed, element ) {
				if ( listReversed ) {
					writer.setAttribute( 'reversed', 'reversed', element );
				} else {
					writer.removeAttribute( 'reversed', element );
				}
			},

			getAttributeOnUpcast( listParent ) {
				return listParent.hasAttribute( 'reversed' );
			}
		} );
	}

	if ( enabledProperties.startIndex ) {
		strategies.push( {
			attributeName: 'listStart',
			defaultValue: 1,
			viewConsumables: { attributes: 'start' },

			addCommand( editor ) {
				editor.commands.add( 'listStart', new DocumentListStartCommand( editor ) );
			},

			appliesToListItem( item ) {
				return item.getAttribute( 'listType' ) == 'numbered';
			},

			hasValidAttribute( item ) {
				return this.appliesToListItem( item ) == item.hasAttribute( 'listStart' );
			},

			setAttributeOnDowncast( writer, listStart, element ) {
				if ( listStart && listStart > 1 ) {
					writer.setAttribute( 'start', listStart, element );
				} else {
					writer.removeAttribute( 'start', element );
				}
			},

			getAttributeOnUpcast( listParent ) {
				return listParent.getAttribute( 'start' ) || 1;
			}
		} );
	}

	return strategies;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-list/theme/collapsible.css
var collapsible = __webpack_require__(3195);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/collapsible.css

            

var collapsible_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

collapsible_options.insert = "head";
collapsible_options.singleton = true;

var collapsible_update = injectStylesIntoStyleTag_default()(collapsible/* default */.Z, collapsible_options);



/* harmony default export */ const theme_collapsible = (collapsible/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/listproperties/ui/collapsibleview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/ui/collapsibleview
 */



// eslint-disable-next-line ckeditor5-rules/ckeditor-imports




/**
 * A collapsible UI component. Consists of a labeled button and a container which can be collapsed
 * by clicking the button. The collapsible container can be a host to other UI views.
 *
 * @protected
 * @extends module:ui/view~View
 */
class CollapsibleView extends src_view_View {
	/**
	 * Creates an instance of the collapsible view.
	 *
	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
	 * @param {Array.<module:ui/view~View>} [childViews] An optional array of initial child views to be inserted
	 * into the collapsible.
	 */
	constructor( locale, childViews ) {
		super( locale );

		const bind = this.bindTemplate;

		/**
		 * `true` when the container with {@link #children} is collapsed. `false` otherwise.
		 *
		 * @observable
		 * @member {Boolean} #isCollapsed
		 */
		this.set( 'isCollapsed', false );

		/**
		 * The text label of the {@link #buttonView}.
		 *
		 * @observable
		 * @member {String} #label
		 * @default 'Show more'
		 */
		this.set( 'label', '' );

		/**
		 * The main button that, when clicked, collapses or expands the container with {@link #children}.
		 *
		 * @readonly
		 * @member {module:ui/button/buttonview~ButtonView} #buttonView
		 */
		this.buttonView = this._createButtonView();

		/**
		 * A collection of the child views that can be collapsed by clicking the {@link #buttonView}.
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection} #children
		 */
		this.children = this.createCollection();

		/**
		 * The ID of the label inside the {@link #buttonView} that describes the collapsible
		 * container for assistive technologies. Set after the button was {@link #render rendered}.
		 *
		 * @private
		 * @readonly
		 * @observable
		 * @member {String} #_collapsibleAriaLabelUid
		 */
		this.set( '_collapsibleAriaLabelUid' );

		if ( childViews ) {
			this.children.addMany( childViews );
		}

		this.setTemplate( {
			tag: 'div',
			attributes: {
				class: [
					'ck',
					'ck-collapsible',
					bind.if( 'isCollapsed', 'ck-collapsible_collapsed' )
				]
			},
			children: [
				this.buttonView,
				{
					tag: 'div',
					attributes: {
						class: [
							'ck',
							'ck-collapsible__children'
						],
						role: 'region',
						hidden: bind.if( 'isCollapsed', 'hidden' ),
						'aria-labelledby': bind.to( '_collapsibleAriaLabelUid' )
					},
					children: this.children
				}
			]
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		this._collapsibleAriaLabelUid = this.buttonView.labelView.element.id;
	}

	/**
	 * Creates the main {@link #buttonView} of the collapsible.
	 *
	 * @private
	 * @returns {module:ui/button/buttonview~ButtonView}
	 */
	_createButtonView() {
		const buttonView = new buttonview_ButtonView( this.locale );
		const bind = buttonView.bindTemplate;

		buttonView.set( {
			withText: true,
			icon: dropdown_arrow
		} );

		buttonView.extendTemplate( {
			attributes: {
				'aria-expanded': bind.to( 'isOn', value => String( value ) )
			}
		} );

		buttonView.bind( 'label' ).to( this );
		buttonView.bind( 'isOn' ).to( this, 'isCollapsed', isCollapsed => !isCollapsed );

		buttonView.on( 'execute', () => {
			this.isCollapsed = !this.isCollapsed;
		} );

		return buttonView;
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-list/theme/listproperties.css
var listproperties = __webpack_require__(7133);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/listproperties.css

            

var listproperties_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

listproperties_options.insert = "head";
listproperties_options.singleton = true;

var listproperties_update = injectStylesIntoStyleTag_default()(listproperties/* default */.Z, listproperties_options);



/* harmony default export */ const theme_listproperties = (listproperties/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/listproperties/ui/listpropertiesview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/ui/listpropertiesview
 */








/**
 * The list properties view to be displayed in the list dropdown.
 *
 * Contains a grid of available list styles and, for numbered list, also the list start index and reversed fields.
 *
 * @extends module:ui/view~View
 */
class ListPropertiesView extends src_view_View {
	/**
	 * Creates an instance of the list properties view.
	 *
	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
	 * @param {Object} options Options of the view.
	 * @param {Object.<String,Boolean>} options.enabledProperties An object containing the configuration of enabled list property names.
	 * Allows conditional rendering the sub-components of the properties view.
	 * @param {Array.<module:ui/button/buttonview~ButtonView>|null} options.styleButtonViews A list of style buttons to be rendered
	 * inside the styles grid. The grid will not be rendered when `enabledProperties` does not include the `'styles'` key.
	 * @param {String} options.styleGridAriaLabel An assistive technologies label set on the grid of styles (if the grid is rendered).
	 */
	constructor( locale, { enabledProperties, styleButtonViews, styleGridAriaLabel } ) {
		super( locale );

		const elementCssClasses = [
			'ck',
			'ck-list-properties'
		];

		/**
		 * A collection of the child views.
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.children = this.createCollection();

		/**
		 * A view that renders the grid of list styles.
		 *
		 * @readonly
		 * @member {module:ui/view~View|null}
		 */
		this.stylesView = null;

		/**
		 * A collapsible view that hosts additional list property fields ({@link #startIndexFieldView} and
		 * {@link #reversedSwitchButtonView}) to visually separate them from the {@link #stylesView grid of styles}.
		 *
		 * **Note**: Only present when:
		 * * the view represents **numbered** list properties,
		 * * and the {@link #stylesView} is rendered,
		 * * and either {@link #startIndexFieldView} or {@link #reversedSwitchButtonView} is rendered.
		 *
		 * @readonly
		 * @member {module:list/ui/collapsibleview~CollapsibleView|null}
		 */
		this.additionalPropertiesCollapsibleView = null;

		/**
		 * A labeled number field allowing the user to set the start index of the list.
		 *
		 * **Note**: Only present when the view represents **numbered** list properties.
		 *
		 * @readonly
		 * @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView|null}
		 */
		this.startIndexFieldView = null;

		/**
		 * A switch button allowing the user to make the edited list reversed.
		 *
		 * **Note**: Only present when the view represents **numbered** list properties.
		 *
		 * @readonly
		 * @member {module:ui/button/switchbuttonview~SwitchButtonView|null}
		 */
		this.reversedSwitchButtonView = null;

		/**
		 * Tracks information about the DOM focus in the view.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * A collection of views that can be focused in the properties view.
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #focusables} in the view.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this.focusCycler = new FocusCycler( {
			focusables: this.focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate #children backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke.
				focusPrevious: 'shift + tab',

				// Navigate #children forwards using the <kbd>Tab</kbd> key.
				focusNext: 'tab'
			}
		} );

		// The rendering of the styles grid is conditional. When there is no styles grid, the view will render without collapsible
		// for numbered list properties, hence simplifying the layout.
		if ( enabledProperties.styles ) {
			this.stylesView = this._createStylesView( styleButtonViews, styleGridAriaLabel );
			this.children.add( this.stylesView );
		} else {
			elementCssClasses.push( 'ck-list-properties_without-styles' );
		}

		// The rendering of the numbered list property views is also conditional. It only makes sense for the numbered list
		// dropdown. The unordered list does not have such properties.
		if ( enabledProperties.startIndex || enabledProperties.reversed ) {
			this._addNumberedListPropertyViews( enabledProperties, styleButtonViews );

			elementCssClasses.push( 'ck-list-properties_with-numbered-properties' );
		}

		this.setTemplate( {
			tag: 'div',
			attributes: {
				class: elementCssClasses
			},
			children: this.children
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		if ( this.stylesView ) {
			this.focusables.add( this.stylesView );
			this.focusTracker.add( this.stylesView.element );

			// Register the collapsible toggle button to the focus system.
			if ( this.startIndexFieldView || this.reversedSwitchButtonView ) {
				this.focusables.add( this.children.last.buttonView );
				this.focusTracker.add( this.children.last.buttonView.element );
			}

			for ( const item of this.stylesView.children ) {
				this.stylesView.focusTracker.add( item.element );
			}

			addKeyboardHandlingForGrid( {
				keystrokeHandler: this.stylesView.keystrokes,
				focusTracker: this.stylesView.focusTracker,
				gridItems: this.stylesView.children,
				// Note: The styles view has a different number of columns depending on whether the other properties
				// are enabled in the dropdown or not (https://github.com/ckeditor/ckeditor5/issues/12340)
				numberOfColumns: () => dom_global.window.getComputedStyle( this.stylesView.element )
					.getPropertyValue( 'grid-template-columns' )
					.split( ' ' )
					.length
			} );
		}

		if ( this.startIndexFieldView ) {
			this.focusables.add( this.startIndexFieldView );
			this.focusTracker.add( this.startIndexFieldView.element );

			// Intercept the `selectstart` event, which is blocked by default because of the default behavior
			// of the DropdownView#panelView.
			// TODO: blocking `selectstart` in the #panelView should be configurable per–drop–down instance.
			this.listenTo( this.startIndexFieldView.element, 'selectstart', ( evt, domEvt ) => {
				domEvt.stopPropagation();
			}, { priority: 'high' } );

			const stopPropagation = data => data.stopPropagation();

			// Since the form is in the dropdown panel which is a child of the toolbar, the toolbar's
			// keystroke handler would take over the key management in the input. We need to prevent
			// this ASAP. Otherwise, the basic caret movement using the arrow keys will be impossible.
			this.keystrokes.set( 'arrowright', stopPropagation );
			this.keystrokes.set( 'arrowleft', stopPropagation );
			this.keystrokes.set( 'arrowup', stopPropagation );
			this.keystrokes.set( 'arrowdown', stopPropagation );
		}

		if ( this.reversedSwitchButtonView ) {
			this.focusables.add( this.reversedSwitchButtonView );
			this.focusTracker.add( this.reversedSwitchButtonView.element );
		}

		// Start listening for the keystrokes coming from #element.
		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	focus() {
		this.focusCycler.focusFirst();
	}

	/**
	 * @inheritDoc
	 */
	focusLast() {
		this.focusCycler.focusLast();
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Creates the list styles grid.
	 *
	 * @protected
	 * @param {Array.<module:ui/button/buttonview~ButtonView>} styleButtons Buttons to be placed in the grid.
	 * @param {String} styleGridAriaLabel The assistive technology label of the grid.
	 * @returns {module:ui/view~View}
	 */
	_createStylesView( styleButtons, styleGridAriaLabel ) {
		const stylesView = new src_view_View( this.locale );

		stylesView.children = stylesView.createCollection( this.locale );
		stylesView.children.addMany( styleButtons );

		stylesView.setTemplate( {
			tag: 'div',
			attributes: {
				'aria-label': styleGridAriaLabel,
				class: [
					'ck',
					'ck-list-styles-list'
				]
			},
			children: stylesView.children
		} );

		stylesView.children.delegate( 'execute' ).to( this );

		stylesView.focus = function() {
			this.children.first.focus();
		};

		stylesView.focusTracker = new FocusTracker();
		stylesView.keystrokes = new KeystrokeHandler();

		stylesView.render();

		stylesView.keystrokes.listenTo( stylesView.element );

		return stylesView;
	}

	/**
	 * Renders {@link #startIndexFieldView} and/or {@link #reversedSwitchButtonView} depending on the configuration of the properties view.
	 *
	 * @private
	 * @param {Object.<String,Boolean>} options.enabledProperties An object containing the configuration of enabled list property names
	 * (see {@link #constructor}).
	 */
	_addNumberedListPropertyViews( enabledProperties ) {
		const t = this.locale.t;
		const numberedPropertyViews = [];

		if ( enabledProperties.startIndex ) {
			this.startIndexFieldView = this._createStartIndexField();
			numberedPropertyViews.push( this.startIndexFieldView );
		}

		if ( enabledProperties.reversed ) {
			this.reversedSwitchButtonView = this._createReversedSwitchButton();
			numberedPropertyViews.push( this.reversedSwitchButtonView );
		}

		// When there are some style buttons, pack the numbered list properties into a collapsible to separate them.
		if ( enabledProperties.styles ) {
			this.additionalPropertiesCollapsibleView = new CollapsibleView( this.locale, numberedPropertyViews );

			this.additionalPropertiesCollapsibleView.set( {
				label: t( 'List properties' ),
				isCollapsed: true
			} );

			// Don't enable the collapsible view unless either start index or reversed field is enabled (e.g. when no list is selected).
			this.additionalPropertiesCollapsibleView.buttonView.bind( 'isEnabled' ).toMany(
				numberedPropertyViews, 'isEnabled', ( ...areEnabled ) => areEnabled.some( isEnabled => isEnabled ) );

			// Automatically collapse the additional properties collapsible when either start index or reversed field gets disabled.
			this.additionalPropertiesCollapsibleView.buttonView.on( 'change:isEnabled', ( evt, data, isEnabled ) => {
				if ( !isEnabled ) {
					this.additionalPropertiesCollapsibleView.isCollapsed = true;
				}
			} );

			this.children.add( this.additionalPropertiesCollapsibleView );
		} else {
			this.children.addMany( numberedPropertyViews );
		}
	}

	/**
	 * Creates the list start index labeled field.
	 *
	 * @private
	 * @protected
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
	 */
	_createStartIndexField() {
		const t = this.locale.t;
		const startIndexFieldView = new LabeledFieldView( this.locale, createLabeledInputNumber );

		startIndexFieldView.set( {
			label: t( 'Start at' ),
			class: 'ck-numbered-list-properties__start-index'
		} );

		startIndexFieldView.fieldView.set( {
			min: 1,
			step: 1,
			value: 1,
			inputMode: 'numeric'
		} );

		startIndexFieldView.fieldView.on( 'input', () => {
			const inputElement = startIndexFieldView.fieldView.element;
			const startIndex = inputElement.valueAsNumber;

			if ( Number.isNaN( startIndex ) ) {
				return;
			}

			if ( !inputElement.checkValidity() ) {
				startIndexFieldView.errorText = t( 'Start index must be greater than 0.' );
			} else {
				this.fire( 'listStart', { startIndex } );
			}
		} );

		return startIndexFieldView;
	}

	/**
	 * Creates the reversed list switch button.
	 *
	 * @private
	 * @protected
	 * @returns {module:ui/button/switchbuttonview~SwitchButtonView}
	 */
	_createReversedSwitchButton() {
		const t = this.locale.t;
		const reversedButtonView = new SwitchButtonView( this.locale );

		reversedButtonView.set( {
			withText: true,
			label: t( 'Reversed order' ),
			class: 'ck-numbered-list-properties__reversed-order'
		} );

		reversedButtonView.delegate( 'execute' ).to( this, 'listReversed' );

		return reversedButtonView;
	}

	/**
	 * Fired when the list start index value has changed via {@link #startIndexFieldView}.
	 *
	 * @event listStart
	 * @param {Object} data
	 * @param {Number} data.startIndex The new start index of the list.
	 */

	/**
	 * Fired when the list order has changed (reversed) via {@link #reversedSwitchButtonView}.
	 *
	 * @event listReversed
	 */
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststyledisc.svg
/* harmony default export */ const liststyledisc = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M11 27a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm0-9a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm0-9a3 3 0 1 1 0 6 3 3 0 0 1 0-6z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststylecircle.svg
/* harmony default export */ const liststylecircle = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M11 27a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm0 1a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm0-10a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm0 1a2 2 0 1 0 0 4 2 2 0 0 0 0-4zm0-10a3 3 0 1 1 0 6 3 3 0 0 1 0-6zm0 1a2 2 0 1 0 0 4 2 2 0 0 0 0-4z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststylesquare.svg
/* harmony default export */ const liststylesquare = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M14 27v6H8v-6h6zm0-9v6H8v-6h6zm0-9v6H8V9h6z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststyledecimal.svg
/* harmony default export */ const liststyledecimal = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M10.29 15V8.531H9.286c-.14.393-.4.736-.778 1.03-.378.295-.728.495-1.05.6v1.121a4.257 4.257 0 0 0 1.595-.936V15h1.235zm3.343 0v-1.235h-1.235V15h1.235zM11.3 24v-1.147H8.848c.064-.111.148-.226.252-.343.104-.117.351-.354.74-.712.39-.357.66-.631.81-.821.225-.288.39-.562.494-.824.104-.263.156-.539.156-.829 0-.51-.182-.936-.545-1.279-.363-.342-.863-.514-1.499-.514-.58 0-1.063.148-1.45.444-.387.296-.617.784-.69 1.463l1.23.124c.024-.36.112-.619.264-.774.153-.155.358-.233.616-.233.26 0 .465.074.613.222.148.148.222.36.222.635 0 .25-.085.501-.255.756-.126.185-.468.536-1.024 1.055-.692.641-1.155 1.156-1.389 1.544-.234.389-.375.8-.422 1.233H11.3zm2.333 0v-1.235h-1.235V24h1.235zM9.204 34.11c.615 0 1.129-.2 1.542-.598.413-.398.62-.88.62-1.446 0-.39-.11-.722-.332-.997a1.5 1.5 0 0 0-.886-.532c.619-.337.928-.788.928-1.353 0-.399-.151-.756-.453-1.073-.366-.386-.852-.58-1.459-.58a2.25 2.25 0 0 0-.96.2 1.617 1.617 0 0 0-.668.55c-.16.232-.28.544-.358.933l1.138.194c.032-.282.123-.495.272-.642.15-.146.33-.22.54-.22.215 0 .386.065.515.194s.193.302.193.518c0 .255-.087.46-.263.613-.176.154-.43.227-.765.218l-.136 1.006c.22-.061.409-.092.567-.092.24 0 .444.09.61.272.168.182.251.428.251.739 0 .328-.087.589-.261.782a.833.833 0 0 1-.644.29.841.841 0 0 1-.607-.242c-.167-.16-.27-.394-.307-.698l-1.196.145c.062.542.285.98.668 1.316.384.335.868.503 1.45.503zm4.43-.11v-1.235h-1.236V34h1.235z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststyledecimalleadingzero.svg
/* harmony default export */ const liststyledecimalleadingzero = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M5.714 15.11c.624 0 1.11-.22 1.46-.66.421-.533.632-1.408.632-2.627 0-1.222-.21-2.096-.629-2.624-.351-.445-.839-.668-1.463-.668-.624 0-1.11.22-1.459.66-.422.533-.633 1.406-.633 2.619 0 1.236.192 2.095.576 2.577.384.482.89.723 1.516.723zm0-1.024a.614.614 0 0 1-.398-.14c-.115-.094-.211-.283-.287-.565-.077-.283-.115-.802-.115-1.558s.043-1.294.128-1.613c.064-.246.155-.417.272-.512a.617.617 0 0 1 .4-.143.61.61 0 0 1 .398.143c.116.095.211.284.288.567.076.283.114.802.114 1.558s-.043 1.292-.128 1.608c-.064.246-.155.417-.272.512a.617.617 0 0 1-.4.143zm6.078.914V8.531H10.79c-.14.393-.4.736-.778 1.03-.378.295-.728.495-1.05.6v1.121a4.257 4.257 0 0 0 1.595-.936V15h1.235zm3.344 0v-1.235h-1.235V15h1.235zm-9.422 9.11c.624 0 1.11-.22 1.46-.66.421-.533.632-1.408.632-2.627 0-1.222-.21-2.096-.629-2.624-.351-.445-.839-.668-1.463-.668-.624 0-1.11.22-1.459.66-.422.533-.633 1.406-.633 2.619 0 1.236.192 2.095.576 2.577.384.482.89.723 1.516.723zm0-1.024a.614.614 0 0 1-.398-.14c-.115-.094-.211-.283-.287-.565-.077-.283-.115-.802-.115-1.558s.043-1.294.128-1.613c.064-.246.155-.417.272-.512a.617.617 0 0 1 .4-.143.61.61 0 0 1 .398.143c.116.095.211.284.288.567.076.283.114.802.114 1.558s-.043 1.292-.128 1.608c-.064.246-.155.417-.272.512a.617.617 0 0 1-.4.143zm7.088.914v-1.147H10.35c.065-.111.149-.226.253-.343.104-.117.35-.354.74-.712.39-.357.66-.631.81-.821.225-.288.39-.562.493-.824.104-.263.156-.539.156-.829 0-.51-.181-.936-.544-1.279-.364-.342-.863-.514-1.499-.514-.58 0-1.063.148-1.45.444-.387.296-.617.784-.69 1.463l1.23.124c.024-.36.112-.619.264-.774.152-.155.357-.233.615-.233.261 0 .465.074.613.222.148.148.222.36.222.635 0 .25-.085.501-.255.756-.126.185-.467.536-1.024 1.055-.691.641-1.154 1.156-1.388 1.544-.235.389-.375.8-.422 1.233h4.328zm2.334 0v-1.235h-1.235V24h1.235zM5.714 34.11c.624 0 1.11-.22 1.46-.66.421-.533.632-1.408.632-2.627 0-1.222-.21-2.096-.629-2.624-.351-.445-.839-.668-1.463-.668-.624 0-1.11.22-1.459.66-.422.533-.633 1.406-.633 2.619 0 1.236.192 2.095.576 2.577.384.482.89.723 1.516.723zm0-1.024a.614.614 0 0 1-.398-.14c-.115-.094-.211-.283-.287-.565-.077-.283-.115-.802-.115-1.558s.043-1.294.128-1.613c.064-.246.155-.417.272-.512a.617.617 0 0 1 .4-.143.61.61 0 0 1 .398.143c.116.095.211.284.288.567.076.283.114.802.114 1.558s-.043 1.292-.128 1.608c-.064.246-.155.417-.272.512a.617.617 0 0 1-.4.143zm4.992 1.024c.616 0 1.13-.2 1.543-.598.413-.398.62-.88.62-1.446 0-.39-.111-.722-.332-.997a1.5 1.5 0 0 0-.886-.532c.618-.337.927-.788.927-1.353 0-.399-.15-.756-.452-1.073-.366-.386-.853-.58-1.46-.58a2.25 2.25 0 0 0-.96.2 1.617 1.617 0 0 0-.667.55c-.16.232-.28.544-.359.933l1.139.194c.032-.282.123-.495.272-.642.15-.146.33-.22.54-.22.214 0 .386.065.515.194s.193.302.193.518c0 .255-.088.46-.264.613-.175.154-.43.227-.764.218l-.136 1.006c.22-.061.408-.092.566-.092.24 0 .444.09.611.272.167.182.25.428.25.739 0 .328-.086.589-.26.782a.833.833 0 0 1-.644.29.841.841 0 0 1-.607-.242c-.167-.16-.27-.394-.308-.698l-1.195.145c.062.542.284.98.668 1.316.384.335.867.503 1.45.503zm4.43-.11v-1.235h-1.235V34h1.235z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststylelowerroman.svg
/* harmony default export */ const liststylelowerroman = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M11.88 8.7V7.558h-1.234V8.7h1.234zm0 5.3V9.333h-1.234V14h1.234zm2.5 0v-1.235h-1.234V14h1.235zm-4.75 4.7v-1.142H8.395V18.7H9.63zm0 5.3v-4.667H8.395V24H9.63zm2.5-5.3v-1.142h-1.234V18.7h1.235zm0 5.3v-4.667h-1.234V24h1.235zm2.501 0v-1.235h-1.235V24h1.235zM7.38 28.7v-1.142H6.145V28.7H7.38zm0 5.3v-4.667H6.145V34H7.38zm2.5-5.3v-1.142H8.646V28.7H9.88zm0 5.3v-4.667H8.646V34H9.88zm2.5-5.3v-1.142h-1.234V28.7h1.235zm0 5.3v-4.667h-1.234V34h1.235zm2.501 0v-1.235h-1.235V34h1.235z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststyleupperroman.svg
/* harmony default export */ const liststyleupperroman = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M11.916 15V8.558h-1.301V15h1.3zm2.465 0v-1.235h-1.235V15h1.235zM9.665 25v-6.442h-1.3V25h1.3zm2.5 0v-6.442h-1.3V25h1.3zm2.466 0v-1.235h-1.235V25h1.235zm-7.216 9v-6.442h-1.3V34h1.3zm2.5 0v-6.442h-1.3V34h1.3zm2.501 0v-6.442h-1.3V34h1.3zm2.465 0v-1.235h-1.235V34h1.235z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststylelowerlatin.svg
/* harmony default export */ const liststylelowerlatin = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"M9.62 14.105c.272 0 .528-.05.768-.153s.466-.257.677-.462c.009.024.023.072.044.145.047.161.086.283.119.365h1.221a2.649 2.649 0 0 1-.222-.626c-.04-.195-.059-.498-.059-.908l.013-1.441c0-.536-.055-.905-.165-1.105-.11-.201-.3-.367-.569-.497-.27-.13-.68-.195-1.23-.195-.607 0-1.064.108-1.371.325-.308.217-.525.55-.65 1.002l1.12.202c.076-.217.176-.369.299-.455.123-.086.294-.13.514-.13.325 0 .546.05.663.152.118.101.176.27.176.508v.123c-.222.093-.622.194-1.2.303-.427.082-.755.178-.982.288-.227.11-.403.268-.53.474a1.327 1.327 0 0 0-.188.706c0 .398.138.728.415.988.277.261.656.391 1.136.391zm.368-.87a.675.675 0 0 1-.492-.189.606.606 0 0 1-.193-.448c0-.176.08-.32.241-.435.106-.07.33-.142.673-.215a7.19 7.19 0 0 0 .751-.19v.247c0 .296-.016.496-.048.602a.773.773 0 0 1-.295.409 1.07 1.07 0 0 1-.637.22zm4.645.765v-1.235h-1.235V14h1.235zM10.2 25.105c.542 0 1.003-.215 1.382-.646.38-.43.57-1.044.57-1.84 0-.771-.187-1.362-.559-1.774a1.82 1.82 0 0 0-1.41-.617c-.522 0-.973.216-1.354.65v-2.32H7.594V25h1.147v-.686a1.9 1.9 0 0 0 .67.592c.26.133.523.2.79.2zm-.299-.975c-.354 0-.638-.164-.852-.492-.153-.232-.229-.59-.229-1.073 0-.468.098-.818.295-1.048a.93.93 0 0 1 .738-.345c.302 0 .55.118.743.354.193.236.29.62.29 1.154 0 .5-.096.868-.288 1.1-.192.233-.424.35-.697.35zm4.478.87v-1.235h-1.234V25h1.234zm-4.017 9.105c.6 0 1.08-.142 1.437-.426.357-.284.599-.704.725-1.261l-1.213-.207c-.061.326-.167.555-.316.688a.832.832 0 0 1-.576.2.916.916 0 0 1-.75-.343c-.185-.228-.278-.62-.278-1.173 0-.498.091-.853.274-1.066.183-.212.429-.318.736-.318.232 0 .42.061.565.184.145.123.238.306.28.55l1.216-.22c-.146-.501-.387-.874-.722-1.119-.336-.244-.788-.366-1.356-.366-.695 0-1.245.214-1.653.643-.407.43-.61 1.03-.61 1.8 0 .762.202 1.358.608 1.788.406.431.95.646 1.633.646zM14.633 34v-1.235h-1.235V34h1.235z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/icons/liststyleupperlatin.svg
/* harmony default export */ const liststyleupperlatin = ("<svg viewBox=\"0 0 44 44\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M35 29a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17zm0-9a1 1 0 0 1 1 1v1a1 1 0 0 1-1 1H18a1 1 0 0 1-1-1v-1a1 1 0 0 1 1-1h17z\" fill-opacity=\".163\"/><path d=\"m7.88 15 .532-1.463h2.575L11.549 15h1.415l-2.58-6.442H9.01L6.5 15h1.38zm2.69-2.549H8.811l.87-2.39.887 2.39zM14.88 15v-1.235h-1.234V15h1.234zM9.352 25c.83-.006 1.352-.02 1.569-.044.346-.038.636-.14.872-.305.236-.166.422-.387.558-.664.137-.277.205-.562.205-.855 0-.372-.106-.695-.317-.97-.21-.276-.512-.471-.905-.585a1.51 1.51 0 0 0 .661-.567 1.5 1.5 0 0 0 .244-.83c0-.28-.066-.53-.197-.754a1.654 1.654 0 0 0-.495-.539 1.676 1.676 0 0 0-.672-.266c-.25-.042-.63-.063-1.14-.063H7.158V25h2.193zm.142-3.88H8.46v-1.49h.747c.612 0 .983.007 1.112.022.217.026.38.102.49.226.11.125.165.287.165.486a.68.68 0 0 1-.192.503.86.86 0 0 1-.525.23 11.47 11.47 0 0 1-.944.023h.18zm.17 2.795H8.46v-1.723h1.05c.592 0 .977.03 1.154.092.177.062.313.16.406.295a.84.84 0 0 1 .14.492c0 .228-.06.41-.181.547a.806.806 0 0 1-.473.257c-.126.026-.423.04-.892.04zM14.88 25v-1.235h-1.234V25h1.234zm-5.018 9.11c.691 0 1.262-.17 1.711-.512.45-.341.772-.864.965-1.567l-1.261-.4c-.109.472-.287.818-.536 1.037-.25.22-.547.33-.892.33-.47 0-.85-.173-1.143-.519-.293-.345-.44-.925-.44-1.74 0-.767.15-1.322.447-1.665.297-.343.684-.514 1.162-.514.346 0 .64.096.881.29.242.193.4.457.477.79l1.288-.307c-.147-.516-.367-.911-.66-1.187-.492-.465-1.132-.698-1.92-.698-.902 0-1.63.296-2.184.89-.554.593-.83 1.426-.83 2.498 0 1.014.275 1.813.825 2.397.551.585 1.254.877 2.11.877zM14.88 34v-1.235h-1.234V34h1.234z\"/></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-list/theme/liststyles.css
var liststyles = __webpack_require__(4553);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/theme/liststyles.css

            

var liststyles_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

liststyles_options.insert = "head";
liststyles_options.singleton = true;

var liststyles_update = injectStylesIntoStyleTag_default()(liststyles/* default */.Z, liststyles_options);



/* harmony default export */ const theme_liststyles = (liststyles/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/listproperties/listpropertiesui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/listproperties/listpropertiesui
 */





















/**
 * The list properties UI plugin. It introduces the extended `'bulletedList'` and `'numberedList'` toolbar
 * buttons that allow users to control such aspects of list as the marker, start index or order.
 *
 * **Note**: Buttons introduced by this plugin override implementations from the {@link module:list/list/listui~ListUI}
 * (because they share the same names).
 *
 * @extends module:core/plugin~Plugin
 */
class ListPropertiesUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ListPropertiesUI';
	}

	init() {
		const editor = this.editor;
		const t = editor.locale.t;
		const enabledProperties = editor.config.get( 'list.properties' );

		// Note: When this plugin does not register the "bulletedList" dropdown due to properties configuration,
		// a simple button will be still registered under the same name by ListUI as a fallback. This should happen
		// in most editor configuration because the List plugin automatically requires ListUI.
		if ( enabledProperties.styles ) {
			editor.ui.componentFactory.add( 'bulletedList', getDropdownViewCreator( {
				editor,
				parentCommandName: 'bulletedList',
				buttonLabel: t( 'Bulleted List' ),
				buttonIcon: bulletedlist,
				styleGridAriaLabel: t( 'Bulleted list styles toolbar' ),
				styleDefinitions: [
					{
						label: t( 'Toggle the disc list style' ),
						tooltip: t( 'Disc' ),
						type: 'disc',
						icon: liststyledisc
					},
					{
						label: t( 'Toggle the circle list style' ),
						tooltip: t( 'Circle' ),
						type: 'circle',
						icon: liststylecircle
					},
					{
						label: t( 'Toggle the square list style' ),
						tooltip: t( 'Square' ),
						type: 'square',
						icon: liststylesquare
					}
				]
			} ) );
		}

		// Note: When this plugin does not register the "numberedList" dropdown due to properties configuration,
		// a simple button will be still registered under the same name by ListUI as a fallback. This should happen
		// in most editor configuration because the List plugin automatically requires ListUI.
		if ( enabledProperties.styles || enabledProperties.startIndex || enabledProperties.reversed ) {
			editor.ui.componentFactory.add( 'numberedList', getDropdownViewCreator( {
				editor,
				parentCommandName: 'numberedList',
				buttonLabel: t( 'Numbered List' ),
				buttonIcon: numberedlist,
				styleGridAriaLabel: t( 'Numbered list styles toolbar' ),
				styleDefinitions: [
					{
						label: t( 'Toggle the decimal list style' ),
						tooltip: t( 'Decimal' ),
						type: 'decimal',
						icon: liststyledecimal
					},
					{
						label: t( 'Toggle the decimal with leading zero list style' ),
						tooltip: t( 'Decimal with leading zero' ),
						type: 'decimal-leading-zero',
						icon: liststyledecimalleadingzero
					},
					{
						label: t( 'Toggle the lower–roman list style' ),
						tooltip: t( 'Lower–roman' ),
						type: 'lower-roman',
						icon: liststylelowerroman
					},
					{
						label: t( 'Toggle the upper–roman list style' ),
						tooltip: t( 'Upper-roman' ),
						type: 'upper-roman',
						icon: liststyleupperroman
					},
					{
						label: t( 'Toggle the lower–latin list style' ),
						tooltip: t( 'Lower-latin' ),
						type: 'lower-latin',
						icon: liststylelowerlatin
					},
					{
						label: t( 'Toggle the upper–latin list style' ),
						tooltip: t( 'Upper-latin' ),
						type: 'upper-latin',
						icon: liststyleupperlatin
					}
				]
			} ) );
		}
	}
}

// A helper that returns a function that creates a split button with a toolbar in the dropdown,
// which in turn contains buttons allowing users to change list styles in the context of the current selection.
//
// @param {Object} options
// @param {module:core/editor/editor~Editor} options.editor
// @param {'bulletedList'|'numberedList'} options.parentCommandName The name of the higher-order editor command associated with
// the set of particular list styles (e.g. "bulletedList" for "disc", "circle", and "square" styles).
// @param {String} options.buttonLabel Label of the main part of the split button.
// @param {String} options.buttonIcon The SVG string of an icon for the main part of the split button.
// @param {String} options.styleGridAriaLabel The ARIA label for the styles grid in the split button dropdown.
// @param {Object} options.styleDefinitions Definitions of the style buttons.
// @returns {Function} A function that can be passed straight into {@link module:ui/componentfactory~ComponentFactory#add}.
function getDropdownViewCreator( { editor, parentCommandName, buttonLabel, buttonIcon, styleGridAriaLabel, styleDefinitions } ) {
	const parentCommand = editor.commands.get( parentCommandName );

	// @param {module:utils/locale~Locale} locale
	// @returns {module:ui/dropdown/dropdownview~DropdownView}
	return locale => {
		const dropdownView = createDropdown( locale, SplitButtonView );
		const mainButtonView = dropdownView.buttonView;

		dropdownView.bind( 'isEnabled' ).to( parentCommand );
		dropdownView.class = 'ck-list-styles-dropdown';

		// Main button was clicked.
		mainButtonView.on( 'execute', () => {
			editor.execute( parentCommandName );
			editor.editing.view.focus();
		} );

		mainButtonView.set( {
			label: buttonLabel,
			icon: buttonIcon,
			tooltip: true,
			isToggleable: true
		} );

		mainButtonView.bind( 'isOn' ).to( parentCommand, 'value', value => !!value );

		const listPropertiesView = createListPropertiesView( {
			editor,
			dropdownView,
			parentCommandName,
			styleGridAriaLabel,
			styleDefinitions
		} );

		dropdownView.panelView.children.add( listPropertiesView );

		// Focus the editable after executing the command.
		// Overrides a default behaviour where the focus is moved to the dropdown button (#12125).
		dropdownView.on( 'execute', () => {
			editor.editing.view.focus();
		} );

		return dropdownView;
	};
}

// A helper that returns a function (factory) that creates individual buttons used by users to change styles
// of lists.
//
// @param {Object} options
// @param {module:core/editor/editor~Editor} options.editor
// @param {module:list/liststylecommand~ListStylesCommand} options.listStyleCommand The instance of the `ListStylesCommand` class.
// @param {'bulletedList'|'numberedList'} options.parentCommandName The name of the higher-order command associated with a
// particular list style (e.g. "bulletedList" is associated with "square" and "numberedList" is associated with "roman").
// @returns {Function} A function that can be passed straight into {@link module:ui/componentfactory~ComponentFactory#add}.
function getStyleButtonCreator( { editor, listStyleCommand, parentCommandName } ) {
	const locale = editor.locale;
	const parentCommand = editor.commands.get( parentCommandName );

	// @param {String} label The label of the style button.
	// @param {String} type The type of the style button (e.g. "roman" or "circle").
	// @param {String} icon The SVG string of an icon of the style button.
	// @param {String} tooltip The tooltip text of the button (shorter than verbose label).
	// @returns {module:ui/button/buttonview~ButtonView}
	return ( { label, type, icon, tooltip } ) => {
		const button = new buttonview_ButtonView( locale );

		button.set( { label, icon, tooltip } );

		listStyleCommand.on( 'change:value', () => {
			button.isOn = listStyleCommand.value === type;
		} );

		button.on( 'execute', () => {
			// If the content the selection is anchored to is a list, let's change its style.
			if ( parentCommand.value ) {
				// If the current list style is not set in the model or the style is different than the
				// one to be applied, simply apply the new style.
				if ( listStyleCommand.value !== type ) {
					editor.execute( 'listStyle', { type } );
				}
				// If the style was the same, remove it (the button works as an off toggle).
				else {
					editor.execute( 'listStyle', { type: listStyleCommand._defaultType } );
				}
			}
			// Otherwise, leave the creation of the styled list to the `ListStyleCommand`.
			else {
				editor.model.change( () => {
					editor.execute( 'listStyle', { type } );
				} );
			}
		} );

		return button;
	};
}

// A helper that creates the properties view for the individual style dropdown.
//
// @param {Object} options
// @param {module:core/editor/editor~Editor} options.editor Editor instance.
// @param {module:ui/dropdown/dropdownview~DropdownView} options.dropdownView Styles dropdown view that hosts the properties view.
// @param {'bulletedList'|'numberedList'} options.parentCommandName The name of the higher-order editor command associated with
// the set of particular list styles (e.g. "bulletedList" for "disc", "circle", and "square" styles).
// @param {Object} options.styleDefinitions Definitions of the style buttons.
// @param {String} options.styleGridAriaLabel An assistive technologies label set on the grid of styles (if the grid is rendered).
// @returns {module:list/ui/listpropertiesview~ListPropertiesView}
function createListPropertiesView( {
	editor,
	dropdownView,
	parentCommandName,
	styleDefinitions,
	styleGridAriaLabel
} ) {
	const locale = editor.locale;
	const enabledProperties = editor.config.get( 'list.properties' );
	let styleButtonViews;

	if ( parentCommandName != 'numberedList' ) {
		enabledProperties.startIndex = false;
		enabledProperties.reversed = false;
	}

	if ( enabledProperties.styles ) {
		const listStyleCommand = editor.commands.get( 'listStyle' );

		const styleButtonCreator = getStyleButtonCreator( {
			editor,
			parentCommandName,
			listStyleCommand
		} );

		// The command can be ListStyleCommand or DocumentListStyleCommand.
		const isStyleTypeSupported = typeof listStyleCommand.isStyleTypeSupported == 'function' ?
			styleDefinition => listStyleCommand.isStyleTypeSupported( styleDefinition.type ) :
			() => true;

		styleButtonViews = styleDefinitions.filter( isStyleTypeSupported ).map( styleButtonCreator );
	}

	const listPropertiesView = new ListPropertiesView( locale, {
		styleGridAriaLabel,
		enabledProperties,
		styleButtonViews
	} );

	if ( enabledProperties.styles ) {
		// Accessibility: focus the first active style when opening the dropdown.
		focusChildOnDropdownOpen( dropdownView, () => {
			return listPropertiesView.stylesView.children.find( child => child.isOn );
		} );
	}

	if ( enabledProperties.startIndex ) {
		const listStartCommand = editor.commands.get( 'listStart' );

		listPropertiesView.startIndexFieldView.bind( 'isEnabled' ).to( listStartCommand );
		listPropertiesView.startIndexFieldView.fieldView.bind( 'value' ).to( listStartCommand );
		listPropertiesView.on( 'listStart', ( evt, data ) => editor.execute( 'listStart', data ) );
	}

	if ( enabledProperties.reversed ) {
		const listReversedCommand = editor.commands.get( 'listReversed' );

		listPropertiesView.reversedSwitchButtonView.bind( 'isEnabled' ).to( listReversedCommand );
		listPropertiesView.reversedSwitchButtonView.bind( 'isOn' ).to( listReversedCommand, 'value' );
		listPropertiesView.on( 'listReversed', () => {
			const isReversed = listReversedCommand.value;

			editor.execute( 'listReversed', { reversed: !isReversed } );
		} );
	}

	// Make sure applying styles closes the dropdown.
	listPropertiesView.delegate( 'execute' ).to( dropdownView );

	return listPropertiesView;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/documentlistproperties.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/documentlistproperties
 */





/**
 * The document list properties feature.
 *
 * This is a "glue" plugin that loads the
 * {@link module:list/documentlistproperties/documentlistpropertiesediting~DocumentListPropertiesEditing document list properties
 * editing feature} and the {@link module:list/listproperties/listpropertiesui~ListPropertiesUI list properties UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class DocumentListProperties extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ DocumentListPropertiesEditing, ListPropertiesUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'DocumentListProperties';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/converters
 */

/**
 * Returns a function that converts the model "url" attribute to the view representation.
 *
 * Depending on the configuration, the view representation can be "semantic" (for the data pipeline):
 *
 *		<figure class="media">
 *			<oembed url="foo"></oembed>
 *		</figure>
 *
 * or "non-semantic" (for the editing view pipeline):
 *
 *		<figure class="media">
 *			<div data-oembed-url="foo">[ non-semantic media preview for "foo" ]</div>
 *		</figure>
 *
 * **Note:** Changing the model "url" attribute replaces the entire content of the
 * `<figure>` in the view.
 *
 * @param {module:media-embed/mediaregistry~MediaRegistry} registry The registry providing
 * the media and their content.
 * @param {Object} options
 * @param {String} [options.elementName] When set, overrides the default element name for semantic media embeds.
 * @param {String} [options.renderMediaPreview] When `true`, the converter will create the view in the non-semantic form.
 * @param {String} [options.renderForEditingView] When `true`, the converter will create a view specific for the
 * editing pipeline (e.g. including CSS classes, content placeholders).
 * @returns {Function}
 */
function modelToViewUrlAttributeConverter( registry, options ) {
	return dispatcher => {
		dispatcher.on( 'attribute:url:media', converter );
	};

	function converter( evt, data, conversionApi ) {
		if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
			return;
		}

		const url = data.attributeNewValue;
		const viewWriter = conversionApi.writer;
		const figure = conversionApi.mapper.toViewElement( data.item );
		const mediaContentElement = [ ...figure.getChildren() ]
			.find( child => child.getCustomProperty( 'media-content' ) );

		// TODO: removing the wrapper and creating it from scratch is a hack. We can do better than that.
		viewWriter.remove( mediaContentElement );

		const mediaViewElement = registry.getMediaViewElement( viewWriter, url, options );

		viewWriter.insert( viewWriter.createPositionAt( figure, 0 ), mediaViewElement );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/utils
 */



/**
 * Converts a given {@link module:engine/view/element~Element} to a media embed widget:
 * * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to recognize the media widget element.
 * * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
 *
 * @param {module:engine/view/element~Element} viewElement
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
 * @param {String} label The element's label.
 * @returns {module:engine/view/element~Element}
 */
function toMediaWidget( viewElement, writer, label ) {
	writer.setCustomProperty( 'media', true, viewElement );

	return toWidget( viewElement, writer, { label } );
}

/**
 * Returns a media widget editing view element if one is selected.
 *
 * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
 * @returns {module:engine/view/element~Element|null}
 */
function getSelectedMediaViewWidget( selection ) {
	const viewElement = selection.getSelectedElement();

	if ( viewElement && isMediaWidget( viewElement ) ) {
		return viewElement;
	}

	return null;
}

/**
 * Checks if a given view element is a media widget.
 *
 * @param {module:engine/view/element~Element} viewElement
 * @returns {Boolean}
 */
function isMediaWidget( viewElement ) {
	return !!viewElement.getCustomProperty( 'media' ) && isWidget( viewElement );
}

/**
 * Creates a view element representing the media. Either a "semantic" one for the data pipeline:
 *
 *		<figure class="media">
 *			<oembed url="foo"></oembed>
 *		</figure>
 *
 * or a "non-semantic" (for the editing view pipeline):
 *
 *		<figure class="media">
 *			<div data-oembed-url="foo">[ non-semantic media preview for "foo" ]</div>
 *		</figure>
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} writer
 * @param {module:media-embed/mediaregistry~MediaRegistry} registry
 * @param {String} url
 * @param {Object} options
 * @param {String} [options.elementName]
 * @param {Boolean} [options.useSemanticWrapper]
 * @param {Boolean} [options.renderForEditingView]
 * @returns {module:engine/view/containerelement~ContainerElement}
 */
function createMediaFigureElement( writer, registry, url, options ) {
	return writer.createContainerElement( 'figure', { class: 'media' }, [
		registry.getMediaViewElement( writer, url, options ),
		writer.createSlot()
	] );
}

/**
 * Returns a selected media element in the model, if any.
 *
 * @param {module:engine/model/selection~Selection} selection
 * @returns {module:engine/model/element~Element|null}
 */
function getSelectedMediaModelWidget( selection ) {
	const selectedElement = selection.getSelectedElement();

	if ( selectedElement && selectedElement.is( 'element', 'media' ) ) {
		return selectedElement;
	}

	return null;
}

/**
 * Creates a media element and inserts it into the model.
 *
 * **Note**: This method will use {@link module:engine/model/model~Model#insertContent `model.insertContent()`} logic of inserting content
 * if no `insertPosition` is passed.
 *
 * @param {module:engine/model/model~Model} model
 * @param {String} url An URL of an embeddable media.
 * @param {module:engine/model/range~Range} [insertRange] The range to insert the media. If not specified,
 * the default behavior of {@link module:engine/model/model~Model#insertContent `model.insertContent()`} will
 * be applied.
 * @param {Boolean} findOptimalPosition If true it will try to find optimal position to insert media without breaking content
 * in which a selection is.
 */
function insertMedia( model, url, selectable, findOptimalPosition ) {
	model.change( writer => {
		const mediaElement = writer.createElement( 'media', { url } );

		model.insertObject( mediaElement, selectable, null, {
			setSelection: 'on',
			findOptimalPosition
		} );
	} );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/mediaembedcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/mediaembedcommand
 */





/**
 * The insert media command.
 *
 * The command is registered by the {@link module:media-embed/mediaembedediting~MediaEmbedEditing} as `'mediaEmbed'`.
 *
 * To insert media at the current selection, execute the command and specify the URL:
 *
 *		editor.execute( 'mediaEmbed', 'http://url.to.the/media' );
 *
 * @extends module:core/command~Command
 */
class MediaEmbedCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const selection = model.document.selection;
		const selectedMedia = getSelectedMediaModelWidget( selection );

		this.value = selectedMedia ? selectedMedia.getAttribute( 'url' ) : null;

		this.isEnabled = isMediaSelected( selection ) || isAllowedInParent( selection, model );
	}

	/**
	 * Executes the command, which either:
	 *
	 * * updates the URL of the selected media,
	 * * inserts the new media into the editor and puts the selection around it.
	 *
	 * @fires execute
	 * @param {String} url The URL of the media.
	 */
	execute( url ) {
		const model = this.editor.model;
		const selection = model.document.selection;
		const selectedMedia = getSelectedMediaModelWidget( selection );

		if ( selectedMedia ) {
			model.change( writer => {
				writer.setAttribute( 'url', url, selectedMedia );
			} );
		} else {
			insertMedia( model, url, selection, true );
		}
	}
}

// Checks if the table is allowed in the parent.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/model~Model} model
// @returns {Boolean}
function isAllowedInParent( selection, model ) {
	const insertionRange = utils_findOptimalInsertionRange( selection, model );
	let parent = insertionRange.start.parent;

	// The model.insertContent() will remove empty parent (unless it is a $root or a limit).
	if ( parent.isEmpty && !model.schema.isLimit( parent ) ) {
		parent = parent.parent;
	}

	return model.schema.checkChild( parent, 'media' );
}

// Checks if the media object is selected.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @returns {Boolean}
function isMediaSelected( selection ) {
	const element = selection.getSelectedElement();
	return !!element && element.name === 'media';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/theme/icons/media-placeholder.svg
/* harmony default export */ const media_placeholder = ("<svg viewBox=\"0 0 64 42\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M47.426 17V3.713L63.102 0v19.389h-.001l.001.272c0 1.595-2.032 3.43-4.538 4.098-2.506.668-4.538-.083-4.538-1.678 0-1.594 2.032-3.43 4.538-4.098.914-.244 2.032-.565 2.888-.603V4.516L49.076 7.447v9.556A1.014 1.014 0 0 0 49 17h-1.574zM29.5 17h-8.343a7.073 7.073 0 1 0-4.657 4.06v3.781H3.3a2.803 2.803 0 0 1-2.8-2.804V8.63a2.803 2.803 0 0 1 2.8-2.805h4.082L8.58 2.768A1.994 1.994 0 0 1 10.435 1.5h8.985c.773 0 1.477.448 1.805 1.149l1.488 3.177H26.7c1.546 0 2.8 1.256 2.8 2.805V17zm-11.637 0H17.5a1 1 0 0 0-1 1v.05A4.244 4.244 0 1 1 17.863 17zm29.684 2c.97 0 .953-.048.953.889v20.743c0 .953.016.905-.953.905H19.453c-.97 0-.953.048-.953-.905V19.89c0-.937-.016-.889.97-.889h28.077zm-4.701 19.338V22.183H24.154v16.155h18.692zM20.6 21.375v1.616h1.616v-1.616H20.6zm0 3.231v1.616h1.616v-1.616H20.6zm0 3.231v1.616h1.616v-1.616H20.6zm0 3.231v1.616h1.616v-1.616H20.6zm0 3.231v1.616h1.616v-1.616H20.6zm0 3.231v1.616h1.616V37.53H20.6zm24.233-16.155v1.616h1.615v-1.616h-1.615zm0 3.231v1.616h1.615v-1.616h-1.615zm0 3.231v1.616h1.615v-1.616h-1.615zm0 3.231v1.616h1.615v-1.616h-1.615zm0 3.231v1.616h1.615v-1.616h-1.615zm0 3.231v1.616h1.615V37.53h-1.615zM29.485 25.283a.4.4 0 0 1 .593-.35l9.05 4.977a.4.4 0 0 1 0 .701l-9.05 4.978a.4.4 0 0 1-.593-.35v-9.956z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/mediaregistry.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/mediaregistry
 */






const mediaPlaceholderIconViewBox = '0 0 64 42';

/**
 * A bridge between the raw media content provider definitions and the editor view content.
 *
 * It helps translating media URLs to corresponding {@link module:engine/view/element~Element view elements}.
 *
 * Mostly used by the {@link module:media-embed/mediaembedediting~MediaEmbedEditing} plugin.
 */
class MediaRegistry {
	/**
	 * Creates an instance of the {@link module:media-embed/mediaregistry~MediaRegistry} class.
	 *
	 * @param {module:utils/locale~Locale} locale The localization services instance.
	 * @param {module:media-embed/mediaembed~MediaEmbedConfig} config The configuration of the media embed feature.
	 */
	constructor( locale, config ) {
		const providers = config.providers;
		const extraProviders = config.extraProviders || [];
		const removedProviders = new Set( config.removeProviders );
		const providerDefinitions = providers
			.concat( extraProviders )
			.filter( provider => {
				const name = provider.name;

				if ( !name ) {
					/**
					 * One of the providers (or extra providers) specified in the media embed configuration
					 * has no name and will not be used by the editor. In order to get this media
					 * provider working, double check your editor configuration.
					 *
					 * @error media-embed-no-provider-name
					 */
					logWarning( 'media-embed-no-provider-name', { provider } );

					return false;
				}

				return !removedProviders.has( name );
			} );

		/**
		 * The {@link module:utils/locale~Locale} instance.
		 *
		 * @member {module:utils/locale~Locale}
		 */
		this.locale = locale;

		/**
		 * The media provider definitions available for the registry. Usually corresponding with the
		 * {@link module:media-embed/mediaembed~MediaEmbedConfig media configuration}.
		 *
		 * @member {Array}
		 */
		this.providerDefinitions = providerDefinitions;
	}

	/**
	 * Checks whether the passed URL is representing a certain media type allowed in the editor.
	 *
	 * @param {String} url The URL to be checked
	 * @returns {Boolean}
	 */
	hasMedia( url ) {
		return !!this._getMedia( url );
	}

	/**
	 * For the given media URL string and options, it returns the {@link module:engine/view/element~Element view element}
	 * representing that media.
	 *
	 * **Note:** If no URL is specified, an empty view element is returned.
	 *
	 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer used to produce a view element.
	 * @param {String} url The URL to be translated into a view element.
	 * @param {Object} options
	 * @param {String} [options.elementName]
	 * @param {Boolean} [options.renderMediaPreview]
	 * @param {Boolean} [options.renderForEditingView]
	 * @returns {module:engine/view/element~Element}
	 */
	getMediaViewElement( writer, url, options ) {
		return this._getMedia( url ).getViewElement( writer, options );
	}

	/**
	 * Returns a `Media` instance for the given URL.
	 *
	 * @protected
	 * @param {String} url The URL of the media.
	 * @returns {module:media-embed/mediaregistry~Media|null} The `Media` instance or `null` when there is none.
	 */
	_getMedia( url ) {
		if ( !url ) {
			return new Media( this.locale );
		}

		url = url.trim();

		for ( const definition of this.providerDefinitions ) {
			const previewRenderer = definition.html;
			const pattern = toArray( definition.url );

			for ( const subPattern of pattern ) {
				const match = this._getUrlMatches( url, subPattern );

				if ( match ) {
					return new Media( this.locale, url, match, previewRenderer );
				}
			}
		}

		return null;
	}

	/**
	 * Tries to match `url` to `pattern`.
	 *
	 * @private
	 * @param {String} url The URL of the media.
	 * @param {RegExp} pattern The pattern that should accept the media URL.
	 * @returns {Array|null}
	 */
	_getUrlMatches( url, pattern ) {
		// 1. Try to match without stripping the protocol and "www" subdomain.
		let match = url.match( pattern );

		if ( match ) {
			return match;
		}

		// 2. Try to match after stripping the protocol.
		let rawUrl = url.replace( /^https?:\/\//, '' );
		match = rawUrl.match( pattern );

		if ( match ) {
			return match;
		}

		// 3. Try to match after stripping the "www" subdomain.
		rawUrl = rawUrl.replace( /^www\./, '' );
		match = rawUrl.match( pattern );

		if ( match ) {
			return match;
		}

		return null;
	}
}

/**
 * Represents media defined by the provider configuration.
 *
 * It can be rendered to the {@link module:engine/view/element~Element view element} and used in the editing or data pipeline.
 *
 * @private
 */
class Media {
	constructor( locale, url, match, previewRenderer ) {
		/**
		 * The URL this Media instance represents.
		 *
		 * @member {String}
		 */
		this.url = this._getValidUrl( url );

		/**
		 * Shorthand for {@link module:utils/locale~Locale#t}.
		 *
		 * @see module:utils/locale~Locale#t
		 * @method
		 */
		this._locale = locale;

		/**
		 * The output of the `RegExp.match` which validated the {@link #url} of this media.
		 *
		 * @member {Object}
		 */
		this._match = match;

		/**
		 * The function returning the HTML string preview of this media.
		 *
		 * @member {Function}
		 */
		this._previewRenderer = previewRenderer;
	}

	/**
	 * Returns the view element representation of the media.
	 *
	 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer used to produce a view element.
	 * @param {Object} options
	 * @param {String} [options.elementName]
	 * @param {Boolean} [options.renderMediaPreview]
	 * @param {Boolean} [options.renderForEditingView]
	 * @returns {module:engine/view/element~Element}
	 */
	getViewElement( writer, options ) {
		const attributes = {};
		let viewElement;

		if ( options.renderForEditingView || ( options.renderMediaPreview && this.url && this._previewRenderer ) ) {
			if ( this.url ) {
				attributes[ 'data-oembed-url' ] = this.url;
			}

			if ( options.renderForEditingView ) {
				attributes.class = 'ck-media__wrapper';
			}

			const mediaHtml = this._getPreviewHtml( options );

			viewElement = writer.createRawElement( 'div', attributes, ( domElement, domConverter ) => {
				domConverter.setContentOf( domElement, mediaHtml );
			} );
		} else {
			if ( this.url ) {
				attributes.url = this.url;
			}

			viewElement = writer.createEmptyElement( options.elementName, attributes );
		}

		writer.setCustomProperty( 'media-content', true, viewElement );

		return viewElement;
	}

	/**
	 * Returns the HTML string of the media content preview.
	 *
	 * @param {module:engine/view/downcastwriter~DowncastWriter} writer The view writer used to produce a view element.
	 * @param {Object} options
	 * @param {Boolean} [options.renderForEditingView]
	 * @returns {String}
	 */
	_getPreviewHtml( options ) {
		if ( this._previewRenderer ) {
			return this._previewRenderer( this._match );
		} else {
			// The placeholder only makes sense for editing view and media which have URLs.
			// Placeholder is never displayed in data and URL-less media have no content.
			if ( this.url && options.renderForEditingView ) {
				return this._getPlaceholderHtml();
			}

			return '';
		}
	}

	/**
	 * Returns the placeholder HTML when the media has no content preview.
	 *
	 * @returns {String}
	 */
	_getPlaceholderHtml() {
		const icon = new IconView();
		const t = this._locale.t;

		icon.content = media_placeholder;
		icon.viewBox = mediaPlaceholderIconViewBox;

		const placeholder = new Template( {
			tag: 'div',
			attributes: {
				class: 'ck ck-reset_all ck-media__placeholder'
			},
			children: [
				{
					tag: 'div',
					attributes: {
						class: 'ck-media__placeholder__icon'
					},
					children: [ icon ]
				},
				{
					tag: 'a',
					attributes: {
						class: 'ck-media__placeholder__url',
						target: '_blank',
						rel: 'noopener noreferrer',
						href: this.url,
						'data-cke-tooltip-text': t( 'Open media in new tab' )
					},
					children: [
						{
							tag: 'span',
							attributes: {
								class: 'ck-media__placeholder__url__text'
							},
							children: [ this.url ]
						}
					]
				}
			]
		} ).render();

		return placeholder.outerHTML;
	}

	/**
	 * Returns the full URL to the specified media.
	 *
	 * @param {String} url The URL of the media.
	 * @returns {String|null}
	 */
	_getValidUrl( url ) {
		if ( !url ) {
			return null;
		}

		if ( url.match( /^https?/ ) ) {
			return url;
		}

		return 'https://' + url;
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaembedediting.css
var mediaembedediting = __webpack_require__(952);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaembedediting.css

            

var mediaembedediting_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

mediaembedediting_options.insert = "head";
mediaembedediting_options.singleton = true;

var mediaembedediting_update = injectStylesIntoStyleTag_default()(mediaembedediting/* default */.Z, mediaembedediting_options);



/* harmony default export */ const theme_mediaembedediting = (mediaembedediting/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/mediaembedediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/mediaembedediting
 */











/**
 * The media embed editing feature.
 *
 * @extends module:core/plugin~Plugin
 */
class MediaEmbedEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'MediaEmbedEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'mediaEmbed', {
			elementName: 'oembed',
			providers: [
				{
					name: 'dailymotion',
					url: /^dailymotion\.com\/video\/(\w+)/,
					html: match => {
						const id = match[ 1 ];

						return (
							'<div style="position: relative; padding-bottom: 100%; height: 0; ">' +
								`<iframe src="https://www.dailymotion.com/embed/video/${ id }" ` +
									'style="position: absolute; width: 100%; height: 100%; top: 0; left: 0;" ' +
									'frameborder="0" width="480" height="270" allowfullscreen allow="autoplay">' +
								'</iframe>' +
							'</div>'
						);
					}
				},

				{
					name: 'spotify',
					url: [
						/^open\.spotify\.com\/(artist\/\w+)/,
						/^open\.spotify\.com\/(album\/\w+)/,
						/^open\.spotify\.com\/(track\/\w+)/
					],
					html: match => {
						const id = match[ 1 ];

						return (
							'<div style="position: relative; padding-bottom: 100%; height: 0; padding-bottom: 126%;">' +
								`<iframe src="https://open.spotify.com/embed/${ id }" ` +
									'style="position: absolute; width: 100%; height: 100%; top: 0; left: 0;" ' +
									'frameborder="0" allowtransparency="true" allow="encrypted-media">' +
								'</iframe>' +
							'</div>'
						);
					}
				},

				{
					name: 'youtube',
					url: [
						/^(?:m\.)?youtube\.com\/watch\?v=([\w-]+)(?:&t=(\d+))?/,
						/^(?:m\.)?youtube\.com\/v\/([\w-]+)(?:\?t=(\d+))?/,
						/^youtube\.com\/embed\/([\w-]+)(?:\?start=(\d+))?/,
						/^youtu\.be\/([\w-]+)(?:\?t=(\d+))?/
					],
					html: match => {
						const id = match[ 1 ];
						const time = match[ 2 ];

						return (
							'<div style="position: relative; padding-bottom: 100%; height: 0; padding-bottom: 56.2493%;">' +
								`<iframe src="https://www.youtube.com/embed/${ id }${ time ? `?start=${ time }` : '' }" ` +
									'style="position: absolute; width: 100%; height: 100%; top: 0; left: 0;" ' +
									'frameborder="0" allow="autoplay; encrypted-media" allowfullscreen>' +
								'</iframe>' +
							'</div>'
						);
					}
				},

				{
					name: 'vimeo',
					url: [
						/^vimeo\.com\/(\d+)/,
						/^vimeo\.com\/[^/]+\/[^/]+\/video\/(\d+)/,
						/^vimeo\.com\/album\/[^/]+\/video\/(\d+)/,
						/^vimeo\.com\/channels\/[^/]+\/(\d+)/,
						/^vimeo\.com\/groups\/[^/]+\/videos\/(\d+)/,
						/^vimeo\.com\/ondemand\/[^/]+\/(\d+)/,
						/^player\.vimeo\.com\/video\/(\d+)/
					],
					html: match => {
						const id = match[ 1 ];

						return (
							'<div style="position: relative; padding-bottom: 100%; height: 0; padding-bottom: 56.2493%;">' +
								`<iframe src="https://player.vimeo.com/video/${ id }" ` +
									'style="position: absolute; width: 100%; height: 100%; top: 0; left: 0;" ' +
									'frameborder="0" webkitallowfullscreen mozallowfullscreen allowfullscreen>' +
								'</iframe>' +
							'</div>'
						);
					}
				},

				{
					name: 'instagram',
					url: /^instagram\.com\/p\/(\w+)/
				},
				{
					name: 'twitter',
					url: /^twitter\.com/
				},
				{
					name: 'googleMaps',
					url: [
						/^google\.com\/maps/,
						/^goo\.gl\/maps/,
						/^maps\.google\.com/,
						/^maps\.app\.goo\.gl/
					]
				},
				{
					name: 'flickr',
					url: /^flickr\.com/
				},
				{
					name: 'facebook',
					url: /^facebook\.com/
				}
			]
		} );

		/**
		 * The media registry managing the media providers in the editor.
		 *
		 * @member {module:media-embed/mediaregistry~MediaRegistry} #registry
		 */
		this.registry = new MediaRegistry( editor.locale, editor.config.get( 'mediaEmbed' ) );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;
		const t = editor.t;
		const conversion = editor.conversion;
		const renderMediaPreview = editor.config.get( 'mediaEmbed.previewsInData' );
		const elementName = editor.config.get( 'mediaEmbed.elementName' );

		const registry = this.registry;

		editor.commands.add( 'mediaEmbed', new MediaEmbedCommand( editor ) );

		// Configure the schema.
		schema.register( 'media', {
			inheritAllFrom: '$blockObject',
			allowAttributes: [ 'url' ]
		} );

		// Model -> Data
		conversion.for( 'dataDowncast' ).elementToStructure( {
			model: 'media',
			view: ( modelElement, { writer } ) => {
				const url = modelElement.getAttribute( 'url' );

				return createMediaFigureElement( writer, registry, url, {
					elementName,
					renderMediaPreview: url && renderMediaPreview
				} );
			}
		} );

		// Model -> Data (url -> data-oembed-url)
		conversion.for( 'dataDowncast' ).add(
			modelToViewUrlAttributeConverter( registry, {
				elementName,
				renderMediaPreview
			} ) );

		// Model -> View (element)
		conversion.for( 'editingDowncast' ).elementToStructure( {
			model: 'media',
			view: ( modelElement, { writer } ) => {
				const url = modelElement.getAttribute( 'url' );
				const figure = createMediaFigureElement( writer, registry, url, {
					elementName,
					renderForEditingView: true
				} );

				return toMediaWidget( figure, writer, t( 'media widget' ) );
			}
		} );

		// Model -> View (url -> data-oembed-url)
		conversion.for( 'editingDowncast' ).add(
			modelToViewUrlAttributeConverter( registry, {
				elementName,
				renderForEditingView: true
			} ) );

		// View -> Model (data-oembed-url -> url)
		conversion.for( 'upcast' )
			// Upcast semantic media.
			.elementToElement( {
				view: element => [ 'oembed', elementName ].includes( element.name ) && element.getAttribute( 'url' ) ?
					{ name: true } :
					null,
				model: ( viewMedia, { writer } ) => {
					const url = viewMedia.getAttribute( 'url' );

					if ( registry.hasMedia( url ) ) {
						return writer.createElement( 'media', { url } );
					}
				}
			} )
			// Upcast non-semantic media.
			.elementToElement( {
				view: {
					name: 'div',
					attributes: {
						'data-oembed-url': true
					}
				},
				model: ( viewMedia, { writer } ) => {
					const url = viewMedia.getAttribute( 'data-oembed-url' );

					if ( registry.hasMedia( url ) ) {
						return writer.createElement( 'media', { url } );
					}
				}
			} )
			// Consume `<figure class="media">` elements, that were left after upcast.
			.add( dispatcher => {
				dispatcher.on( 'element:figure', converter );

				function converter( evt, data, conversionApi ) {
					if ( !conversionApi.consumable.consume( data.viewItem, { name: true, classes: 'media' } ) ) {
						return;
					}

					const { modelRange, modelCursor } = conversionApi.convertChildren( data.viewItem, data.modelCursor );

					data.modelRange = modelRange;
					data.modelCursor = modelCursor;

					const modelElement = first_first( modelRange.getItems() );

					if ( !modelElement ) {
						// Revert consumed figure so other features can convert it.
						conversionApi.consumable.revert( data.viewItem, { name: true, classes: 'media' } );
					}
				}
			} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/automediaembed.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/automediaembed
 */











const URL_REGEXP = /^(?:http(s)?:\/\/)?[\w-]+\.[\w-.~:/?#[\]@!$&'()*+,;=%]+$/;

/**
 * The auto-media embed plugin. It recognizes media links in the pasted content and embeds
 * them shortly after they are injected into the document.
 *
 * @extends module:core/plugin~Plugin
 */
class AutoMediaEmbed extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ Clipboard, delete_Delete, Undo ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'AutoMediaEmbed';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * The paste–to–embed `setTimeout` ID. Stored as a property to allow
		 * cleaning of the timeout.
		 *
		 * @private
		 * @member {Number} #_timeoutId
		 */
		this._timeoutId = null;

		/**
		 * The position where the `<media>` element will be inserted after the timeout,
		 * determined each time the new content is pasted into the document.
		 *
		 * @private
		 * @member {module:engine/model/liveposition~LivePosition} #_positionToInsert
		 */
		this._positionToInsert = null;
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const modelDocument = editor.model.document;

		// We need to listen on `Clipboard#inputTransformation` because we need to save positions of selection.
		// After pasting, the content between those positions will be checked for a URL that could be transformed
		// into media.
		this.listenTo( editor.plugins.get( 'ClipboardPipeline' ), 'inputTransformation', () => {
			const firstRange = modelDocument.selection.getFirstRange();

			const leftLivePosition = LivePosition.fromPosition( firstRange.start );
			leftLivePosition.stickiness = 'toPrevious';

			const rightLivePosition = LivePosition.fromPosition( firstRange.end );
			rightLivePosition.stickiness = 'toNext';

			modelDocument.once( 'change:data', () => {
				this._embedMediaBetweenPositions( leftLivePosition, rightLivePosition );

				leftLivePosition.detach();
				rightLivePosition.detach();
			}, { priority: 'high' } );
		} );

		editor.commands.get( 'undo' ).on( 'execute', () => {
			if ( this._timeoutId ) {
				dom_global.window.clearTimeout( this._timeoutId );
				this._positionToInsert.detach();

				this._timeoutId = null;
				this._positionToInsert = null;
			}
		}, { priority: 'high' } );
	}

	/**
	 * Analyzes the part of the document between provided positions in search for a URL representing media.
	 * When the URL is found, it is automatically converted into media.
	 *
	 * @protected
	 * @param {module:engine/model/liveposition~LivePosition} leftPosition Left position of the selection.
	 * @param {module:engine/model/liveposition~LivePosition} rightPosition Right position of the selection.
	 */
	_embedMediaBetweenPositions( leftPosition, rightPosition ) {
		const editor = this.editor;
		const mediaRegistry = editor.plugins.get( MediaEmbedEditing ).registry;
		// TODO: Use marker instead of LiveRange & LivePositions.
		const urlRange = new LiveRange( leftPosition, rightPosition );
		const walker = urlRange.getWalker( { ignoreElementEnd: true } );

		let url = '';

		for ( const node of walker ) {
			if ( node.item.is( '$textProxy' ) ) {
				url += node.item.data;
			}
		}

		url = url.trim();

		// If the URL does not match to universal URL regexp, let's skip that.
		if ( !url.match( URL_REGEXP ) ) {
			urlRange.detach();

			return;
		}

		// If the URL represents a media, let's use it.
		if ( !mediaRegistry.hasMedia( url ) ) {
			urlRange.detach();

			return;
		}

		const mediaEmbedCommand = editor.commands.get( 'mediaEmbed' );

		// Do not anything if media element cannot be inserted at the current position (#47).
		if ( !mediaEmbedCommand.isEnabled ) {
			urlRange.detach();

			return;
		}

		// Position won't be available in the `setTimeout` function so let's clone it.
		this._positionToInsert = LivePosition.fromPosition( leftPosition );

		// This action mustn't be executed if undo was called between pasting and auto-embedding.
		this._timeoutId = dom_global.window.setTimeout( () => {
			editor.model.change( writer => {
				this._timeoutId = null;

				writer.remove( urlRange );
				urlRange.detach();

				let insertionPosition;

				// Check if position where the media element should be inserted is still valid.
				// Otherwise leave it as undefined to use document.selection - default behavior of model.insertContent().
				if ( this._positionToInsert.root.rootName !== '$graveyard' ) {
					insertionPosition = this._positionToInsert;
				}

				insertMedia( editor.model, url, insertionPosition, false );

				this._positionToInsert.detach();
				this._positionToInsert = null;
			} );

			editor.plugins.get( 'Delete' ).requestUndoOnBackspace();
		}, 100 );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaform.css
var mediaform = __webpack_require__(3525);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaform.css

            

var mediaform_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

mediaform_options.insert = "head";
mediaform_options.singleton = true;

var mediaform_update = injectStylesIntoStyleTag_default()(mediaform/* default */.Z, mediaform_options);



/* harmony default export */ const theme_mediaform = (mediaform/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/ui/mediaformview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/ui/mediaformview
 */





// See: #8833.
// eslint-disable-next-line ckeditor5-rules/ckeditor-imports



/**
 * The media form view controller class.
 *
 * See {@link module:media-embed/ui/mediaformview~MediaFormView}.
 *
 * @extends module:ui/view~View
 */
class MediaFormView extends src_view_View {
	/**
	 * @param {Array.<Function>} validators Form validators used by {@link #isValid}.
	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
	 */
	constructor( validators, locale ) {
		super( locale );

		const t = locale.t;

		/**
		 * Tracks information about the DOM focus in the form.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * The value of the URL input.
		 *
		 * @member {String} #mediaURLInputValue
		 * @observable
		 */
		this.set( 'mediaURLInputValue', '' );

		/**
		 * The URL input view.
		 *
		 * @member {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
		 */
		this.urlInputView = this._createUrlInput();

		/**
		 * The Save button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.saveButtonView = this._createButton( t( 'Save' ), icons.check, 'ck-button-save' );
		this.saveButtonView.type = 'submit';
		this.saveButtonView.bind( 'isEnabled' ).to( this, 'mediaURLInputValue', value => !!value );

		/**
		 * The Cancel button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.cancelButtonView = this._createButton( t( 'Cancel' ), icons.cancel, 'ck-button-cancel', 'cancel' );

		/**
		 * A collection of views that can be focused in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate form fields backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke.
				focusPrevious: 'shift + tab',

				// Navigate form fields forwards using the <kbd>Tab</kbd> key.
				focusNext: 'tab'
			}
		} );

		/**
		 * An array of form validators used by {@link #isValid}.
		 *
		 * @readonly
		 * @protected
		 * @member {Array.<Function>}
		 */
		this._validators = validators;

		this.setTemplate( {
			tag: 'form',

			attributes: {
				class: [
					'ck',
					'ck-media-form',
					'ck-responsive-form'
				],

				tabindex: '-1'
			},

			children: [
				this.urlInputView,
				this.saveButtonView,
				this.cancelButtonView
			]
		} );

		injectCssTransitionDisabler( this );

		/**
		 * The default info text for the {@link #urlInputView}.
		 *
		 * @private
		 * @member {String} #_urlInputViewInfoDefault
		 */

		/**
		 * The info text with an additional tip for the {@link #urlInputView},
		 * displayed when the input has some value.
		 *
		 * @private
		 * @member {String} #_urlInputViewInfoTip
		 */
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		submitHandler( {
			view: this
		} );

		const childViews = [
			this.urlInputView,
			this.saveButtonView,
			this.cancelButtonView
		];

		childViews.forEach( v => {
			// Register the view as focusable.
			this._focusables.add( v );

			// Register the view in the focus tracker.
			this.focusTracker.add( v.element );
		} );

		// Start listening for the keystrokes coming from #element.
		this.keystrokes.listenTo( this.element );

		const stopPropagation = data => data.stopPropagation();

		// Since the form is in the dropdown panel which is a child of the toolbar, the toolbar's
		// keystroke handler would take over the key management in the URL input. We need to prevent
		// this ASAP. Otherwise, the basic caret movement using the arrow keys will be impossible.
		this.keystrokes.set( 'arrowright', stopPropagation );
		this.keystrokes.set( 'arrowleft', stopPropagation );
		this.keystrokes.set( 'arrowup', stopPropagation );
		this.keystrokes.set( 'arrowdown', stopPropagation );

		// Intercept the `selectstart` event, which is blocked by default because of the default behavior
		// of the DropdownView#panelView.
		// TODO: blocking `selectstart` in the #panelView should be configurable per–drop–down instance.
		this.listenTo( this.urlInputView.element, 'selectstart', ( evt, domEvt ) => {
			domEvt.stopPropagation();
		}, { priority: 'high' } );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Focuses the fist {@link #_focusables} in the form.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}

	/**
	 * The native DOM `value` of the {@link #urlInputView} element.
	 *
	 * **Note**: Do not confuse it with the {@link module:ui/inputtext/inputtextview~InputTextView#value}
	 * which works one way only and may not represent the actual state of the component in the DOM.
	 *
	 * @type {String}
	 */
	get url() {
		return this.urlInputView.fieldView.element.value.trim();
	}

	set url( url ) {
		this.urlInputView.fieldView.element.value = url.trim();
	}

	/**
	 * Validates the form and returns `false` when some fields are invalid.
	 *
	 * @returns {Boolean}
	 */
	isValid() {
		this.resetFormStatus();

		for ( const validator of this._validators ) {
			const errorText = validator( this );

			// One error per field is enough.
			if ( errorText ) {
				// Apply updated error.
				this.urlInputView.errorText = errorText;

				return false;
			}
		}

		return true;
	}

	/**
	 * Cleans up the supplementary error and information text of the {@link #urlInputView}
	 * bringing them back to the state when the form has been displayed for the first time.
	 *
	 * See {@link #isValid}.
	 */
	resetFormStatus() {
		this.urlInputView.errorText = null;
		this.urlInputView.infoText = this._urlInputViewInfoDefault;
	}

	/**
	 * Creates a labeled input view.
	 *
	 * @private
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView} Labeled input view instance.
	 */
	_createUrlInput() {
		const t = this.locale.t;

		const labeledInput = new LabeledFieldView( this.locale, createLabeledInputText );
		const inputField = labeledInput.fieldView;

		this._urlInputViewInfoDefault = t( 'Paste the media URL in the input.' );
		this._urlInputViewInfoTip = t( 'Tip: Paste the URL into the content to embed faster.' );

		labeledInput.label = t( 'Media URL' );
		labeledInput.infoText = this._urlInputViewInfoDefault;

		inputField.on( 'input', () => {
			// Display the tip text only when there is some value. Otherwise fall back to the default info text.
			labeledInput.infoText = inputField.element.value ? this._urlInputViewInfoTip : this._urlInputViewInfoDefault;
			this.mediaURLInputValue = inputField.element.value.trim();
		} );

		return labeledInput;
	}

	/**
	 * Creates a button view.
	 *
	 * @private
	 * @param {String} label The button label.
	 * @param {String} icon The button icon.
	 * @param {String} className The additional button CSS class name.
	 * @param {String} [eventName] An event name that the `ButtonView#execute` event will be delegated to.
	 * @returns {module:ui/button/buttonview~ButtonView} The button view instance.
	 */
	_createButton( label, icon, className, eventName ) {
		const button = new buttonview_ButtonView( this.locale );

		button.set( {
			label,
			icon,
			tooltip: true
		} );

		button.extendTemplate( {
			attributes: {
				class: className
			}
		} );

		if ( eventName ) {
			button.delegate( 'execute' ).to( this, eventName );
		}

		return button;
	}
}

/**
 * Fired when the form view is submitted (when one of the children triggered the submit event),
 * e.g. click on {@link #saveButtonView}.
 *
 * @event submit
 */

/**
 * Fired when the form view is canceled, e.g. by a click on {@link #cancelButtonView}.
 *
 * @event cancel
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/theme/icons/media.svg
/* harmony default export */ const media = ("<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\"><path d=\"M18.68 3.03c.6 0 .59-.03.59.55v12.84c0 .59.01.56-.59.56H1.29c-.6 0-.59.03-.59-.56V3.58c0-.58-.01-.55.6-.55h17.38zM15.77 15V5H4.2v10h11.57zM2 4v1h1V4H2zm0 2v1h1V6H2zm0 2v1h1V8H2zm0 2v1h1v-1H2zm0 2v1h1v-1H2zm0 2v1h1v-1H2zM17 4v1h1V4h-1zm0 2v1h1V6h-1zm0 2v1h1V8h-1zm0 2v1h1v-1h-1zm0 2v1h1v-1h-1zm0 2v1h1v-1h-1zM7.5 7.177a.4.4 0 0 1 .593-.351l5.133 2.824a.4.4 0 0 1 0 .7l-5.133 2.824a.4.4 0 0 1-.593-.35V7.176v.001z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/mediaembedui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/mediaembedui
 */








/**
 * The media embed UI plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class MediaEmbedUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ MediaEmbedEditing ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'MediaEmbedUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const command = editor.commands.get( 'mediaEmbed' );
		const registry = editor.plugins.get( MediaEmbedEditing ).registry;

		editor.ui.componentFactory.add( 'mediaEmbed', locale => {
			const dropdown = createDropdown( locale );

			const mediaForm = new MediaFormView( getFormValidators( editor.t, registry ), editor.locale );

			this._setUpDropdown( dropdown, mediaForm, command, editor );
			this._setUpForm( dropdown, mediaForm, command );

			return dropdown;
		} );
	}

	/**
	 * @private
	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdown
	 * @param {module:ui/view~View} form
	 * @param {module:media-embed/mediaembedcommand~MediaEmbedCommand} command
	 */
	_setUpDropdown( dropdown, form, command ) {
		const editor = this.editor;
		const t = editor.t;
		const button = dropdown.buttonView;

		dropdown.bind( 'isEnabled' ).to( command );
		dropdown.panelView.children.add( form );

		button.set( {
			label: t( 'Insert media' ),
			icon: media,
			tooltip: true
		} );

		// Note: Use the low priority to make sure the following listener starts working after the
		// default action of the drop-down is executed (i.e. the panel showed up). Otherwise, the
		// invisible form/input cannot be focused/selected.
		button.on( 'open', () => {
			form.disableCssTransitions();

			// Make sure that each time the panel shows up, the URL field remains in sync with the value of
			// the command. If the user typed in the input, then canceled (`urlInputView#fieldView#value` stays
			// unaltered) and re-opened it without changing the value of the media command (e.g. because they
			// didn't change the selection), they would see the old value instead of the actual value of the
			// command.
			form.url = command.value || '';
			form.urlInputView.fieldView.select();
			form.enableCssTransitions();
		}, { priority: 'low' } );

		dropdown.on( 'submit', () => {
			if ( form.isValid() ) {
				editor.execute( 'mediaEmbed', form.url );
				editor.editing.view.focus();
			}
		} );

		dropdown.on( 'change:isOpen', () => form.resetFormStatus() );
		dropdown.on( 'cancel', () => {
			editor.editing.view.focus();
		} );
	}

	/**
	 * @private
	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdown
	 * @param {module:ui/view~View} form
	 * @param {module:media-embed/mediaembedcommand~MediaEmbedCommand} command
	 */
	_setUpForm( dropdown, form, command ) {
		form.delegate( 'submit', 'cancel' ).to( dropdown );
		form.urlInputView.bind( 'value' ).to( command, 'value' );

		// Form elements should be read-only when corresponding commands are disabled.
		form.urlInputView.bind( 'isReadOnly' ).to( command, 'isEnabled', value => !value );
	}
}

function getFormValidators( t, registry ) {
	return [
		form => {
			if ( !form.url.length ) {
				return t( 'The URL must not be empty.' );
			}
		},
		form => {
			if ( !registry.hasMedia( form.url ) ) {
				return t( 'This media URL is not supported.' );
			}
		}
	];
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaembed.css
var mediaembed = __webpack_require__(5777);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/theme/mediaembed.css

            

var mediaembed_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

mediaembed_options.insert = "head";
mediaembed_options.singleton = true;

var mediaembed_update = injectStylesIntoStyleTag_default()(mediaembed/* default */.Z, mediaembed_options);



/* harmony default export */ const theme_mediaembed = (mediaembed/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/mediaembed.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/mediaembed
 */










/**
 * The media embed plugin.
 *
 * For a detailed overview, check the {@glink features/media-embed Media Embed feature documentation}.
 *
 * This is a "glue" plugin which loads the following plugins:
 *
 * * The {@link module:media-embed/mediaembedediting~MediaEmbedEditing media embed editing feature},
 * * The {@link module:media-embed/mediaembedui~MediaEmbedUI media embed UI feature} and
 * * The {@link module:media-embed/automediaembed~AutoMediaEmbed auto-media embed feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class MediaEmbed extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ MediaEmbedEditing, MediaEmbedUI, AutoMediaEmbed, Widget ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'MediaEmbed';
	}
}

/**
 * The media embed provider descriptor. Used in
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#providers `config.mediaEmbed.providers`} and
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#extraProviders `config.mediaEmbed.extraProviders`}.
 *
 * See {@link module:media-embed/mediaembed~MediaEmbedConfig} to learn more.
 *
 *		{
 *			name: 'example',
 *
 *			// The following RegExp matches https://www.example.com/media/{media id},
 *			// (either with "http(s)://" and "www" or without), so the valid URLs are:
 *			//
 *			// * https://www.example.com/media/{media id},
 *			// * http://www.example.com/media/{media id},
 *			// * www.example.com/media/{media id},
 *			// * example.com/media/{media id}
 *			url: /^example\.com\/media\/(\w+)/,
 *
 *			// The rendering function of the provider.
 *			// Used to represent the media when editing the content (i.e. in the view)
 *			// and also in the data output of the editor if semantic data output is disabled.
 *			html: match => `The HTML representing the media with ID=${ match[ 1 ] }.`
 *		}
 *
 * You can allow any sort of media in the editor using the "allow–all" `RegExp`.
 * But mind that, since URLs are processed in the order of configuration, if one of the previous
 * `RegExps` matches the URL, it will have a precedence over this one.
 *
 *		{
 *			name: 'allow-all',
 *			url: /^.+/
 *		}
 *
 * To implement responsive media, you can use the following HTML structure:
 *
 *		{
 *			...
 *			html: match =>
 *				'<div style="position:relative; padding-bottom:100%; height:0">' +
 *					'<iframe src="..." frameborder="0" ' +
 *						'style="position:absolute; width:100%; height:100%; top:0; left:0">' +
 *					'</iframe>' +
 *				'</div>'
 *		}
 *
 * @typedef {Object} module:media-embed/mediaembed~MediaEmbedProvider
 * @property {String} name The name of the provider. Used e.g. when
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#removeProviders removing providers}.
 * @property {RegExp|Array.<RegExp>} url The `RegExp` object (or array of objects) defining the URL of the media.
 * If any URL matches the `RegExp`, it becomes the media in the editor model, as defined by the provider. The result
 * of matching (output of `String.prototype.match()`) is passed to the `html` rendering function of the media.
 *
 * **Note:** You do not need to include the protocol (`http://`, `https://`) and `www` subdomain in your `RegExps`,
 * they are stripped from the URLs before matching anyway.
 * @property {Function} [html] (optional) The rendering function of the media. The function receives the entire matching
 * array from the corresponding `url` `RegExp` as an argument, allowing rendering a dedicated
 * preview of the media identified by a certain ID or a hash. When not defined, the media embed feature
 * will use a generic media representation in the view and output data.
 * Note that when
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#previewsInData `config.mediaEmbed.previewsInData`}
 * is `true`, the rendering function **will always** be used for the media in the editor data output.
 */

/**
 * The configuration of the {@link module:media-embed/mediaembed~MediaEmbed} feature.
 *
 * Read more in {@link module:media-embed/mediaembed~MediaEmbedConfig}.
 *
 * @member {module:media-embed/mediaembed~MediaEmbedConfig} module:core/editor/editorconfig~EditorConfig#mediaEmbed
 */

/**
 * The configuration of the media embed features.
 *
 * Read more about {@glink features/media-embed#configuration configuring the media embed feature}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				mediaEmbed: ... // Media embed feature options.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface MediaEmbedConfig
 */

/**
 * The default media providers supported by the editor.
 *
 * The names of providers with rendering functions (previews):
 *
 * * "dailymotion",
 * * "spotify",
 * * "youtube",
 * * "vimeo"
 *
 * The names of providers without rendering functions:
 *
 * * "instagram",
 * * "twitter",
 * * "googleMaps",
 * * "flickr",
 * * "facebook"
 *
 * See the {@link module:media-embed/mediaembed~MediaEmbedProvider provider syntax} to learn more about
 * different kinds of media and media providers.
 *
 * **Note**: The default media provider configuration may not support all possible media URLs,
 * only the most common are included.
 *
 * Media without rendering functions are always represented in the data using the "semantic" markup. See
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#previewsInData `config.mediaEmbed.previewsInData`} to
 * learn more about possible data outputs.
 *
 * The priority of media providers corresponds to the order of configuration. The first provider
 * to match the URL is always used, even if there are other providers that support a particular URL.
 * The URL is never matched against the remaining providers.
 *
 * To discard **all** default media providers, simply override this configuration with your own
 * {@link module:media-embed/mediaembed~MediaEmbedProvider definitions}:
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				plugins: [ MediaEmbed, ... ],
 *				mediaEmbed: {
 *					providers: [
 *						{
 *							 name: 'myProvider',
 *							 url: /^example\.com\/media\/(\w+)/,
 *							 html: match => '...'
 *						},
 *						...
 * 					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * You can take inspiration from the default configuration of this feature which you can find in:
 * https://github.com/ckeditor/ckeditor5-media-embed/blob/master/src/mediaembedediting.js
 *
 * To **extend** the list of default providers, use
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#extraProviders `config.mediaEmbed.extraProviders`}.
 *
 * To **remove** certain providers, use
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#removeProviders `config.mediaEmbed.removeProviders`}.
 *
 * @member {Array.<module:media-embed/mediaembed~MediaEmbedProvider>} module:media-embed/mediaembed~MediaEmbedConfig#providers
 */

/**
 * The additional media providers supported by the editor. This configuration helps extend the default
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#providers}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				plugins: [ MediaEmbed, ... ],
 *				mediaEmbed: {
 *					extraProviders: [
 *						{
 *							 name: 'extraProvider',
 *							 url: /^example\.com\/media\/(\w+)/,
 *							 html: match => '...'
 *						},
 *						...
 * 					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See the {@link module:media-embed/mediaembed~MediaEmbedProvider provider syntax} to learn more.
 *
 * @member {Array.<module:media-embed/mediaembed~MediaEmbedProvider>} module:media-embed/mediaembed~MediaEmbedConfig#extraProviders
 */

/**
 * The list of media providers that should not be used despite being available in
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#providers `config.mediaEmbed.providers`} and
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#extraProviders `config.mediaEmbed.extraProviders`}
 *
 *		mediaEmbed: {
 *			removeProviders: [ 'youtube', 'twitter' ]
 *		}
 *
 * @member {Array.<String>} module:media-embed/mediaembed~MediaEmbedConfig#removeProviders
 */

/**
 * Overrides the element name used for "semantic" data.
 *
 * This is not relevant if {@link module:media-embed/mediaembed~MediaEmbedConfig#previewsInData `config.mediaEmbed.previewsInData`}
 * is set to `true`.
 *
 * When not set, the feature produces the `<oembed>` tag:
 *
 *		<figure class="media">
 *			<oembed url="https://url"></oembed>
 *		</figure>
 *
 * To override the element name with, for instance, the `o-embed` name:
 *
 *		mediaEmbed: {
 *			elementName: 'o-embed'
 *		}
 *
 * This will produce semantic data with the `<o-embed>` tag:
 *
 *		<figure class="media">
 *			<o-embed url="https://url"></o-embed>
 *		</figure>
 *
 * @default 'oembed'
 * @member {String} [module:media-embed/mediaembed~MediaEmbedConfig#elementName]
 */

/**
 * Controls the data format produced by the feature.
 *
 * When `false` (default), the feature produces "semantic" data, i.e. it does not include the preview of
 * the media, just the `<oembed>` tag with the `url` attribute:
 *
 *		<figure class="media">
 *			<oembed url="https://url"></oembed>
 *		</figure>
 *
 * When `true`, the media is represented in the output in the same way it looks in the editor,
 * i.e. the media preview is saved to the database:
 *
 *		<figure class="media">
 *			<div data-oembed-url="https://url">
 *				<iframe src="https://preview"></iframe>
 *			</div>
 *		</figure>
 *
 * **Note:** Media without preview are always represented in the data using the "semantic" markup
 * regardless of the value of the `previewsInData`. Learn more about different kinds of media
 * in the {@link module:media-embed/mediaembed~MediaEmbedConfig#providers `config.mediaEmbed.providers`}
 * configuration description.
 *
 * @member {Boolean} [module:media-embed/mediaembed~MediaEmbedConfig#previewsInData=false]
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-media-embed/src/mediaembedtoolbar.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module media-embed/mediaembedtoolbar
 */






/**
 * The media embed toolbar plugin. It creates a toolbar for media embed that shows up when the media element is selected.
 *
 * Instances of toolbar components (e.g. buttons) are created based on the
 * {@link module:media-embed/mediaembed~MediaEmbedConfig#toolbar `media.toolbar` configuration option}.
 *
 * @extends module:core/plugin~Plugin
 */
class MediaEmbedToolbar extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ WidgetToolbarRepository ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'MediaEmbedToolbar';
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const editor = this.editor;
		const t = editor.t;
		const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );

		widgetToolbarRepository.register( 'mediaEmbed', {
			ariaLabel: t( 'Media toolbar' ),
			items: editor.config.get( 'mediaEmbed.toolbar' ) || [],
			getRelatedElement: getSelectedMediaViewWidget
		} );
	}
}

/**
 * Items to be placed in the media embed toolbar.
 * This option requires adding {@link module:media-embed/mediaembedtoolbar~MediaEmbedToolbar} to the plugin list.
 *
 * Read more about configuring toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
 *
 * @member {Array.<String>} module:media-embed/mediaembed~MediaEmbedConfig#toolbar
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/src/mentioncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module mention/mentioncommand
 */






/**
 * The mention command.
 *
 * The command is registered by {@link module:mention/mentionediting~MentionEditing} as `'mention'`.
 *
 * To insert a mention onto a range, execute the command and specify a mention object with a range to replace:
 *
 *		const focus = editor.model.document.selection.focus;
 *
 *		// It will replace one character before the selection focus with the '#1234' text
 *		// with the mention attribute filled with passed attributes.
 *		editor.execute( 'mention', {
 *			marker: '#',
 *			mention: {
 *				id: '#1234',
 *				name: 'Foo',
 *				title: 'Big Foo'
 *			},
 *			range: editor.model.createRange( focus.getShiftedBy( -1 ), focus )
 *		} );
 *
 *		// It will replace one character before the selection focus with the 'The "Big Foo"' text
 *		// with the mention attribute filled with passed attributes.
 *		editor.execute( 'mention', {
 *			marker: '#',
 *			mention: {
 *				id: '#1234',
 *				name: 'Foo',
 *				title: 'Big Foo'
 *			},
 *			text: 'The "Big Foo"',
 *			range: editor.model.createRange( focus.getShiftedBy( -1 ), focus )
 *		} );
 *
 * @extends module:core/command~Command
 */
class MentionCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const doc = model.document;

		this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'mention' );
	}

	/**
	 * Executes the command.
	 *
	 * @param {Object} [options] Options for the executed command.
	 * @param {Object|String} options.mention The mention object to insert. When a string is passed, it will be used to create a plain
	 * object with the name attribute that equals the passed string.
	 * @param {String} options.marker The marker character (e.g. `'@'`).
	 * @param {String} [options.text] The text of the inserted mention. Defaults to the full mention string composed from `marker` and
	 * `mention` string or `mention.id` if an object is passed.
	 * @param {module:engine/model/range~Range} [options.range] The range to replace.
	 * Note that the replaced range might be shorter than the inserted text with the mention attribute.
	 * @fires execute
	 */
	execute( options ) {
		const model = this.editor.model;
		const document = model.document;
		const selection = document.selection;

		const mentionData = typeof options.mention == 'string' ? { id: options.mention } : options.mention;
		const mentionID = mentionData.id;

		const range = options.range || selection.getFirstRange();

		const mentionText = options.text || mentionID;

		const mention = _addMentionAttributes( { _text: mentionText, id: mentionID }, mentionData );

		if ( options.marker.length != 1 ) {
			/**
			 * The marker must be a single character.
			 *
			 * Correct markers: `'@'`, `'#'`.
			 *
			 * Incorrect markers: `'$$'`, `'[@'`.
			 *
			 * See {@link module:mention/mention~MentionConfig}.
			 *
			 * @error mentioncommand-incorrect-marker
			 */
			throw new CKEditorError(
				'mentioncommand-incorrect-marker',
				this
			);
		}

		if ( mentionID.charAt( 0 ) != options.marker ) {
			/**
			 * The feed item ID must start with the marker character.
			 *
			 * Correct mention feed setting:
			 *
			 *		mentions: [
			 *			{
			 *				marker: '@',
			 *				feed: [ '@Ann', '@Barney', ... ]
			 *			}
			 *		]
			 *
			 * Incorrect mention feed setting:
			 *
			 *		mentions: [
			 *			{
			 *				marker: '@',
			 *				feed: [ 'Ann', 'Barney', ... ]
			 *			}
			 *		]
			 *
			 * See {@link module:mention/mention~MentionConfig}.
			 *
			 * @error mentioncommand-incorrect-id
			 */
			throw new CKEditorError(
				'mentioncommand-incorrect-id',
				this
			);
		}

		model.change( writer => {
			const currentAttributes = toMap( selection.getAttributes() );
			const attributesWithMention = new Map( currentAttributes.entries() );

			attributesWithMention.set( 'mention', mention );

			// Replace a range with the text with a mention.
			model.insertContent( writer.createText( mentionText, attributesWithMention ), range );
			model.insertContent( writer.createText( ' ', currentAttributes ), range.start.getShiftedBy( mentionText.length ) );
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/src/mentionediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module mention/mentionediting
 */






/**
 * The mention editing feature.
 *
 * It introduces the {@link module:mention/mentioncommand~MentionCommand command} and the `mention`
 * attribute in the {@link module:engine/model/model~Model model} which renders in the {@link module:engine/view/view view}
 * as a `<span class="mention" data-mention="@mention">`.
 *
 * @extends module:core/plugin~Plugin
 */
class MentionEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'MentionEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const model = editor.model;
		const doc = model.document;

		// Allow the mention attribute on all text nodes.
		model.schema.extend( '$text', { allowAttributes: 'mention' } );

		// Upcast conversion.
		editor.conversion.for( 'upcast' ).elementToAttribute( {
			view: {
				name: 'span',
				key: 'data-mention',
				classes: 'mention'
			},
			model: {
				key: 'mention',
				value: viewElement => _toMentionAttribute( viewElement )
			}
		} );

		// Downcast conversion.
		editor.conversion.for( 'downcast' ).attributeToElement( {
			model: 'mention',
			view: createViewMentionElement
		} );
		editor.conversion.for( 'downcast' ).add( preventPartialMentionDowncast );

		doc.registerPostFixer( writer => removePartialMentionPostFixer( writer, doc, model.schema ) );
		doc.registerPostFixer( writer => extendAttributeOnMentionPostFixer( writer, doc ) );
		doc.registerPostFixer( writer => selectionMentionAttributePostFixer( writer, doc ) );

		editor.commands.add( 'mention', new MentionCommand( editor ) );
	}
}

function _addMentionAttributes( baseMentionData, data ) {
	return Object.assign( { uid: uid() }, baseMentionData, data || {} );
}

/**
 * Creates a mention attribute value from the provided view element and optional data.
 *
 * This function is exposed as
 * {@link module:mention/mention~Mention#toMentionAttribute `editor.plugins.get( 'Mention' ).toMentionAttribute()`}.
 *
 * @protected
 * @param {module:engine/view/element~Element} viewElementOrMention
 * @param {String|Object} [data] Mention data to be extended.
 * @returns {module:mention/mention~MentionAttribute}
 */
function _toMentionAttribute( viewElementOrMention, data ) {
	const dataMention = viewElementOrMention.getAttribute( 'data-mention' );

	const textNode = viewElementOrMention.getChild( 0 );

	// Do not convert empty mentions.
	if ( !textNode ) {
		return;
	}

	const baseMentionData = {
		id: dataMention,
		_text: textNode.data
	};

	return _addMentionAttributes( baseMentionData, data );
}

// A converter that blocks partial mention from being converted.
//
// This converter is registered with 'highest' priority in order to consume mention attribute before it is converted by
// any other converters. This converter only consumes partial mention - those whose `_text` attribute is not equal to text with mention
// attribute. This may happen when copying part of mention text.
//
// @param {module:engine/conversion/dwoncastdispatcher~DowncastDispatcher}
function preventPartialMentionDowncast( dispatcher ) {
	dispatcher.on( 'attribute:mention', ( evt, data, conversionApi ) => {
		const mention = data.attributeNewValue;

		if ( !data.item.is( '$textProxy' ) || !mention ) {
			return;
		}

		const start = data.range.start;
		const textNode = start.textNode || start.nodeAfter;

		if ( textNode.data != mention._text ) {
			// Consume item to prevent partial mention conversion.
			conversionApi.consumable.consume( data.item, evt.name );
		}
	}, { priority: 'highest' } );
}

// Creates a mention element from the mention data.
//
// @param {Object} mention
// @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
// @returns {module:engine/view/attributeelement~AttributeElement}
function createViewMentionElement( mention, { writer } ) {
	if ( !mention ) {
		return;
	}

	const attributes = {
		class: 'mention',
		'data-mention': mention.id
	};

	const options = {
		id: mention.uid,
		priority: 20
	};

	return writer.createAttributeElement( 'span', attributes, options );
}

// Model post-fixer that disallows typing with selection when the selection is placed after the text node with the mention attribute or
// before a text node with mention attribute.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/document~Document} doc
// @returns {Boolean} Returns `true` if the selection was fixed.
function selectionMentionAttributePostFixer( writer, doc ) {
	const selection = doc.selection;
	const focus = selection.focus;

	if ( selection.isCollapsed && selection.hasAttribute( 'mention' ) && shouldNotTypeWithMentionAt( focus ) ) {
		writer.removeSelectionAttribute( 'mention' );

		return true;
	}
}

// Helper function to detect if mention attribute should be removed from selection.
// This check makes only sense if the selection has mention attribute.
//
// The mention attribute should be removed from a selection when selection focus is placed:
// a) after a text node
// b) the position is at parents start - the selection will set attributes from node after.
function shouldNotTypeWithMentionAt( position ) {
	const isAtStart = position.isAtStart;
	const isAfterAMention = position.nodeBefore && position.nodeBefore.is( '$text' );

	return isAfterAMention || isAtStart;
}

// Model post-fixer that removes the mention attribute from the modified text node.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/document~Document} doc
// @returns {Boolean} Returns `true` if the selection was fixed.
function removePartialMentionPostFixer( writer, doc, schema ) {
	const changes = doc.differ.getChanges();

	let wasChanged = false;

	for ( const change of changes ) {
		// Checks the text node on the current position.
		const position = change.position;

		if ( change.name == '$text' ) {
			const nodeAfterInsertedTextNode = position.textNode && position.textNode.nextSibling;

			// Checks the text node where the change occurred.
			wasChanged = checkAndFix( position.textNode, writer ) || wasChanged;

			// Occurs on paste inside a text node with mention.
			wasChanged = checkAndFix( nodeAfterInsertedTextNode, writer ) || wasChanged;
			wasChanged = checkAndFix( position.nodeBefore, writer ) || wasChanged;
			wasChanged = checkAndFix( position.nodeAfter, writer ) || wasChanged;
		}

		// Checks text nodes in inserted elements (might occur when splitting a paragraph or pasting content inside text with mention).
		if ( change.name != '$text' && change.type == 'insert' ) {
			const insertedNode = position.nodeAfter;

			for ( const item of writer.createRangeIn( insertedNode ).getItems() ) {
				wasChanged = checkAndFix( item, writer ) || wasChanged;
			}
		}

		// Inserted inline elements might break mention.
		if ( change.type == 'insert' && schema.isInline( change.name ) ) {
			const nodeAfterInserted = position.nodeAfter && position.nodeAfter.nextSibling;

			wasChanged = checkAndFix( position.nodeBefore, writer ) || wasChanged;
			wasChanged = checkAndFix( nodeAfterInserted, writer ) || wasChanged;
		}
	}

	return wasChanged;
}

// This post-fixer will extend the attribute applied on the part of the mention so the whole text node of the mention will have
// the added attribute.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/document~Document} doc
// @returns {Boolean} Returns `true` if the selection was fixed.
function extendAttributeOnMentionPostFixer( writer, doc ) {
	const changes = doc.differ.getChanges();

	let wasChanged = false;

	for ( const change of changes ) {
		if ( change.type === 'attribute' && change.attributeKey != 'mention' ) {
			// Checks the node on the left side of the range...
			const nodeBefore = change.range.start.nodeBefore;
			// ... and on the right side of the range.
			const nodeAfter = change.range.end.nodeAfter;

			for ( const node of [ nodeBefore, nodeAfter ] ) {
				if ( isBrokenMentionNode( node ) && node.getAttribute( change.attributeKey ) != change.attributeNewValue ) {
					writer.setAttribute( change.attributeKey, change.attributeNewValue, node );

					wasChanged = true;
				}
			}
		}
	}

	return wasChanged;
}

// Checks if a node has a correct mention attribute if present.
// Returns `true` if the node is text and has a mention attribute whose text does not match the expected mention text.
//
// @param {module:engine/model/node~Node} node The node to check.
// @returns {Boolean}
function isBrokenMentionNode( node ) {
	if ( !node || !( node.is( '$text' ) || node.is( '$textProxy' ) ) || !node.hasAttribute( 'mention' ) ) {
		return false;
	}

	const text = node.data;
	const mention = node.getAttribute( 'mention' );

	const expectedText = mention._text;

	return text != expectedText;
}

// Fixes a mention on a text node if it needs a fix.
//
// @param {module:engine/model/text~Text} textNode
// @param {module:engine/model/writer~Writer} writer
// @returns {Boolean}
function checkAndFix( textNode, writer ) {
	if ( isBrokenMentionNode( textNode ) ) {
		writer.removeAttribute( 'mention', textNode );

		return true;
	}

	return false;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-mention/theme/mentionui.css
var mentionui = __webpack_require__(6391);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/theme/mentionui.css

            

var mentionui_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

mentionui_options.insert = "head";
mentionui_options.singleton = true;

var mentionui_update = injectStylesIntoStyleTag_default()(mentionui/* default */.Z, mentionui_options);



/* harmony default export */ const theme_mentionui = (mentionui/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/src/ui/mentionsview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module mention/ui/mentionsview
 */






/**
 * The mention ui view.
 *
 * @extends module:ui/list/listview~ListView
 */
class MentionsView extends ListView {
	/**
	 * @inheritDoc
	 */
	constructor( locale ) {
		super( locale );

		this.extendTemplate( {
			attributes: {
				class: [
					'ck-mentions'
				],

				tabindex: '-1'
			}
		} );
	}

	/**
	 * {@link #select Selects} the first item.
	 */
	selectFirst() {
		this.select( 0 );
	}

	/**
	 * Selects next item to the currently {@link #select selected}.
	 *
	 * If the last item is already selected, it will select the first item.
	 */
	selectNext() {
		const item = this.selected;
		const index = this.items.getIndex( item );

		this.select( index + 1 );
	}

	/**
	 * Selects previous item to the currently {@link #select selected}.
	 *
	 * If the first item is already selected, it will select the last item.
	 */
	selectPrevious() {
		const item = this.selected;
		const index = this.items.getIndex( item );

		this.select( index - 1 );
	}

	/**
	 * Marks item at a given index as selected.
	 *
	 * Handles selection cycling when passed index is out of bounds:
	 * - if the index is lower than 0, it will select the last item,
	 * - if the index is higher than the last item index, it will select the first item.
	 *
	 * @param {Number} index Index of an item to be marked as selected.
	 */
	select( index ) {
		let indexToGet = 0;

		if ( index > 0 && index < this.items.length ) {
			indexToGet = index;
		} else if ( index < 0 ) {
			indexToGet = this.items.length - 1;
		}

		const item = this.items.get( indexToGet );

		// Return early if item is already selected.
		if ( this.selected === item ) {
			return;
		}

		// Remove highlight of previously selected item.
		if ( this.selected ) {
			this.selected.removeHighlight();
		}

		item.highlight();
		this.selected = item;

		// Scroll the mentions view to the selected element.
		if ( !this._isItemVisibleInScrolledArea( item ) ) {
			this.element.scrollTop = item.element.offsetTop;
		}
	}

	/**
	 * Triggers the `execute` event on the {@link #select selected} item.
	 */
	executeSelected() {
		this.selected.fire( 'execute' );
	}

	// Checks if an item is visible in the scrollable area.
	//
	// The item is considered visible when:
	// - its top boundary is inside the scrollable rect
	// - its bottom boundary is inside the scrollable rect (the whole item must be visible)
	_isItemVisibleInScrolledArea( item ) {
		return new rect_Rect( this.element ).contains( new rect_Rect( item.element ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/src/ui/domwrapperview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module mention/ui/domwrapperview
 */



/**
 * This class wraps DOM element as a CKEditor5 UI View.
 *
 * It allows to render any DOM element and use it in mentions list.
 *
 * @extends {module:ui/view~View}
 */
class DomWrapperView extends src_view_View {
	/**
	 * Creates an instance of {@link module:mention/ui/domwrapperview~DomWrapperView} class.
	 *
	 * Also see {@link #render}.
	 *
	 * @param {module:utils/locale~Locale} [locale] The localization services instance.
	 * @param {Element} domElement
	 */
	constructor( locale, domElement ) {
		super( locale );

		// Disable template rendering on this view.
		this.template = false;

		/**
		 * The DOM element for which wrapper was created.
		 *
		 * @type {Element}
		 */
		this.domElement = domElement;

		// Render dom wrapper as a button.
		this.domElement.classList.add( 'ck-button' );

		/**
		 * Controls whether the dom wrapper view is "on". This is in line with {@link module:ui/button/button~Button#isOn} property.
		 *
		 * @observable
		 * @default true
		 * @member {Boolean} #isOn
		 */
		this.set( 'isOn', false );

		// Handle isOn state as in buttons.
		this.on( 'change:isOn', ( evt, name, isOn ) => {
			if ( isOn ) {
				this.domElement.classList.add( 'ck-on' );
				this.domElement.classList.remove( 'ck-off' );
			} else {
				this.domElement.classList.add( 'ck-off' );
				this.domElement.classList.remove( 'ck-on' );
			}
		} );

		// Pass click event as execute event.
		this.listenTo( this.domElement, 'click', () => {
			this.fire( 'execute' );
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		this.element = this.domElement;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/src/ui/mentionlistitemview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module mention/ui/mentionlistitemview
 */



class MentionListItemView extends ListItemView {
	highlight() {
		const child = this.children.first;

		child.isOn = true;
	}

	removeHighlight() {
		const child = this.children.first;

		child.isOn = false;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/src/mentionui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module mention/mentionui
 */












const VERTICAL_SPACING = 3;

// The key codes that mention UI handles when it is open (without commit keys).
const defaultHandledKeyCodes = [
	keyCodes.arrowup,
	keyCodes.arrowdown,
	keyCodes.esc
];

// Dropdown commit key codes.
const defaultCommitKeyCodes = [
	keyCodes.enter,
	keyCodes.tab
];

/**
 * The mention UI feature.
 *
 * @extends module:core/plugin~Plugin
 */
class MentionUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'MentionUI';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ContextualBalloon ];
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * The mention view.
		 *
		 * @type {module:mention/ui/mentionsview~MentionsView}
		 * @private
		 */
		this._mentionsView = this._createMentionView();

		/**
		 * Stores mention feeds configurations.
		 *
		 * @type {Map<String, Object>}
		 * @private
		 */
		this._mentionsConfigurations = new Map();

		/**
		 * Debounced feed requester. It uses `lodash#debounce` method to delay function call.
		 *
		 * @private
		 * @param {String} marker
		 * @param {String} feedText
		 * @method
		 */
		this._requestFeedDebounced = lodash_es_debounce( this._requestFeed, 100 );

		editor.config.define( 'mention', { feeds: [] } );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		const commitKeys = editor.config.get( 'mention.commitKeys' ) || defaultCommitKeyCodes;
		const handledKeyCodes = defaultHandledKeyCodes.concat( commitKeys );

		/**
		 * The contextual balloon plugin instance.
		 *
		 * @private
		 * @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
		 */
		this._balloon = editor.plugins.get( ContextualBalloon );

		// Key listener that handles navigation in mention view.
		editor.editing.view.document.on( 'keydown', ( evt, data ) => {
			if ( isHandledKey( data.keyCode ) && this._isUIVisible ) {
				data.preventDefault();
				evt.stop(); // Required for Enter key overriding.

				if ( data.keyCode == keyCodes.arrowdown ) {
					this._mentionsView.selectNext();
				}

				if ( data.keyCode == keyCodes.arrowup ) {
					this._mentionsView.selectPrevious();
				}

				if ( commitKeys.includes( data.keyCode ) ) {
					this._mentionsView.executeSelected();
				}

				if ( data.keyCode == keyCodes.esc ) {
					this._hideUIAndRemoveMarker();
				}
			}
		}, { priority: 'highest' } ); // Required to override the Enter key.

		// Close the dropdown upon clicking outside of the plugin UI.
		clickoutsidehandler_clickOutsideHandler( {
			emitter: this._mentionsView,
			activator: () => this._isUIVisible,
			contextElements: [ this._balloon.view.element ],
			callback: () => this._hideUIAndRemoveMarker()
		} );

		const feeds = editor.config.get( 'mention.feeds' );

		for ( const mentionDescription of feeds ) {
			const feed = mentionDescription.feed;

			const marker = mentionDescription.marker;

			if ( !isValidMentionMarker( marker ) ) {
				/**
				 * The marker must be a single character.
				 *
				 * Correct markers: `'@'`, `'#'`.
				 *
				 * Incorrect markers: `'$$'`, `'[@'`.
				 *
				 * See {@link module:mention/mention~MentionConfig}.
				 *
				 * @error mentionconfig-incorrect-marker
				 * @param {String} marker Configured marker
				 */
				throw new CKEditorError( 'mentionconfig-incorrect-marker', null, { marker } );
			}

			const feedCallback = typeof feed == 'function' ? feed.bind( this.editor ) : createFeedCallback( feed );
			const itemRenderer = mentionDescription.itemRenderer;
			const definition = { marker, feedCallback, itemRenderer };

			this._mentionsConfigurations.set( marker, definition );
		}

		this._setupTextWatcher( feeds );
		this.listenTo( editor, 'change:isReadOnly', () => {
			this._hideUIAndRemoveMarker();
		} );
		this.on( 'requestFeed:response', ( evt, data ) => this._handleFeedResponse( data ) );
		this.on( 'requestFeed:error', () => this._hideUIAndRemoveMarker() );

		// Checks if a given key code is handled by the mention UI.
		//
		// @param {Number}
		// @returns {Boolean}
		function isHandledKey( keyCode ) {
			return handledKeyCodes.includes( keyCode );
		}
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		// Destroy created UI components as they are not automatically destroyed (see ckeditor5#1341).
		this._mentionsView.destroy();
	}

	/**
	 * Returns true when {@link #_mentionsView} is in the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon} and it is
	 * currently visible.
	 *
	 * @readonly
	 * @protected
	 * @type {Boolean}
	 */
	get _isUIVisible() {
		return this._balloon.visibleView === this._mentionsView;
	}

	/**
	 * Creates the {@link #_mentionsView}.
	 *
	 * @private
	 * @returns {module:mention/ui/mentionsview~MentionsView}
	 */
	_createMentionView() {
		const locale = this.editor.locale;

		const mentionsView = new MentionsView( locale );

		this._items = new Collection();

		mentionsView.items.bindTo( this._items ).using( data => {
			const { item, marker } = data;

			// Set to 10 by default for backwards compatibility. See: #10479
			const dropdownLimit = this.editor.config.get( 'mention.dropdownLimit' ) || 10;

			if ( mentionsView.items.length >= dropdownLimit ) {
				return;
			}

			const listItemView = new MentionListItemView( locale );

			const view = this._renderItem( item, marker );
			view.delegate( 'execute' ).to( listItemView );

			listItemView.children.add( view );
			listItemView.item = item;
			listItemView.marker = marker;

			listItemView.on( 'execute', () => {
				mentionsView.fire( 'execute', {
					item,
					marker
				} );
			} );

			return listItemView;
		} );

		mentionsView.on( 'execute', ( evt, data ) => {
			const editor = this.editor;
			const model = editor.model;

			const item = data.item;
			const marker = data.marker;

			const mentionMarker = editor.model.markers.get( 'mention' );

			// Create a range on matched text.
			const end = model.createPositionAt( model.document.selection.focus );
			const start = model.createPositionAt( mentionMarker.getStart() );
			const range = model.createRange( start, end );

			this._hideUIAndRemoveMarker();

			editor.execute( 'mention', {
				mention: item,
				text: item.text,
				marker,
				range
			} );

			editor.editing.view.focus();
		} );

		return mentionsView;
	}

	/**
	 * Returns item renderer for the marker.
	 *
	 * @private
	 * @param {String} marker
	 * @returns {Function|null}
	 */
	_getItemRenderer( marker ) {
		const { itemRenderer } = this._mentionsConfigurations.get( marker );

		return itemRenderer;
	}

	/**
	 * Requests a feed from a configured callbacks.
	 *
	 * @private
	 * @fires module:mention/mentionui~MentionUI#event:requestFeed:response
	 * @fires module:mention/mentionui~MentionUI#event:requestFeed:discarded
	 * @fires module:mention/mentionui~MentionUI#event:requestFeed:error
	 * @param {String} marker
	 * @param {String} feedText
	 */
	_requestFeed( marker, feedText ) {
		// @if CK_DEBUG_MENTION // console.log( '%c[Feed]%c Requesting for', 'color: blue', 'color: black', `"${ feedText }"` );

		// Store the last requested feed - it is used to discard any out-of order requests.
		this._lastRequested = feedText;

		const { feedCallback } = this._mentionsConfigurations.get( marker );
		const feedResponse = feedCallback( feedText );

		const isAsynchronous = feedResponse instanceof Promise;

		// For synchronous feeds (e.g. callbacks, arrays) fire the response event immediately.
		if ( !isAsynchronous ) {
			/**
			 * Fired whenever requested feed has a response.
			 *
			 * @event requestFeed:response
			 * @param {Object} data Event data.
			 * @param {Array.<module:mention/mention~MentionFeedItem>} data.feed Autocomplete items.
			 * @param {String} data.marker The character which triggers autocompletion for mention.
			 * @param {String} data.feedText The text for which feed items were requested.
			 */
			this.fire( 'requestFeed:response', { feed: feedResponse, marker, feedText } );

			return;
		}

		// Handle the asynchronous responses.
		feedResponse
			.then( response => {
				// Check the feed text of this response with the last requested one so either:
				if ( this._lastRequested == feedText ) {
					// It is the same and fire the response event.
					this.fire( 'requestFeed:response', { feed: response, marker, feedText } );
				} else {
					// It is different - most probably out-of-order one, so fire the discarded event.
					/**
					 * Fired whenever the requested feed was discarded. This happens when the response was delayed and
					 * other feed was already requested.
					 *
					 * @event requestFeed:discarded
					 * @param {Object} data Event data.
					 * @param {Array.<module:mention/mention~MentionFeedItem>} data.feed Autocomplete items.
					 * @param {String} data.marker The character which triggers autocompletion for mention.
					 * @param {String} data.feedText The text for which feed items were requested.
					 */
					this.fire( 'requestFeed:discarded', { feed: response, marker, feedText } );
				}
			} )
			.catch( error => {
				/**
				 * Fired whenever the requested {@link module:mention/mention~MentionFeed#feed} promise fails with error.
				 *
				 * @event requestFeed:error
				 * @param {Object} data Event data.
				 * @param {Error} data.error The error that was caught.
				 */
				this.fire( 'requestFeed:error', { error } );

				/**
				 * The callback used for obtaining mention autocomplete feed thrown and error and the mention UI was hidden or
				 * not displayed at all.
				 *
				 * @error mention-feed-callback-error
				 * @param {String} marker Configured marker for which error occurred.
				 */
				logWarning( 'mention-feed-callback-error', { marker } );
			} );
	}

	/**
	 * Registers a text watcher for the marker.
	 *
	 * @private
	 * @param {Array.<Object>} feeds Feeds of mention plugin configured in editor
	 * @returns {module:typing/textwatcher~TextWatcher}
	 */
	_setupTextWatcher( feeds ) {
		const editor = this.editor;

		const feedsWithPattern = feeds.map( feed => ( {
			...feed,
			pattern: createRegExp( feed.marker, feed.minimumCharacters || 0 )
		} ) );

		const watcher = new TextWatcher( editor.model, createTestCallback( feedsWithPattern ) );

		watcher.on( 'matched', ( evt, data ) => {
			const markerDefinition = getLastValidMarkerInText( feedsWithPattern, data.text );
			const selection = editor.model.document.selection;
			const focus = selection.focus;
			const markerPosition = editor.model.createPositionAt( focus.parent, markerDefinition.position );

			if ( isPositionInExistingMention( focus ) || isMarkerInExistingMention( markerPosition ) ) {
				this._hideUIAndRemoveMarker();

				return;
			}

			const feedText = requestFeedText( markerDefinition, data.text );
			const matchedTextLength = markerDefinition.marker.length + feedText.length;

			// Create a marker range.
			const start = focus.getShiftedBy( -matchedTextLength );
			const end = focus.getShiftedBy( -feedText.length );

			const markerRange = editor.model.createRange( start, end );

			// @if CK_DEBUG_MENTION // console.group( '%c[TextWatcher]%c matched', 'color: red', 'color: black', `"${ feedText }"` );
			// @if CK_DEBUG_MENTION // console.log( 'data#text', `"${ data.text }"` );
			// @if CK_DEBUG_MENTION // console.log( 'data#range', data.range.start.path, data.range.end.path );
			// @if CK_DEBUG_MENTION // console.log( 'marker definition', markerDefinition );
			// @if CK_DEBUG_MENTION // console.log( 'marker range', markerRange.start.path, markerRange.end.path );

			if ( checkIfStillInCompletionMode( editor ) ) {
				const mentionMarker = editor.model.markers.get( 'mention' );

				// Update the marker - user might've moved the selection to other mention trigger.
				editor.model.change( writer => {
					// @if CK_DEBUG_MENTION // console.log( '%c[Editing]%c Updating the marker.', 'color: purple', 'color: black' );

					writer.updateMarker( mentionMarker, { range: markerRange } );
				} );
			} else {
				editor.model.change( writer => {
					// @if CK_DEBUG_MENTION // console.log( '%c[Editing]%c Adding the marker.', 'color: purple', 'color: black' );

					writer.addMarker( 'mention', { range: markerRange, usingOperation: false, affectsData: false } );
				} );
			}

			this._requestFeedDebounced( markerDefinition.marker, feedText );

			// @if CK_DEBUG_MENTION // console.groupEnd( '[TextWatcher] matched' );
		} );

		watcher.on( 'unmatched', () => {
			this._hideUIAndRemoveMarker();
		} );

		const mentionCommand = editor.commands.get( 'mention' );
		watcher.bind( 'isEnabled' ).to( mentionCommand );

		return watcher;
	}

	/**
	 * Handles the feed response event data.
	 *
	 * @param data
	 * @private
	 */
	_handleFeedResponse( data ) {
		const { feed, marker } = data;

		// eslint-disable-next-line max-len
		// @if CK_DEBUG_MENTION // console.log( `%c[Feed]%c Response for "${ data.feedText }" (${ feed.length })`, 'color: blue', 'color: black', feed );

		// If the marker is not in the document happens when the selection had changed and the 'mention' marker was removed.
		if ( !checkIfStillInCompletionMode( this.editor ) ) {
			return;
		}

		// Reset the view.
		this._items.clear();

		for ( const feedItem of feed ) {
			const item = typeof feedItem != 'object' ? { id: feedItem, text: feedItem } : feedItem;

			this._items.add( { item, marker } );
		}

		const mentionMarker = this.editor.model.markers.get( 'mention' );

		if ( this._items.length ) {
			this._showOrUpdateUI( mentionMarker );
		} else {
			// Do not show empty mention UI.
			this._hideUIAndRemoveMarker();
		}
	}

	/**
	 * Shows the mentions balloon. If the panel is already visible, it will reposition it.
	 *
	 * @private
	 */
	_showOrUpdateUI( markerMarker ) {
		if ( this._isUIVisible ) {
			// @if CK_DEBUG_MENTION // console.log( '%c[UI]%c Updating position.', 'color: green', 'color: black' );

			// Update balloon position as the mention list view may change its size.
			this._balloon.updatePosition( this._getBalloonPanelPositionData( markerMarker, this._mentionsView.position ) );
		} else {
			// @if CK_DEBUG_MENTION // console.log( '%c[UI]%c Showing the UI.', 'color: green', 'color: black' );

			this._balloon.add( {
				view: this._mentionsView,
				position: this._getBalloonPanelPositionData( markerMarker, this._mentionsView.position ),
				singleViewMode: true
			} );
		}

		this._mentionsView.position = this._balloon.view.position;
		this._mentionsView.selectFirst();
	}

	/**
	 * Hides the mentions balloon and removes the 'mention' marker from the markers collection.
	 *
	 * @private
	 */
	_hideUIAndRemoveMarker() {
		// Remove the mention view from balloon before removing marker - it is used by balloon position target().
		if ( this._balloon.hasView( this._mentionsView ) ) {
			// @if CK_DEBUG_MENTION // console.log( '%c[UI]%c Hiding the UI.', 'color: green', 'color: black' );

			this._balloon.remove( this._mentionsView );
		}

		if ( checkIfStillInCompletionMode( this.editor ) ) {
			// @if CK_DEBUG_MENTION // console.log( '%c[Editing]%c Removing marker.', 'color: purple', 'color: black' );

			this.editor.model.change( writer => writer.removeMarker( 'mention' ) );
		}

		// Make the last matched position on panel view undefined so the #_getBalloonPanelPositionData() method will return all positions
		// on the next call.
		this._mentionsView.position = undefined;
	}

	/**
	 * Renders a single item in the autocomplete list.
	 *
	 * @private
	 * @param {module:mention/mention~MentionFeedItem} item
	 * @param {String} marker
	 * @returns {module:ui/button/buttonview~ButtonView|module:mention/ui/domwrapperview~DomWrapperView}
	 */
	_renderItem( item, marker ) {
		const editor = this.editor;

		let view;
		let label = item.id;

		const renderer = this._getItemRenderer( marker );

		if ( renderer ) {
			const renderResult = renderer( item );

			if ( typeof renderResult != 'string' ) {
				view = new DomWrapperView( editor.locale, renderResult );
			} else {
				label = renderResult;
			}
		}

		if ( !view ) {
			const buttonView = new buttonview_ButtonView( editor.locale );

			buttonView.label = label;
			buttonView.withText = true;

			view = buttonView;
		}

		return view;
	}

	/**
	 * Creates a position options object used to position the balloon panel.
	 *
	 * @param {module:engine/model/markercollection~Marker} mentionMarker
	 * @param {String|undefined} preferredPosition The name of the last matched position name.
	 * @returns {module:utils/dom/position~Options}
	 * @private
	 */
	_getBalloonPanelPositionData( mentionMarker, preferredPosition ) {
		const editor = this.editor;
		const editing = editor.editing;
		const domConverter = editing.view.domConverter;
		const mapper = editing.mapper;

		return {
			target: () => {
				let modelRange = mentionMarker.getRange();

				// Target the UI to the model selection range - the marker has been removed so probably the UI will not be shown anyway.
				// The logic is used by ContextualBalloon to display another panel in the same place.
				if ( modelRange.start.root.rootName == '$graveyard' ) {
					modelRange = editor.model.document.selection.getFirstRange();
				}

				const viewRange = mapper.toViewRange( modelRange );
				const rangeRects = rect_Rect.getDomRangeRects( domConverter.viewRangeToDom( viewRange ) );

				return rangeRects.pop();
			},
			limiter: () => {
				const view = this.editor.editing.view;
				const viewDocument = view.document;
				const editableElement = viewDocument.selection.editableElement;

				if ( editableElement ) {
					return view.domConverter.mapViewToDom( editableElement.root );
				}

				return null;
			},
			positions: getBalloonPanelPositions( preferredPosition )
		};
	}
}

// Returns the balloon positions data callbacks.
//
// @param {String} preferredPosition
// @returns {Array.<module:utils/dom/position~Position>}
function getBalloonPanelPositions( preferredPosition ) {
	const positions = {
		// Positions the panel to the southeast of the caret rectangle.
		'caret_se': targetRect => {
			return {
				top: targetRect.bottom + VERTICAL_SPACING,
				left: targetRect.right,
				name: 'caret_se',
				config: {
					withArrow: false
				}
			};
		},

		// Positions the panel to the northeast of the caret rectangle.
		'caret_ne': ( targetRect, balloonRect ) => {
			return {
				top: targetRect.top - balloonRect.height - VERTICAL_SPACING,
				left: targetRect.right,
				name: 'caret_ne',
				config: {
					withArrow: false
				}
			};
		},

		// Positions the panel to the southwest of the caret rectangle.
		'caret_sw': ( targetRect, balloonRect ) => {
			return {
				top: targetRect.bottom + VERTICAL_SPACING,
				left: targetRect.right - balloonRect.width,
				name: 'caret_sw',
				config: {
					withArrow: false
				}
			};
		},

		// Positions the panel to the northwest of the caret rect.
		'caret_nw': ( targetRect, balloonRect ) => {
			return {
				top: targetRect.top - balloonRect.height - VERTICAL_SPACING,
				left: targetRect.right - balloonRect.width,
				name: 'caret_nw',
				config: {
					withArrow: false
				}
			};
		}
	};

	// Returns only the last position if it was matched to prevent the panel from jumping after the first match.
	if ( Object.prototype.hasOwnProperty.call( positions, preferredPosition ) ) {
		return [
			positions[ preferredPosition ]
		];
	}

	// By default return all position callbacks.
	return [
		positions.caret_se,
		positions.caret_sw,
		positions.caret_ne,
		positions.caret_nw
	];
}

// Returns a marker definition of the last valid occurring marker in a given string.
// If there is no valid marker in a string, it returns undefined.
//
// Example of returned object:
//
//		{
//			marker: '@',
//			position: 4,
//			minimumCharacters: 0
//		}
//
// @param {Array.<Object>} feedsWithPattern Registered feeds in editor for mention plugin with created RegExp for matching marker.
// @param {String} text String to find the marker in
// @returns {Object} Matched marker's definition
function getLastValidMarkerInText( feedsWithPattern, text ) {
	let lastValidMarker;

	for ( const feed of feedsWithPattern ) {
		const currentMarkerLastIndex = text.lastIndexOf( feed.marker );

		if ( currentMarkerLastIndex > 0 && !text.substring( currentMarkerLastIndex - 1 ).match( feed.pattern ) ) {
			continue;
		}

		if ( !lastValidMarker || currentMarkerLastIndex >= lastValidMarker.position ) {
			lastValidMarker = {
				marker: feed.marker,
				position: currentMarkerLastIndex,
				minimumCharacters: feed.minimumCharacters,
				pattern: feed.pattern
			};
		}
	}

	return lastValidMarker;
}

// Creates a RegExp pattern for the marker.
//
// Function has to be exported to achieve 100% code coverage.
//
// @param {String} marker
// @param {Number} minimumCharacters
// @returns {RegExp}
function createRegExp( marker, minimumCharacters ) {
	const numberOfCharacters = minimumCharacters == 0 ? '*' : `{${ minimumCharacters },}`;

	const openAfterCharacters = src_env.features.isRegExpUnicodePropertySupported ? '\\p{Ps}\\p{Pi}"\'' : '\\(\\[{"\'';
	const mentionCharacters = '.';

	// The pattern consists of 3 groups:
	// - 0 (non-capturing): Opening sequence - start of the line, space or an opening punctuation character like "(" or "\"",
	// - 1: The marker character,
	// - 2: Mention input (taking the minimal length into consideration to trigger the UI),
	//
	// The pattern matches up to the caret (end of string switch - $).
	//               (0:      opening sequence       )(1:   marker  )(2:                typed mention              )$
	const pattern = `(?:^|[ ${ openAfterCharacters }])([${ marker }])(${ mentionCharacters }${ numberOfCharacters })$`;
	return new RegExp( pattern, 'u' );
}

// Creates a test callback for the marker to be used in the text watcher instance.
//
// @param {Array.<Object>} feedsWithPattern Feeds of mention plugin configured in editor with RegExp to match marker in text
// @returns {Function}
function createTestCallback( feedsWithPattern ) {
	const textMatcher = text => {
		const markerDefinition = getLastValidMarkerInText( feedsWithPattern, text );

		if ( !markerDefinition ) {
			return false;
		}

		let splitStringFrom = 0;

		if ( markerDefinition.position !== 0 ) {
			splitStringFrom = markerDefinition.position - 1;
		}

		const textToTest = text.substring( splitStringFrom );

		return markerDefinition.pattern.test( textToTest );
	};

	return textMatcher;
}

// Creates a text matcher from the marker.
//
// @param {Object} markerDefinition
// @param {String} text
// @returns {Function}
function requestFeedText( markerDefinition, text ) {
	let splitStringFrom = 0;

	if ( markerDefinition.position !== 0 ) {
		splitStringFrom = markerDefinition.position - 1;
	}

	const regExp = createRegExp( markerDefinition.marker, 0 );
	const textToMatch = text.substring( splitStringFrom );
	const match = textToMatch.match( regExp );

	return match[ 2 ];
}

// The default feed callback.
function createFeedCallback( feedItems ) {
	return feedText => {
		const filteredItems = feedItems
		// Make the default mention feed case-insensitive.
			.filter( item => {
				// Item might be defined as object.
				const itemId = typeof item == 'string' ? item : String( item.id );

				// The default feed is case insensitive.
				return itemId.toLowerCase().includes( feedText.toLowerCase() );
			} );
		return filteredItems;
	};
}

// Checks if position in inside or right after a text with a mention.
//
// @param {module:engine/model/position~Position} position.
// @returns {Boolean}
function isPositionInExistingMention( position ) {
	// The text watcher listens only to changed range in selection - so the selection attributes are not yet available
	// and you cannot use selection.hasAttribute( 'mention' ) just yet.
	// See https://github.com/ckeditor/ckeditor5-engine/issues/1723.
	const hasMention = position.textNode && position.textNode.hasAttribute( 'mention' );

	const nodeBefore = position.nodeBefore;

	return hasMention || nodeBefore && nodeBefore.is( '$text' ) && nodeBefore.hasAttribute( 'mention' );
}

// Checks if the closest marker offset is at the beginning of a mention.
//
// See https://github.com/ckeditor/ckeditor5/issues/11400.
//
// @param {module:engine/model/position~Position} markerPosition
// @returns {Boolean}
function isMarkerInExistingMention( markerPosition ) {
	const nodeAfter = markerPosition.nodeAfter;

	return nodeAfter && nodeAfter.is( '$text' ) && nodeAfter.hasAttribute( 'mention' );
}

// Checks if string is a valid mention marker.
//
// @param {String} marker
// @returns {Boolean}
function isValidMentionMarker( marker ) {
	return marker && marker.length == 1;
}

// Checks the mention plugins is in completion mode (e.g. when typing is after a valid mention string like @foo).
//
// @returns {Boolean}
function checkIfStillInCompletionMode( editor ) {
	return editor.model.markers.has( 'mention' );
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-mention/theme/mention.css
var mention = __webpack_require__(7583);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/theme/mention.css

            

var mention_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

mention_options.insert = "head";
mention_options.singleton = true;

var mention_update = injectStylesIntoStyleTag_default()(mention/* default */.Z, mention_options);



/* harmony default export */ const theme_mention = (mention/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-mention/src/mention.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module mention/mention
 */








/**
 * The mention plugin.
 *
 * For a detailed overview, check the {@glink features/mentions Mention feature documentation}.
 *
 * @extends module:core/plugin~Plugin
 */
class Mention extends plugin_Plugin {
	/**
	 * Creates a mention attribute value from the provided view element and optional data.
	 *
	 *		editor.plugins.get( 'Mention' ).toMentionAttribute( viewElement, { userId: '1234' } );
	 *
	 *		// For a view element: <span data-mention="@joe">@John Doe</span>
	 *		// it will return:
	 *		// { id: '@joe', userId: '1234', uid: '7a7bc7...', _text: '@John Doe' }
	 *
	 * @param {module:engine/view/element~Element} viewElement
	 * @param {String|Object} [data] Additional data to be stored in the mention attribute.
	 * @returns {module:mention/mention~MentionAttribute}
	 */
	toMentionAttribute( viewElement, data ) {
		return _toMentionAttribute( viewElement, data );
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Mention';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ MentionEditing, MentionUI ];
	}
}

/**
 * The configuration of the {@link module:mention/mention~Mention} feature.
 *
 * Read more in {@link module:mention/mention~MentionConfig}.
 *
 * @member {module:mention/mention~MentionConfig} module:core/editor/editorconfig~EditorConfig#mention
 * @type {Array.<module/mention~MentionFeed>}
 */

/**
 * The configuration of the mention feature.
 *
 * Read more about {@glink features/mentions#configuration configuring the mention feature}.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				mention: ... // Mention feature options.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface MentionConfig
 */

/**
 * The list of mention feeds supported by the editor.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				plugins: [ Mention, ... ],
 *				mention: {
 *					feeds: [
 *						{
 *							marker: '@',
 *							feed: [ '@Barney', '@Lily', '@Marshall', '@Robin', '@Ted' ]
 *						},
 *						...
 * 					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * You can provide many mention feeds but they must use different `marker`s.
 * For example, you can use `'@'` to autocomplete people and `'#'` to autocomplete tags.
 *
 * @member {Array.<module:mention/mention~MentionFeed>} module:mention/mention~MentionConfig#feeds
 */

/**
 * The configuration of the custom commit keys supported by the editor.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				plugins: [ Mention, ... ],
 *				mention: {
 *					// [ Enter, Space ]
 *	 				commitKeys: [ 13, 32 ]
 *					feeds: [
 *						{ ... }
 *						...
 * 					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * Custom commit keys configuration allows you to customize how users will confirm the selection of mentions from the dropdown list.
 * You can add as many mention commit keys as you need. For instance, in the snippet above new mentions will be committed by pressing
 * either <kbd>Enter</kbd> or <kbd>Space</kbd> (13 and 32 key codes respectively).
 *
 * @member {Array.<Number>} module:mention/mention~MentionConfig#commitKeys
 * @default [ 13, 9 ] // [ Enter, Tab ]
 */

/**
 * The configuration of the custom number of visible mentions.
 *
 * Customizing the number of visible mentions allows you to specify how many available elements will the users be able to see
 * in the dropdown list. You can specify any number you see fit. For example, in the snippets below you will find the
 * dropdownLimit set to `20` and `Infinity` (this will result in showing all available mentions).
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				plugins: [ Mention, ... ],
 *				mention: {
 *	 				dropdownLimit: 20,
 *					feeds: [
 *						{ ... }
 *						...
 * 					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				plugins: [ Mention, ... ],
 *				mention: {
 *	 				dropdownLimit: Infinity,
 *					feeds: [
 *						{ ... }
 *						...
 * 					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @member {Number} module:mention/mention~MentionConfig#dropdownLimit
 * @default 10
 */

/**
 * The mention feed descriptor. Used in {@link module:mention/mention~MentionConfig `config.mention`}.
 *
 * See {@link module:mention/mention~MentionConfig} to learn more.
 *
 *		// Static configuration.
 *		const mentionFeedPeople = {
 *			marker: '@',
 *			feed: [ '@Alice', '@Bob', ... ],
 *			minimumCharacters: 2
 *		};
 *
 *		// Simple synchronous callback.
 *		const mentionFeedTags = {
 *			marker: '#',
 *			feed: searchString => {
 *				return tags
 *					// Filter the tags list.
 *					.filter( tag => {
 *						return tag.toLowerCase().includes( queryText.toLowerCase() );
 *					} )
 *			}
 * 		};
 *
 *		const tags = [ 'wysiwyg', 'rte', 'rich-text-edior', 'collaboration', 'real-time', ... ];
 *
 *		// Asynchronous callback.
 *		const mentionFeedPlaceholders = {
 *			marker: '$',
 *			feed: searchString => {
 *				return getMatchingPlaceholders( searchString );
 *			}
 * 		};
 *
 *		function getMatchingPlaceholders( searchString ) {
 *			return new Promise( resolve => {
 *				doSomeXHRQuery( result => {
 *					// console.log( result );
 *					// -> [ '$name', '$surname', '$postal', ... ]
 *
 *					resolve( result );
 * 				} );
 *			} );
 *		}
 *
 * @typedef {Object} module:mention/mention~MentionFeed
 * @property {String} [marker] The character which triggers autocompletion for mention. It must be a single character.
 * @property {Array.<module:mention/mention~MentionFeedItem>|Function} feed Autocomplete items. Provide an array for
 * a static configuration (the mention feature will show matching items automatically) or a function which returns an array of
 * matching items (directly, or via a promise). If a function is passed, it is executed in the context of the editor instance.
 * @property {Number} [minimumCharacters=0] Specifies after how many characters the autocomplete panel should be shown.
 * @property {Function} [itemRenderer] A function that renders a {@link module:mention/mention~MentionFeedItem}
 * to the autocomplete panel.
 */

/**
 * The mention feed item. It may be defined as a string or a plain object.
 *
 * When defining a feed item as a plain object, the `id` property is obligatory. Additional properties
 * can be used when customizing the mention feature bahavior
 * (see {@glink features/mentions#customizing-the-autocomplete-list "Customizing the autocomplete list"}
 * and {@glink features/mentions#customizing-the-output "Customizing the output"} sections).
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				plugins: [ Mention, ... ],
 *				mention: {
 *					feeds: [
 *						// Feed items as objects.
 *						{
 *							marker: '@',
 *							feed: [
 *								{
 *									id: '@Barney',
 *									fullName: 'Barney Bloom'
 *								},
 *								{
 *									id: '@Lily',
 *									fullName: 'Lily Smith'
 *								},
 *								{
 *									id: '@Marshall',
 *									fullName: 'Marshall McDonald'
 *								},
 *								{
 *									id: '@Robin',
 *									fullName: 'Robin Hood'
 *								},
 *								{
 *									id: '@Ted',
 *									fullName: 'Ted Cruze'
 *								},
 *								// ...
 *							]
 *						},
 *
 *						// Feed items as plain strings.
 *						{
 *							marker: '#',
 *							feed: [ 'wysiwyg', 'rte', 'rich-text-edior', 'collaboration', 'real-time', ... ]
 *						},
 * 					]
 *				}
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @typedef {Object|String} module:mention/mention~MentionFeedItem
 * @property {String} id A unique ID of the mention. It must start with the marker character.
 * @property {String} [text] Text inserted into the editor when creating a mention.
 */

/**
 * Represents a mention in the model.
 *
 * See {@link module:mention/mention~Mention#toMentionAttribute `Mention#toMentionAttribute()`}.
 *
 * @interface module:mention/mention~MentionAttribute
 * @property {String} id The ID of a mention. It identifies the mention item in the mention feed. There can be multiple mentions
 * in the document with the same ID (e.g. the same hashtag being mentioned).
 * @property {String} uid A unique ID of this mention instance. Should be passed as an `option.id` when using
 * {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement writer.createAttributeElement()}.
 * @property {String} _text Helper property that stores the text of the inserted mention. Used for detecting a broken mention
 * in the editing area.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-page-break/src/pagebreakcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module page-break/pagebreakcommand
 */




/**
 * The page break command.
 *
 * The command is registered by {@link module:page-break/pagebreakediting~PageBreakEditing} as `'pageBreak'`.
 *
 * To insert a page break at the current selection, execute the command:
 *
 *		editor.execute( 'pageBreak' );
 *
 * @extends module:core/command~Command
 */
class PageBreakCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const schema = model.schema;
		const selection = model.document.selection;

		this.isEnabled = isPageBreakAllowedInParent( selection, schema, model );
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 */
	execute() {
		const model = this.editor.model;

		model.change( writer => {
			const pageBreakElement = writer.createElement( 'pageBreak' );

			model.insertObject( pageBreakElement, null, null, {
				setSelection: 'after'
			} );
		} );
	}
}

// Checks if a page break is allowed by the schema in the optimal insertion parent.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/model/model~Model} model Model instance.
// @returns {Boolean}
function isPageBreakAllowedInParent( selection, schema, model ) {
	const parent = getInsertPageBreakParent( selection, model );

	return schema.checkChild( parent, 'pageBreak' );
}

// Returns a node that will be used to insert a page break with `model.insertContent` to check if the page break can be placed there.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/model~Model} model Model instance.
// @returns {module:engine/model/element~Element}
function getInsertPageBreakParent( selection, model ) {
	const insertionRange = utils_findOptimalInsertionRange( selection, model );
	const parent = insertionRange.start.parent;

	if ( parent.isEmpty && !parent.is( 'element', '$root' ) ) {
		return parent.parent;
	}

	return parent;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-page-break/theme/pagebreak.css
var pagebreak = __webpack_require__(6448);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-page-break/theme/pagebreak.css

            

var pagebreak_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

pagebreak_options.insert = "head";
pagebreak_options.singleton = true;

var pagebreak_update = injectStylesIntoStyleTag_default()(pagebreak/* default */.Z, pagebreak_options);



/* harmony default export */ const theme_pagebreak = (pagebreak/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-page-break/src/pagebreakediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module page-break/pagebreakediting
 */








/**
 * The page break editing feature.
 *
 * @extends module:core/plugin~Plugin
 */
class PageBreakEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'PageBreakEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;
		const t = editor.t;
		const conversion = editor.conversion;

		schema.register( 'pageBreak', {
			inheritAllFrom: '$blockObject'
		} );

		conversion.for( 'dataDowncast' ).elementToStructure( {
			model: 'pageBreak',
			view: ( modelElement, { writer } ) => {
				const divElement = writer.createContainerElement( 'div',
					{
						class: 'page-break',
						// If user has no `.ck-content` styles, it should always break a page during print.
						style: 'page-break-after: always'
					},
					// For a rationale of using span inside a div see:
					// https://github.com/ckeditor/ckeditor5-page-break/pull/1#discussion_r328934062.
					writer.createContainerElement( 'span', {
						style: 'display: none'
					} )
				);

				return divElement;
			}
		} );

		conversion.for( 'editingDowncast' ).elementToStructure( {
			model: 'pageBreak',
			view: ( modelElement, { writer } ) => {
				const label = t( 'Page break' );
				const viewWrapper = writer.createContainerElement( 'div' );
				const viewLabelElement = writer.createRawElement(
					'span',
					{ class: 'page-break__label' },
					function( domElement ) {
						domElement.innerText = t( 'Page break' );
					}
				);

				writer.addClass( 'page-break', viewWrapper );
				writer.insert( writer.createPositionAt( viewWrapper, 0 ), viewLabelElement );

				return toPageBreakWidget( viewWrapper, writer, label );
			}
		} );

		conversion.for( 'upcast' )
			.elementToElement( {
				view: element => {
					// For upcast conversion it's enough if we check for element style and verify if it's empty
					// or contains only hidden span element.

					const hasPageBreakBefore = element.getStyle( 'page-break-before' ) == 'always';
					const hasPageBreakAfter = element.getStyle( 'page-break-after' ) == 'always';

					if ( !hasPageBreakBefore && !hasPageBreakAfter ) {
						return;
					}

					// The "page break" div accepts only single child or no child at all.
					if ( element.childCount == 1 ) {
						const viewSpan = element.getChild( 0 );

						// The child must be the "span" element that is not displayed.
						if ( !viewSpan.is( 'element', 'span' ) || viewSpan.getStyle( 'display' ) != 'none' ) {
							return;
						}
					} else if ( element.childCount > 1 ) {
						return;
					}

					return { name: true };
				},
				model: 'pageBreak',

				// This conversion must be checked before <br> conversion because some editors use
				// <br style="page-break-before:always"> as a page break marker.
				converterPriority: 'high'
			} );

		editor.commands.add( 'pageBreak', new PageBreakCommand( editor ) );
	}
}

// Converts a given {@link module:engine/view/element~Element} to a page break widget:
// * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to
//   recognize the page break widget element.
// * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
//
//  @param {module:engine/view/element~Element} viewElement
//  @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
//  @param {String} label The element's label.
//  @returns {module:engine/view/element~Element}
function toPageBreakWidget( viewElement, writer, label ) {
	writer.setCustomProperty( 'pageBreak', true, viewElement );

	return toWidget( viewElement, writer, { label } );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-page-break/theme/icons/pagebreak.svg
/* harmony default export */ const icons_pagebreak = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3.598.687h1.5v5h-1.5zm14.5 0h1.5v5h-1.5z\"/><path d=\"M19.598 4.187v1.5h-16v-1.5zm-16 14.569h1.5v-5h-1.5zm14.5 0h1.5v-5h-1.5z\"/><path d=\"M19.598 15.256v-1.5h-16v1.5zM5.081 9h6v2h-6zm8 0h6v2h-6zm-9.483 1L0 12.5v-5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-page-break/src/pagebreakui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module page-break/pagebreakui
 */






/**
 * The page break UI plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class PageBreakUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'PageBreakUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add pageBreak button to feature components.
		editor.ui.componentFactory.add( 'pageBreak', locale => {
			const command = editor.commands.get( 'pageBreak' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Page break' ),
				icon: icons_pagebreak,
				tooltip: true
			} );

			view.bind( 'isEnabled' ).to( command, 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( 'pageBreak' );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-page-break/src/pagebreak.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module page-break/pagebreak
 */






/**
 * The page break feature.
 *
 * It provides the possibility to insert a page break into the rich-text editor.
 *
 * For a detailed overview, check the {@glink features/page-break Page break feature} documentation.
 *
 * @extends module:core/plugin~Plugin
 */
class PageBreak extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ PageBreakEditing, PageBreakUI, Widget ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'PageBreak';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/filters/removeboldwrapper.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/filters/removeboldwrapper
 */

/**
 * Removes `<b>` tag wrapper added by Google Docs to a copied content.
 *
 * @param {module:engine/view/documentfragment~DocumentFragment} documentFragment element `data.content` obtained from clipboard
 * @param {module:engine/view/upcastwriter~UpcastWriter} writer
 */
function removeBoldWrapper( documentFragment, writer ) {
	for ( const child of documentFragment.getChildren() ) {
		if ( child.is( 'element', 'b' ) && child.getStyle( 'font-weight' ) === 'normal' ) {
			const childIndex = documentFragment.getChildIndex( child );

			writer.remove( child );
			writer.insertChild( childIndex, child.getChildren(), documentFragment );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/filters/br.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/filters/br
 */



/**
 * Transforms `<br>` elements that are siblings to some block element into a paragraphs.
 *
 * @param {module:engine/view/documentfragment~DocumentFragment} documentFragment The view structure to be transformed.
 * @param {module:engine/view/upcastwriter~UpcastWriter} writer
 */
function transformBlockBrsToParagraphs( documentFragment, writer ) {
	const viewDocument = new Document( writer.document.stylesProcessor );
	const domConverter = new DomConverter( viewDocument, { renderingMode: 'data' } );

	const blockElements = domConverter.blockElements;
	const inlineObjectElements = domConverter.inlineObjectElements;

	const elementsToReplace = [];

	for ( const value of writer.createRangeIn( documentFragment ) ) {
		const element = value.item;

		if ( element.is( 'element', 'br' ) ) {
			const nextSibling = findSibling( element, 'forward', writer, { blockElements, inlineObjectElements } );
			const previousSibling = findSibling( element, 'backward', writer, { blockElements, inlineObjectElements } );

			const nextSiblingIsBlock = isBlockViewElement( nextSibling, blockElements );
			const previousSiblingIsBlock = isBlockViewElement( previousSibling, blockElements );

			// If the <br> is surrounded by blocks then convert it to a paragraph:
			// * <p>foo</p>[<br>]<p>bar</p> -> <p>foo</p>[<p></p>]<p>bar</p>
			// * <p>foo</p>[<br>] -> <p>foo</p>[<p></p>]
			// * [<br>]<p>foo</p> -> [<p></p>]<p>foo</p>
			if ( previousSiblingIsBlock || nextSiblingIsBlock ) {
				elementsToReplace.push( element );
			}
		}
	}

	for ( const element of elementsToReplace ) {
		if ( element.hasClass( 'Apple-interchange-newline' ) ) {
			writer.remove( element );
		} else {
			writer.replace( element, writer.createElement( 'p' ) );
		}
	}
}

// Returns sibling node, threats inline elements as transparent (but should stop on an inline objects).
function findSibling( viewElement, direction, writer, { blockElements, inlineObjectElements } ) {
	let position = writer.createPositionAt( viewElement, direction == 'forward' ? 'after' : 'before' );

	// Find first position that is just before a first:
	// * text node,
	// * block element,
	// * inline object element.
	// It's ignoring any inline (non-object) elements like span, strong, etc.
	position = position.getLastMatchingPosition( ( { item } ) => (
		item.is( 'element' ) &&
		!blockElements.includes( item.name ) &&
		!inlineObjectElements.includes( item.name )
	), { direction } );

	return direction == 'forward' ? position.nodeAfter : position.nodeBefore;
}

// Returns true for view elements that are listed as block view elements.
function isBlockViewElement( node, blockElements ) {
	return !!node && node.is( 'element' ) && blockElements.includes( node.name );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/filters/list.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/filters/list
 */



/**
 * Transforms Word specific list-like elements to the semantic HTML lists.
 *
 * Lists in Word are represented by block elements with special attributes like:
 *
 *		<p class=MsoListParagraphCxSpFirst style='mso-list:l1 level1 lfo1'>...</p> // Paragraph based list.
 *		<h1 style='mso-list:l0 level1 lfo1'>...</h1> // Heading 1 based list.
 *
 * @param {module:engine/view/documentfragment~DocumentFragment} documentFragment The view structure to be transformed.
 * @param {String} stylesString Styles from which list-like elements styling will be extracted.
 */
function transformListItemLikeElementsIntoLists( documentFragment, stylesString ) {
	if ( !documentFragment.childCount ) {
		return;
	}

	const writer = new UpcastWriter( documentFragment.document );
	const itemLikeElements = findAllItemLikeElements( documentFragment, writer );

	if ( !itemLikeElements.length ) {
		return;
	}

	let currentList = null;
	let currentIndentation = 1;

	itemLikeElements.forEach( ( itemLikeElement, i ) => {
		const isDifferentList = isNewListNeeded( itemLikeElements[ i - 1 ], itemLikeElement );
		const previousItemLikeElement = isDifferentList ? null : itemLikeElements[ i - 1 ];
		const indentationDifference = getIndentationDifference( previousItemLikeElement, itemLikeElement );

		if ( isDifferentList ) {
			currentList = null;
			currentIndentation = 1;
		}

		if ( !currentList || indentationDifference !== 0 ) {
			const listStyle = detectListStyle( itemLikeElement, stylesString );

			if ( !currentList ) {
				currentList = insertNewEmptyList( listStyle, itemLikeElement.element, writer );
			} else if ( itemLikeElement.indent > currentIndentation ) {
				const lastListItem = currentList.getChild( currentList.childCount - 1 );
				const lastListItemChild = lastListItem.getChild( lastListItem.childCount - 1 );

				currentList = insertNewEmptyList( listStyle, lastListItemChild, writer );
				currentIndentation += 1;
			} else if ( itemLikeElement.indent < currentIndentation ) {
				const differentIndentation = currentIndentation - itemLikeElement.indent;

				currentList = findParentListAtLevel( currentList, differentIndentation );
				currentIndentation = parseInt( itemLikeElement.indent );
			}

			if ( itemLikeElement.indent <= currentIndentation ) {
				if ( !currentList.is( 'element', listStyle.type ) ) {
					currentList = writer.rename( listStyle.type, currentList );
				}
			}
		}

		const listItem = transformElementIntoListItem( itemLikeElement.element, writer );

		writer.appendChild( listItem, currentList );
	} );
}

/**
 * Removes paragraph wrapping content inside a list item.
 *
 * @param {module:engine/view/documentfragment~DocumentFragment} documentFragment
 * @param {module:engine/view/upcastwriter~UpcastWriter} writer
 */
function unwrapParagraphInListItem( documentFragment, writer ) {
	for ( const value of writer.createRangeIn( documentFragment ) ) {
		const element = value.item;

		if ( element.is( 'element', 'li' ) ) {
			// Google Docs allows for single paragraph inside LI.
			const firstChild = element.getChild( 0 );

			if ( firstChild && firstChild.is( 'element', 'p' ) ) {
				writer.unwrapElement( firstChild );
			}
		}
	}
}

// Finds all list-like elements in a given document fragment.
//
// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment
// in which to look for list-like nodes.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
// @returns {Array.<Object>} Array of found list-like items. Each item is an object containing:
//
//		* {module:engine/src/view/element~Element} element List-like element.
//		* {Number} id List item id parsed from `mso-list` style (see `getListItemData()` function).
//		* {Number} order List item creation order parsed from `mso-list` style (see `getListItemData()` function).
//		* {Number} indent List item indentation level parsed from `mso-list` style (see `getListItemData()` function).
function findAllItemLikeElements( documentFragment, writer ) {
	const range = writer.createRangeIn( documentFragment );

	// Matcher for finding list-like elements.
	const itemLikeElementsMatcher = new Matcher( {
		name: /^p|h\d+$/,
		styles: {
			'mso-list': /.*/
		}
	} );

	const itemLikeElements = [];

	for ( const value of range ) {
		if ( value.type === 'elementStart' && itemLikeElementsMatcher.match( value.item ) ) {
			const itemData = getListItemData( value.item );

			itemLikeElements.push( {
				element: value.item,
				id: itemData.id,
				order: itemData.order,
				indent: itemData.indent
			} );
		}
	}

	return itemLikeElements;
}

// Extracts list item style from the provided CSS.
//
// List item style is extracted from the CSS stylesheet. Each list with its specific style attribute
// value (`mso-list:l1 level1 lfo1`) has its dedicated properties in a CSS stylesheet defined with a selector like:
//
// 		@list l1:level1 { ... }
//
// It contains `mso-level-number-format` property which defines list numbering/bullet style. If this property
// is not defined it means default `decimal` numbering.
//
// Here CSS string representation is used as `mso-level-number-format` property is an invalid CSS property
// and will be removed during CSS parsing.
//
// @param {Object} listLikeItem List-like item for which list style will be searched for. Usually
// a result of `findAllItemLikeElements()` function.
// @param {String} stylesString CSS stylesheet.
// @returns {Object} result
// @returns {String} result.type List type, could be `ul` or `ol`.
// @returns {Number} result.startIndex List start index, valid only for ordered lists.
// @returns {String|null} result.style List style, for example: `decimal`, `lower-roman`, etc. It is extracted
// directly from Word stylesheet and adjusted to represent proper values for the CSS `list-style-type` property.
// If it cannot be adjusted, the `null` value is returned.
function detectListStyle( listLikeItem, stylesString ) {
	const listStyleRegexp = new RegExp( `@list l${ listLikeItem.id }:level${ listLikeItem.indent }\\s*({[^}]*)`, 'gi' );
	const listStyleTypeRegex = /mso-level-number-format:([^;]{0,100});/gi;
	const listStartIndexRegex = /mso-level-start-at:\s{0,100}([0-9]{0,10})\s{0,100};/gi;

	const listStyleMatch = listStyleRegexp.exec( stylesString );

	let listStyleType = 'decimal'; // Decimal is default one.
	let type = 'ol'; // <ol> is default list.
	let startIndex = null;

	if ( listStyleMatch && listStyleMatch[ 1 ] ) {
		const listStyleTypeMatch = listStyleTypeRegex.exec( listStyleMatch[ 1 ] );

		if ( listStyleTypeMatch && listStyleTypeMatch[ 1 ] ) {
			listStyleType = listStyleTypeMatch[ 1 ].trim();
			type = listStyleType !== 'bullet' && listStyleType !== 'image' ? 'ol' : 'ul';
		}

		// Styles for the numbered lists are always defined in the Word CSS stylesheet.
		// Unordered lists MAY contain a value for the Word CSS definition `mso-level-text` but sometimes
		// this tag is missing. And because of that, we cannot depend on that. We need to predict the list style value
		// based on the list style marker element.
		if ( listStyleType === 'bullet' ) {
			const bulletedStyle = findBulletedListStyle( listLikeItem.element );

			if ( bulletedStyle ) {
				listStyleType = bulletedStyle;
			}
		} else {
			const listStartIndexMatch = listStartIndexRegex.exec( listStyleMatch[ 1 ] );

			if ( listStartIndexMatch && listStartIndexMatch[ 1 ] ) {
				startIndex = parseInt( listStartIndexMatch[ 1 ] );
			}
		}
	}

	return {
		type,
		startIndex,
		style: mapListStyleDefinition( listStyleType )
	};
}

// Tries to extract the `list-style-type` value based on the marker element for bulleted list.
//
// @param {module:engine/view/element~Element} element
// @returns {String|null}
function findBulletedListStyle( element ) {
	const listMarkerElement = findListMarkerNode( element );

	if ( !listMarkerElement ) {
		return null;
	}

	const listMarker = listMarkerElement._data;

	if ( listMarker === 'o' ) {
		return 'circle';
	} else if ( listMarker === '·' ) {
		return 'disc';
	}
	// Word returns '§' instead of '■' for the square list style.
	else if ( listMarker === '§' ) {
		return 'square';
	}

	return null;
}

// Tries to find a text node that represents the marker element (list-style-type).
//
// @param {module:engine/view/element~Element} element
// @returns {module:engine/view/text~Text|null}
function findListMarkerNode( element ) {
	// If the first child is a text node, it is the data for the element.
	// The list-style marker is not present here.
	if ( element.getChild( 0 ).is( '$text' ) ) {
		return null;
	}

	for ( const childNode of element.getChildren() ) {
		// The list-style marker will be inside the `<span>` element. Let's ignore all non-span elements.
		// It may happen that the `<a>` element is added as the first child. Most probably, it's an anchor element.
		if ( !childNode.is( 'element', 'span' ) ) {
			continue;
		}

		const textNodeOrElement = childNode.getChild( 0 );

		// If already found the marker element, use it.
		if ( textNodeOrElement.is( '$text' ) ) {
			return textNodeOrElement;
		}

		return textNodeOrElement.getChild( 0 );
	}
}

// Parses the `list-style-type` value extracted directly from the Word CSS stylesheet and returns proper CSS definition.
//
// @param {String|null} value
// @returns {String|null}
function mapListStyleDefinition( value ) {
	if ( value.startsWith( 'arabic-leading-zero' ) ) {
		return 'decimal-leading-zero';
	}

	switch ( value ) {
		case 'alpha-upper':
			return 'upper-alpha';
		case 'alpha-lower':
			return 'lower-alpha';
		case 'roman-upper':
			return 'upper-roman';
		case 'roman-lower':
			return 'lower-roman';
		case 'circle':
		case 'disc':
		case 'square':
			return value;
		default:
			return null;
	}
}

// Creates an empty list of a given type and inserts it after a specified element.
//
// @param {Object} listStyle List style object which determines the type of newly created list.
// Usually a result of `detectListStyle()` function.
// @param {module:engine/view/element~Element} element Element after which list is inserted.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
// @returns {module:engine/view/element~Element} Newly created list element.

function insertNewEmptyList( listStyle, element, writer ) {
	const parent = element.parent;
	const list = writer.createElement( listStyle.type );
	const position = parent.getChildIndex( element ) + 1;

	writer.insertChild( position, list, parent );

	// We do not support modifying the marker for a particular list item.
	// Set the value for the `list-style-type` property directly to the list container.
	if ( listStyle.style ) {
		writer.setStyle( 'list-style-type', listStyle.style, list );
	}

	if ( listStyle.startIndex && listStyle.startIndex > 1 ) {
		writer.setAttribute( 'start', listStyle.startIndex, list );
	}

	return list;
}

// Transforms a given element into a semantic list item. As the function operates on a provided
// {module:engine/src/view/element~Element element} it will modify the view structure to which this element belongs.
//
// @param {module:engine/view/element~Element} element Element which will be transformed into a list item.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
// @returns {module:engine/view/element~Element} New element to which the given one was transformed. It is
// inserted in place of the old element (the reference to the old element is lost due to renaming).
function transformElementIntoListItem( element, writer ) {
	removeBulletElement( element, writer );

	return writer.rename( 'li', element );
}

// Extracts list item information from Word specific list-like element style:
//
//		`style="mso-list:l1 level1 lfo1"`
//
// where:
//
//		* `l1` is a list id (however it does not mean this is a continuous list - see #43),
//		* `level1` is a list item indentation level,
//		* `lfo1` is a list insertion order in a document.
//
// @param {module:engine/view/element~Element} element Element from which style data is extracted.
// @returns {Object} result
// @returns {Number} result.id Parent list id.
// @returns {Number} result.order List item creation order.
// @returns {Number} result.indent List item indentation level.
function getListItemData( element ) {
	const data = {};
	const listStyle = element.getStyle( 'mso-list' );

	if ( listStyle ) {
		const idMatch = listStyle.match( /(^|\s{1,100})l(\d+)/i );
		const orderMatch = listStyle.match( /\s{0,100}lfo(\d+)/i );
		const indentMatch = listStyle.match( /\s{0,100}level(\d+)/i );

		if ( idMatch && orderMatch && indentMatch ) {
			data.id = idMatch[ 2 ];
			data.order = orderMatch[ 1 ];
			data.indent = indentMatch[ 1 ];
		}
	}

	return data;
}

// Removes span with a numbering/bullet from a given element.
//
// @param {module:engine/view/element~Element} element
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
function removeBulletElement( element, writer ) {
	// Matcher for finding `span` elements holding lists numbering/bullets.
	const bulletMatcher = new Matcher( {
		name: 'span',
		styles: {
			'mso-list': 'Ignore'
		}
	} );

	const range = writer.createRangeIn( element );

	for ( const value of range ) {
		if ( value.type === 'elementStart' && bulletMatcher.match( value.item ) ) {
			writer.remove( value.item );
		}
	}
}

// Whether the previous and current items belong to the same list. It is determined based on `item.id`
// (extracted from `mso-list` style, see #getListItemData) and a previous sibling of the current item.
//
// However, it's quite easy to change the `id` attribute for nested lists in Word. It will break the list feature while pasting.
// Let's check also the `indent` attribute. If the difference between those two elements is equal to 1, we can assume that
// the `currentItem` is a beginning of the nested list because lists in CKEditor 5 always start with the `indent=0` attribute.
// See: https://github.com/ckeditor/ckeditor5/issues/7805.
//
// @param {Object} previousItem
// @param {Object} currentItem
// @returns {Boolean}
function isNewListNeeded( previousItem, currentItem ) {
	if ( !previousItem ) {
		return true;
	}

	if ( previousItem.id !== currentItem.id ) {
		// See: https://github.com/ckeditor/ckeditor5/issues/7805.
		//
		// * List item 1.
		//     - Nested list item 1.
		if ( currentItem.indent - previousItem.indent === 1 ) {
			return false;
		}

		return true;
	}

	const previousSibling = currentItem.element.previousSibling;

	if ( !previousSibling ) {
		return true;
	}

	// Even with the same id the list does not have to be continuous (#43).
	return !isList( previousSibling );
}

function isList( element ) {
	return element.is( 'element', 'ol' ) || element.is( 'element', 'ul' );
}

// Calculates the indentation difference between two given list items (based on the indent attribute
// extracted from the `mso-list` style, see #getListItemData).
//
// @param {Object} previousItem
// @param {Object} currentItem
// @returns {Number}
function getIndentationDifference( previousItem, currentItem ) {
	return previousItem ? currentItem.indent - previousItem.indent : currentItem.indent - 1;
}

// Finds the parent list element (ul/ol) of a given list element with indentation level lower by a given value.
//
// @param {module:engine/view/element~Element} listElement List element from which to start looking for a parent list.
// @param {Number} indentationDifference Indentation difference between lists.
// @returns {module:engine/view/element~Element} Found list element with indentation level lower by a given value.
function findParentListAtLevel( listElement, indentationDifference ) {
	const ancestors = listElement.getAncestors( { parentFirst: true } );

	let parentList = null;
	let levelChange = 0;

	for ( const ancestor of ancestors ) {
		if ( ancestor.name === 'ul' || ancestor.name === 'ol' ) {
			levelChange++;
		}

		if ( levelChange === indentationDifference ) {
			parentList = ancestor;
			break;
		}
	}

	return parentList;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/normalizers/googledocsnormalizer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/normalizers/googledocsnormalizer
 */







const googleDocsMatch = /id=("|')docs-internal-guid-[-0-9a-f]+("|')/i;

/**
 * Normalizer for the content pasted from Google Docs.
 *
 * @implements module:paste-from-office/normalizer~Normalizer
 */
class GoogleDocsNormalizer {
	/**
	 * Creates a new `GoogleDocsNormalizer` instance.
	 *
	 * @param {module:engine/view/document~Document} document View document.
	 */
	constructor( document ) {
		/**
		 * @readonly
		 * @type {module:engine/view/document~Document}
		 */
		this.document = document;
	}

	/**
	 * @inheritDoc
	 */
	isActive( htmlString ) {
		return googleDocsMatch.test( htmlString );
	}

	/**
	 * @inheritDoc
	 */
	execute( data ) {
		const writer = new UpcastWriter( this.document );
		const { body: documentFragment } = data._parsedData;

		removeBoldWrapper( documentFragment, writer );
		unwrapParagraphInListItem( documentFragment, writer );
		transformBlockBrsToParagraphs( documentFragment, writer );

		data.content = documentFragment;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/filters/image.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/filters/image
 */

/* globals btoa */



/**
 * Replaces source attribute of all `<img>` elements representing regular
 * images (not the Word shapes) with inlined base64 image representation extracted from RTF or Blob data.
 *
 * @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment on which transform images.
 * @param {String} rtfData The RTF data from which images representation will be used.
 */
function replaceImagesSourceWithBase64( documentFragment, rtfData ) {
	if ( !documentFragment.childCount ) {
		return;
	}

	const upcastWriter = new UpcastWriter();
	const shapesIds = findAllShapesIds( documentFragment, upcastWriter );

	removeAllImgElementsRepresentingShapes( shapesIds, documentFragment, upcastWriter );
	removeAllShapeElements( documentFragment, upcastWriter );

	const images = findAllImageElementsWithLocalSource( documentFragment, upcastWriter );

	if ( images.length ) {
		replaceImagesFileSourceWithInlineRepresentation( images, extractImageDataFromRtf( rtfData ), upcastWriter );
	}
}

/**
 * Converts given HEX string to base64 representation.
 *
 * @protected
 * @param {String} hexString The HEX string to be converted.
 * @returns {String} Base64 representation of a given HEX string.
 */
function _convertHexToBase64( hexString ) {
	return btoa( hexString.match( /\w{2}/g ).map( char => {
		return String.fromCharCode( parseInt( char, 16 ) );
	} ).join( '' ) );
}

// Finds all shapes (`<v:*>...</v:*>`) ids. Shapes can represent images (canvas)
// or Word shapes (which does not have RTF or Blob representation).
//
// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment
// from which to extract shape ids.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
// @returns {Array.<String>} Array of shape ids.
function findAllShapesIds( documentFragment, writer ) {
	const range = writer.createRangeIn( documentFragment );

	const shapeElementsMatcher = new Matcher( {
		name: /v:(.+)/
	} );

	const shapesIds = [];

	for ( const value of range ) {
		if ( value.type != 'elementStart' ) {
			continue;
		}

		const el = value.item;
		const prevSiblingName = el.previousSibling && el.previousSibling.name || null;

		// If shape element have 'o:gfxdata' attribute and is not directly before `<v:shapetype>` element it means it represent Word shape.
		if ( shapeElementsMatcher.match( el ) && el.getAttribute( 'o:gfxdata' ) && prevSiblingName !== 'v:shapetype' ) {
			shapesIds.push( value.item.getAttribute( 'id' ) );
		}
	}

	return shapesIds;
}

// Removes all `<img>` elements which represents Word shapes and not regular images.
//
// @param {Array.<String>} shapesIds Shape ids which will be checked against `<img>` elements.
// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment from which to remove `<img>` elements.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
function removeAllImgElementsRepresentingShapes( shapesIds, documentFragment, writer ) {
	const range = writer.createRangeIn( documentFragment );

	const imageElementsMatcher = new Matcher( {
		name: 'img'
	} );

	const imgs = [];

	for ( const value of range ) {
		if ( imageElementsMatcher.match( value.item ) ) {
			const el = value.item;
			const shapes = el.getAttribute( 'v:shapes' ) ? el.getAttribute( 'v:shapes' ).split( ' ' ) : [];

			if ( shapes.length && shapes.every( shape => shapesIds.indexOf( shape ) > -1 ) ) {
				imgs.push( el );
			// Shapes may also have empty source while content is paste in some browsers (Safari).
			} else if ( !el.getAttribute( 'src' ) ) {
				imgs.push( el );
			}
		}
	}

	for ( const img of imgs ) {
		writer.remove( img );
	}
}

// Removes all shape elements (`<v:*>...</v:*>`) so they do not pollute the output structure.
//
// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment from which to remove shape elements.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
function removeAllShapeElements( documentFragment, writer ) {
	const range = writer.createRangeIn( documentFragment );

	const shapeElementsMatcher = new Matcher( {
		name: /v:(.+)/
	} );

	const shapes = [];

	for ( const value of range ) {
		if ( value.type == 'elementStart' && shapeElementsMatcher.match( value.item ) ) {
			shapes.push( value.item );
		}
	}

	for ( const shape of shapes ) {
		writer.remove( shape );
	}
}

// Finds all `<img>` elements in a given document fragment which have source pointing to local `file://` resource.
//
// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment Document fragment in which to look for `<img>` elements.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
// @returns {Object} result All found images grouped by source type.
// @returns {Array.<module:engine/view/element~Element>} result.file Array of found `<img>` elements with `file://` source.
// @returns {Array.<module:engine/view/element~Element>} result.blob Array of found `<img>` elements with `blob:` source.
function findAllImageElementsWithLocalSource( documentFragment, writer ) {
	const range = writer.createRangeIn( documentFragment );

	const imageElementsMatcher = new Matcher( {
		name: 'img'
	} );

	const imgs = [];

	for ( const value of range ) {
		if ( imageElementsMatcher.match( value.item ) ) {
			if ( value.item.getAttribute( 'src' ).startsWith( 'file://' ) ) {
				imgs.push( value.item );
			}
		}
	}

	return imgs;
}

// Extracts all images HEX representations from a given RTF data.
//
// @param {String} rtfData The RTF data from which to extract images HEX representation.
// @returns {Array.<Object>} Array of found HEX representations. Each array item is an object containing:
//
// 		* {String} hex Image representation in HEX format.
// 		* {string} type Type of image, `image/png` or `image/jpeg`.
function extractImageDataFromRtf( rtfData ) {
	if ( !rtfData ) {
		return [];
	}

	const regexPictureHeader = /{\\pict[\s\S]+?\\bliptag-?\d+(\\blipupi-?\d+)?({\\\*\\blipuid\s?[\da-fA-F]+)?[\s}]*?/;
	const regexPicture = new RegExp( '(?:(' + regexPictureHeader.source + '))([\\da-fA-F\\s]+)\\}', 'g' );
	const images = rtfData.match( regexPicture );
	const result = [];

	if ( images ) {
		for ( const image of images ) {
			let imageType = false;

			if ( image.includes( '\\pngblip' ) ) {
				imageType = 'image/png';
			} else if ( image.includes( '\\jpegblip' ) ) {
				imageType = 'image/jpeg';
			}

			if ( imageType ) {
				result.push( {
					hex: image.replace( regexPictureHeader, '' ).replace( /[^\da-fA-F]/g, '' ),
					type: imageType
				} );
			}
		}
	}

	return result;
}

// Replaces `src` attribute value of all given images with the corresponding base64 image representation.
//
// @param {Array.<module:engine/view/element~Element>} imageElements Array of image elements which will have its source replaced.
// @param {Array.<Object>} imagesHexSources Array of images hex sources (usually the result of `extractImageDataFromRtf()` function).
// The array should be the same length as `imageElements` parameter.
// @param {module:engine/view/upcastwriter~UpcastWriter} writer
function replaceImagesFileSourceWithInlineRepresentation( imageElements, imagesHexSources, writer ) {
	// Assume there is an equal amount of image elements and images HEX sources so they can be matched accordingly based on existing order.
	if ( imageElements.length === imagesHexSources.length ) {
		for ( let i = 0; i < imageElements.length; i++ ) {
			const newSrc = `data:${ imagesHexSources[ i ].type };base64,${ _convertHexToBase64( imagesHexSources[ i ].hex ) }`;
			writer.setAttribute( 'src', newSrc, imageElements[ i ] );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/normalizers/mswordnormalizer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/normalizers/mswordnormalizer
 */




const msWordMatch1 = /<meta\s*name="?generator"?\s*content="?microsoft\s*word\s*\d+"?\/?>/i;
const msWordMatch2 = /xmlns:o="urn:schemas-microsoft-com/i;

/**
 * Normalizer for the content pasted from Microsoft Word.
 *
 * @implements module:paste-from-office/normalizer~Normalizer
 */
class MSWordNormalizer {
	/**
	 * Creates a new `MSWordNormalizer` instance.
	 *
	 * @param {module:engine/view/document~Document} document View document.
	 */
	constructor( document ) {
		/**
		 * @readonly
		 * @type {module:engine/view/document~Document}
		 */
		this.document = document;
	}

	/**
	 * @inheritDoc
	 */
	isActive( htmlString ) {
		return msWordMatch1.test( htmlString ) || msWordMatch2.test( htmlString );
	}

	/**
	 * @inheritDoc
	 */
	execute( data ) {
		const { body: documentFragment, stylesString } = data._parsedData;

		transformListItemLikeElementsIntoLists( documentFragment, stylesString );
		replaceImagesSourceWithBase64( documentFragment, data.dataTransfer.getData( 'text/rtf' ) );

		data.content = documentFragment;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/filters/space.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/filters/space
 */

/**
 * Replaces last space preceding elements closing tag with `&nbsp;`. Such operation prevents spaces from being removed
 * during further DOM/View processing (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
 * This method also takes into account Word specific `<o:p></o:p>` empty tags.
 * Additionally multiline sequences of spaces and new lines between tags are removed (see #39 and #40).
 *
 * @param {String} htmlString HTML string in which spacing should be normalized.
 * @returns {String} Input HTML with spaces normalized.
 */
function normalizeSpacing( htmlString ) {
	// Run normalizeSafariSpaceSpans() two times to cover nested spans.
	return normalizeSafariSpaceSpans( normalizeSafariSpaceSpans( htmlString ) )
		// Remove all \r\n from "spacerun spans" so the last replace line doesn't strip all whitespaces.
		.replace( /(<span\s+style=['"]mso-spacerun:yes['"]>[^\S\r\n]*?)[\r\n]+([^\S\r\n]*<\/span>)/g, '$1$2' )
		.replace( /<span\s+style=['"]mso-spacerun:yes['"]><\/span>/g, '' )
		.replace( / <\//g, '\u00A0</' )
		.replace( / <o:p><\/o:p>/g, '\u00A0<o:p></o:p>' )
		// Remove <o:p> block filler from empty paragraph. Safari uses \u00A0 instead of &nbsp;.
		.replace( /<o:p>(&nbsp;|\u00A0)<\/o:p>/g, '' )
		// Remove all whitespaces when they contain any \r or \n.
		.replace( />([^\S\r\n]*[\r\n]\s*)</g, '><' );
}

/**
 * Normalizes spacing in special Word `spacerun spans` (`<span style='mso-spacerun:yes'>\s+</span>`) by replacing
 * all spaces with `&nbsp; ` pairs. This prevents spaces from being removed during further DOM/View processing
 * (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
 *
 * @param {Document} htmlDocument Native `Document` object in which spacing should be normalized.
 */
function normalizeSpacerunSpans( htmlDocument ) {
	htmlDocument.querySelectorAll( 'span[style*=spacerun]' ).forEach( el => {
		const innerTextLength = el.innerText.length || 0;

		el.innerText = Array( innerTextLength + 1 ).join( '\u00A0 ' ).substr( 0, innerTextLength );
	} );
}

// Normalizes specific spacing generated by Safari when content pasted from Word (`<span class="Apple-converted-space"> </span>`)
// by replacing all spaces sequences longer than 1 space with `&nbsp; ` pairs. This prevents spaces from being removed during
// further DOM/View processing (see especially {@link module:engine/view/domconverter~DomConverter#_processDataFromDomText}).
//
// This function is similar to {@link module:clipboard/utils/normalizeclipboarddata normalizeClipboardData util} but uses
// regular spaces / &nbsp; sequence for replacement.
//
// @param {String} htmlString HTML string in which spacing should be normalized
// @returns {String} Input HTML with spaces normalized.
function normalizeSafariSpaceSpans( htmlString ) {
	return htmlString.replace( /<span(?: class="Apple-converted-space"|)>(\s+)<\/span>/g, ( fullMatch, spaces ) => {
		return spaces.length === 1 ? ' ' : Array( spaces.length + 1 ).join( '\u00A0 ' ).substr( 0, spaces.length );
	} );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/filters/parse.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/filters/parse
 */

/* globals DOMParser */





/**
 * Parses provided HTML extracting contents of `<body>` and `<style>` tags.
 *
 * @param {String} htmlString HTML string to be parsed.
 * @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor
 * @returns {Object} result
 * @returns {module:engine/view/documentfragment~DocumentFragment} result.body Parsed body
 * content as a traversable structure.
 * @returns {String} result.bodyString Entire body content as a string.
 * @returns {Array.<CSSStyleSheet>} result.styles Array of native `CSSStyleSheet` objects, each representing
 * separate `style` tag from the source HTML.
 * @returns {String} result.stylesString All `style` tags contents combined in the order of occurrence into one string.
 */
function parseHtml( htmlString, stylesProcessor ) {
	const domParser = new DOMParser();

	// Remove Word specific "if comments" so content inside is not omitted by the parser.
	htmlString = htmlString.replace( /<!--\[if gte vml 1]>/g, '' );

	const normalizedHtml = normalizeSpacing( cleanContentAfterBody( htmlString ) );

	// Parse htmlString as native Document object.
	const htmlDocument = domParser.parseFromString( normalizedHtml, 'text/html' );

	normalizeSpacerunSpans( htmlDocument );

	// Get `innerHTML` first as transforming to View modifies the source document.
	const bodyString = htmlDocument.body.innerHTML;

	// Transform document.body to View.
	const bodyView = documentToView( htmlDocument, stylesProcessor );

	// Extract stylesheets.
	const stylesObject = extractStyles( htmlDocument );

	return {
		body: bodyView,
		bodyString,
		styles: stylesObject.styles,
		stylesString: stylesObject.stylesString
	};
}

// Transforms native `Document` object into {@link module:engine/view/documentfragment~DocumentFragment}. Comments are skipped.
//
// @param {Document} htmlDocument Native `Document` object to be transformed.
// @param {module:engine/view/stylesmap~StylesProcessor} stylesProcessor
// @returns {module:engine/view/documentfragment~DocumentFragment}
function documentToView( htmlDocument, stylesProcessor ) {
	const viewDocument = new Document( stylesProcessor );
	const domConverter = new DomConverter( viewDocument, { renderingMode: 'data' } );
	const fragment = htmlDocument.createDocumentFragment();
	const nodes = htmlDocument.body.childNodes;

	while ( nodes.length > 0 ) {
		fragment.appendChild( nodes[ 0 ] );
	}

	return domConverter.domToView( fragment, { skipComments: true } );
}

// Extracts both `CSSStyleSheet` and string representation from all `style` elements available in a provided `htmlDocument`.
//
// @param {Document} htmlDocument Native `Document` object from which styles will be extracted.
// @returns {Object} result
// @returns {Array.<CSSStyleSheet>} result.styles Array of native `CSSStyleSheet` object, each representing
// separate `style` tag from the source object.
// @returns {String} result.stylesString All `style` tags contents combined in the order of occurrence as one string.
function extractStyles( htmlDocument ) {
	const styles = [];
	const stylesString = [];
	const styleTags = Array.from( htmlDocument.getElementsByTagName( 'style' ) );

	for ( const style of styleTags ) {
		if ( style.sheet && style.sheet.cssRules && style.sheet.cssRules.length ) {
			styles.push( style.sheet );
			stylesString.push( style.innerHTML );
		}
	}

	return {
		styles,
		stylesString: stylesString.join( ' ' )
	};
}

// Removes leftover content from between closing </body> and closing </html> tag:
//
// 		<html><body><p>Foo Bar</p></body><span>Fo</span></html> -> <html><body><p>Foo Bar</p></body></html>
//
// This function is used as specific browsers (Edge) add some random content after `body` tag when pasting from Word.
// @param {String} htmlString The HTML string to be cleaned.
// @returns {String} The HTML string with leftover content removed.
function cleanContentAfterBody( htmlString ) {
	const bodyCloseTag = '</body>';
	const htmlCloseTag = '</html>';

	const bodyCloseIndex = htmlString.indexOf( bodyCloseTag );

	if ( bodyCloseIndex < 0 ) {
		return htmlString;
	}

	const htmlCloseIndex = htmlString.indexOf( htmlCloseTag, bodyCloseIndex + bodyCloseTag.length );

	return htmlString.substring( 0, bodyCloseIndex + bodyCloseTag.length ) +
		( htmlCloseIndex >= 0 ? htmlString.substring( htmlCloseIndex ) : '' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-paste-from-office/src/pastefromoffice.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module paste-from-office/pastefromoffice
 */









/**
 * The Paste from Office plugin.
 *
 * This plugin handles content pasted from Office apps and transforms it (if necessary)
 * to a valid structure which can then be understood by the editor features.
 *
 * Transformation is made by a set of predefined {@link module:paste-from-office/normalizer~Normalizer normalizers}.
 * This plugin includes following normalizers:
 *   * {@link module:paste-from-office/normalizers/mswordnormalizer~MSWordNormalizer Microsoft Word normalizer}
 *   * {@link module:paste-from-office/normalizers/googledocsnormalizer~GoogleDocsNormalizer Google Docs normalizer}
 *
 * For more information about this feature check the {@glink api/paste-from-office package page}.
 *
 * @extends module:core/plugin~Plugin
 */
class PasteFromOffice extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'PasteFromOffice';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ClipboardPipeline ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const viewDocument = editor.editing.view.document;
		const normalizers = [];

		normalizers.push( new MSWordNormalizer( viewDocument ) );
		normalizers.push( new GoogleDocsNormalizer( viewDocument ) );

		editor.plugins.get( 'ClipboardPipeline' ).on(
			'inputTransformation',
			( evt, data ) => {
				if ( data._isTransformedWithPasteFromOffice ) {
					return;
				}

				const codeBlock = editor.model.document.selection.getFirstPosition().parent;

				if ( codeBlock.is( 'element', 'codeBlock' ) ) {
					return;
				}

				const htmlString = data.dataTransfer.getData( 'text/html' );
				const activeNormalizer = normalizers.find( normalizer => normalizer.isActive( htmlString ) );

				if ( activeNormalizer ) {
					data._parsedData = parseHtml( htmlString, viewDocument.stylesProcessor );

					activeNormalizer.execute( data );

					data._isTransformedWithPasteFromOffice = true;
				}
			},
			{ priority: 'high' }
		);
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-remove-format/theme/icons/remove-format.svg
/* harmony default export */ const remove_format = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8.69 14.915c.053.052.173.083.36.093a.366.366 0 0 1 .345.485l-.003.01a.738.738 0 0 1-.697.497h-2.67a.374.374 0 0 1-.353-.496l.013-.038a.681.681 0 0 1 .644-.458c.197-.012.325-.043.386-.093a.28.28 0 0 0 .072-.11L9.592 4.5H6.269c-.359-.017-.609.013-.75.09-.142.078-.289.265-.442.563-.192.29-.516.464-.864.464H4.17a.43.43 0 0 1-.407-.569L4.46 3h13.08l-.62 2.043a.81.81 0 0 1-.775.574h-.114a.486.486 0 0 1-.486-.486c.001-.284-.054-.464-.167-.54-.112-.076-.367-.106-.766-.091h-3.28l-2.68 10.257c-.006.074.007.127.038.158zM3 17h8a.5.5 0 1 1 0 1H3a.5.5 0 1 1 0-1zm11.299 1.17a.75.75 0 1 1-1.06-1.06l1.414-1.415-1.415-1.414a.75.75 0 0 1 1.06-1.06l1.415 1.414 1.414-1.415a.75.75 0 1 1 1.06 1.06l-1.413 1.415 1.414 1.415a.75.75 0 0 1-1.06 1.06l-1.415-1.414-1.414 1.414z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-remove-format/src/removeformatui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module remove-format/removeformatui
 */






const REMOVE_FORMAT = 'removeFormat';

/**
 * The remove format UI plugin. It registers the `'removeFormat'` button which can be
 * used in the toolbar.
 *
 * @extends module:core/plugin~Plugin
 */
class RemoveFormatUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'RemoveFormatUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.ui.componentFactory.add( REMOVE_FORMAT, locale => {
			const command = editor.commands.get( REMOVE_FORMAT );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Remove Format' ),
				icon: remove_format,
				tooltip: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute the command.
			this.listenTo( view, 'execute', () => {
				editor.execute( REMOVE_FORMAT );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-remove-format/src/removeformatcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module remove-format/removeformatcommand
 */




/**
 * The remove format command.
 *
 * It is used by the {@link module:remove-format/removeformat~RemoveFormat remove format feature}
 * to clear the formatting in the selection.
 *
 *		editor.execute( 'removeFormat' );
 *
 * @extends module:core/command~Command
 */
class RemoveFormatCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;

		this.isEnabled = !!first_first( this._getFormattingItems( model.document.selection, model.schema ) );
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const model = this.editor.model;
		const schema = model.schema;

		model.change( writer => {
			for ( const item of this._getFormattingItems( model.document.selection, schema ) ) {
				if ( item.is( 'selection' ) ) {
					for ( const attributeName of this._getFormattingAttributes( item, schema ) ) {
						writer.removeSelectionAttribute( attributeName );
					}
				} else {
					// Workaround for items with multiple removable attributes. See
					// https://github.com/ckeditor/ckeditor5-remove-format/pull/1#pullrequestreview-220515609
					const itemRange = writer.createRangeOn( item );

					for ( const attributeName of this._getFormattingAttributes( item, schema ) ) {
						writer.removeAttribute( attributeName, itemRange );
					}
				}
			}
		} );
	}

	/**
	 * Returns an iterable of items in a selection (including the selection itself) that have formatting model
	 * attributes to be removed by the feature.
	 *
	 * @protected
	 * @param {module:engine/model/documentselection~DocumentSelection} selection
	 * @param {module:engine/model/schema~Schema} schema The schema describing the item.
	 * @returns {Iterable.<module:engine/model/item~Item>|Iterable.<module:engine/model/documentselection~DocumentSelection>}
	 */
	* _getFormattingItems( selection, schema ) {
		const itemHasRemovableFormatting = item => {
			return !!first_first( this._getFormattingAttributes( item, schema ) );
		};

		// Check formatting on selected items that are not blocks.
		for ( const curRange of selection.getRanges() ) {
			for ( const item of curRange.getItems() ) {
				if ( !schema.isBlock( item ) && itemHasRemovableFormatting( item ) ) {
					yield item;
				}
			}
		}

		// Check formatting from selected blocks.
		for ( const block of selection.getSelectedBlocks() ) {
			if ( itemHasRemovableFormatting( block ) ) {
				yield block;
			}
		}

		// Finally the selection might be formatted as well, so make sure to check it.
		if ( itemHasRemovableFormatting( selection ) ) {
			yield selection;
		}
	}

	/**
	 * Returns an iterable of formatting attributes of a given model item.
	 *
	 * **Note:** Formatting items have the `isFormatting` property set to `true`.
	 *
	 * @protected
	 * @param {module:engine/model/item~Item|module:engine/model/documentselection~DocumentSelection} item
	 * @param {module:engine/model/schema~Schema} schema The schema describing the item.
	 * @returns {Iterable.<String>} The names of formatting attributes found in a given item.
	 */
	* _getFormattingAttributes( item, schema ) {
		for ( const [ attributeName ] of item.getAttributes() ) {
			const attributeProperties = schema.getAttributeProperties( attributeName );

			if ( attributeProperties && attributeProperties.isFormatting ) {
				yield attributeName;
			}
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-remove-format/src/removeformatediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module remove-format/removeformatediting
 */





/**
 * The remove format editing plugin.
 *
 * It registers the {@link module:remove-format/removeformatcommand~RemoveFormatCommand removeFormat} command.
 *
 * @extends module:core/plugin~Plugin
 */
class RemoveFormatEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'RemoveFormatEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		editor.commands.add( 'removeFormat', new RemoveFormatCommand( editor ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-remove-format/src/removeformat.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module remove-format/removeformat
 */






/**
 * The remove format plugin.
 *
 * This is a "glue" plugin which loads the {@link module:remove-format/removeformatediting~RemoveFormatEditing}
 * and {@link module:remove-format/removeformatui~RemoveFormatUI} plugins.
 *
 * For a detailed overview, check out the {@glink features/remove-format remove format} feature documentation.
 *
 * @extends module:core/plugin~Plugin
 */
class RemoveFormat extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ RemoveFormatEditing, RemoveFormatUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'RemoveFormat';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-source-editing/src/utils/formathtml.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module source-editing/utils/formathtml
 */

/**
 * A simple (and naive) HTML code formatter that returns a formatted HTML markup that can be easily
 * parsed by human eyes. It beautifies the HTML code by adding new lines between elements that behave like block elements
 * (https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements
 * and a few more like `tr`, `td`, and similar ones) and inserting indents for nested content.
 *
 * WARNING: This function works only on a text that does not contain any indentations or new lines.
 * Calling this function on the already formatted text will damage the formatting.
 *
 * @param {String} input An HTML string to format.
 * @returns {String}
 */
function formatHtml( input ) {
	// A list of block-like elements around which the new lines should be inserted, and within which
	// the indentation of their children should be increased.
	// The list is partially based on https://developer.mozilla.org/en-US/docs/Web/HTML/Block-level_elements that contains
	// a full list of HTML block-level elements.
	// A void element is an element that cannot have any child - https://html.spec.whatwg.org/multipage/syntax.html#void-elements.
	// Note that <pre> element is not listed on this list to avoid breaking whitespace formatting.
	const elementsToFormat = [
		{ name: 'address', isVoid: false },
		{ name: 'article', isVoid: false },
		{ name: 'aside', isVoid: false },
		{ name: 'blockquote', isVoid: false },
		{ name: 'br', isVoid: true },
		{ name: 'details', isVoid: false },
		{ name: 'dialog', isVoid: false },
		{ name: 'dd', isVoid: false },
		{ name: 'div', isVoid: false },
		{ name: 'dl', isVoid: false },
		{ name: 'dt', isVoid: false },
		{ name: 'fieldset', isVoid: false },
		{ name: 'figcaption', isVoid: false },
		{ name: 'figure', isVoid: false },
		{ name: 'footer', isVoid: false },
		{ name: 'form', isVoid: false },
		{ name: 'h1', isVoid: false },
		{ name: 'h2', isVoid: false },
		{ name: 'h3', isVoid: false },
		{ name: 'h4', isVoid: false },
		{ name: 'h5', isVoid: false },
		{ name: 'h6', isVoid: false },
		{ name: 'header', isVoid: false },
		{ name: 'hgroup', isVoid: false },
		{ name: 'hr', isVoid: true },
		{ name: 'input', isVoid: true },
		{ name: 'li', isVoid: false },
		{ name: 'main', isVoid: false },
		{ name: 'nav', isVoid: false },
		{ name: 'ol', isVoid: false },
		{ name: 'p', isVoid: false },
		{ name: 'section', isVoid: false },
		{ name: 'table', isVoid: false },
		{ name: 'tbody', isVoid: false },
		{ name: 'td', isVoid: false },
		{ name: 'textarea', isVoid: false },
		{ name: 'th', isVoid: false },
		{ name: 'thead', isVoid: false },
		{ name: 'tr', isVoid: false },
		{ name: 'ul', isVoid: false }
	];

	const elementNamesToFormat = elementsToFormat.map( element => element.name ).join( '|' );

	// It is not the fastest way to format the HTML markup but the performance should be good enough.
	const lines = input
		// Add new line before and after `<tag>` and `</tag>`.
		// It may separate individual elements with two new lines, but this will be fixed below.
		.replace( new RegExp( `</?(${ elementNamesToFormat })( .*?)?>`, 'g' ), '\n$&\n' )
		// Divide input string into lines, which start with either an opening tag, a closing tag, or just a text.
		.split( '\n' );

	let indentCount = 0;

	return lines
		.filter( line => line.length )
		.map( line => {
			if ( isNonVoidOpeningTag( line, elementsToFormat ) ) {
				return indentLine( line, indentCount++ );
			}

			if ( isClosingTag( line, elementsToFormat ) ) {
				return indentLine( line, --indentCount );
			}

			return indentLine( line, indentCount );
		} )
		.join( '\n' );
}

// Checks, if an argument is an opening tag of a non-void element to be formatted.
//
// @param {String} line String to check.
// @param {Array} elementsToFormat Elements to be formatted.
// @param {String} elementsToFormat.name Element name.
// @param {Boolean} elementsToFormat.isVoid Flag indicating whether element is a void one.
// @returns {Boolean}
function isNonVoidOpeningTag( line, elementsToFormat ) {
	return elementsToFormat.some( element => {
		if ( element.isVoid ) {
			return false;
		}

		if ( !new RegExp( `<${ element.name }( .*?)?>` ).test( line ) ) {
			return false;
		}

		return true;
	} );
}

// Checks, if an argument is a closing tag.
//
// @param {String} line String to check.
// @param {Array} elementsToFormat Elements to be formatted.
// @param {String} elementsToFormat.name Element name.
// @param {Boolean} elementsToFormat.isVoid Flag indicating whether element is a void one.
// @returns {Boolean}
function isClosingTag( line, elementsToFormat ) {
	return elementsToFormat.some( element => {
		return new RegExp( `</${ element.name }>` ).test( line );
	} );
}

// Indents a line by a specified number of characters.
//
// @param {String} line Line to indent.
// @param {Number} indentCount Number of characters to use for indentation.
// @param {String} [indentChar] Indentation character(s). 4 spaces by default.
// @returns {String}
function indentLine( line, indentCount, indentChar = '    ' ) {
	// More about Math.max() here in https://github.com/ckeditor/ckeditor5/issues/10698.
	return `${ indentChar.repeat( Math.max( 0, indentCount ) ) }${ line }`;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-source-editing/theme/sourceediting.css
var sourceediting = __webpack_require__(671);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-source-editing/theme/sourceediting.css

            

var sourceediting_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

sourceediting_options.insert = "head";
sourceediting_options.singleton = true;

var sourceediting_update = injectStylesIntoStyleTag_default()(sourceediting/* default */.Z, sourceediting_options);



/* harmony default export */ const theme_sourceediting = (sourceediting/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-source-editing/theme/icons/source-editing.svg
/* harmony default export */ const source_editing = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m12.5 0 5 4.5v15.003h-16V0h11zM3 1.5v3.25l-1.497 1-.003 8 1.5 1v3.254L7.685 18l-.001 1.504H17.5V8.002L16 9.428l-.004-4.22-4.222-3.692L3 1.5z\"/><path d=\"M4.06 6.64a.75.75 0 0 1 .958 1.15l-.085.07L2.29 9.75l2.646 1.89c.302.216.4.62.232.951l-.058.095a.75.75 0 0 1-.951.232l-.095-.058-3.5-2.5V9.14l3.496-2.5zm4.194 6.22a.75.75 0 0 1-.958-1.149l.085-.07 2.643-1.89-2.646-1.89a.75.75 0 0 1-.232-.952l.058-.095a.75.75 0 0 1 .95-.232l.096.058 3.5 2.5v1.22l-3.496 2.5zm7.644-.836 2.122 2.122-5.825 5.809-2.125-.005.003-2.116zm2.539-1.847 1.414 1.414a.5.5 0 0 1 0 .707l-1.06 1.06-2.122-2.12 1.061-1.061a.5.5 0 0 1 .707 0z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-source-editing/src/sourceediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module source-editing/sourceediting
 */

/* global console */










const COMMAND_FORCE_DISABLE_ID = 'SourceEditingMode';

/**
 * The source editing feature.
 *
 * It provides the possibility to view and edit the source of the document.
 *
 * For a detailed overview, check the {@glink features/source-editing source editing feature documentation} and the
 * {@glink api/source-editing package page}.
 *
 * @extends module:core/plugin~Plugin
 */
class SourceEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SourceEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ PendingActions ];
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * Flag indicating whether the document source mode is active.
		 *
		 * @observable
		 * @member {Boolean}
		 */
		this.set( 'isSourceEditingMode', false );

		/**
		 * The element replacer instance used to replace the editing roots with the wrapper elements containing the document source.
		 *
		 * @private
		 * @member {module:utils/elementreplacer~ElementReplacer}
		 */
		this._elementReplacer = new ElementReplacer();

		/**
		 * Maps all root names to wrapper elements containing the document source.
		 *
		 * @private
		 * @member {Map.<String,HTMLElement>}
		 */
		this._replacedRoots = new Map();

		/**
		 * Maps all root names to their document data.
		 *
		 * @private
		 * @member {Map.<String,String>}
		 */
		this._dataFromRoots = new Map();
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.ui.componentFactory.add( 'sourceEditing', locale => {
			const buttonView = new buttonview_ButtonView( locale );

			buttonView.set( {
				label: t( 'Source' ),
				icon: source_editing,
				tooltip: true,
				withText: true,
				class: 'ck-source-editing-button'
			} );

			buttonView.bind( 'isOn' ).to( this, 'isSourceEditingMode' );

			// The button should be disabled if one of the following conditions is met:
			buttonView.bind( 'isEnabled' ).to(
				this, 'isEnabled',
				editor, 'isReadOnly',
				editor.plugins.get( PendingActions ), 'hasAny',
				( isEnabled, isEditorReadOnly, hasAnyPendingActions ) => {
					// (1) The plugin itself is disabled.
					if ( !isEnabled ) {
						return false;
					}

					// (2) The editor is in read-only mode.
					if ( isEditorReadOnly ) {
						return false;
					}

					// (3) Any pending action is scheduled. It may change the model, so modifying the document source should be prevented
					// until the model is finally set.
					if ( hasAnyPendingActions ) {
						return false;
					}

					return true;
				}
			);

			this.listenTo( buttonView, 'execute', () => {
				this.isSourceEditingMode = !this.isSourceEditingMode;
			} );

			return buttonView;
		} );

		// Currently, the plugin handles the source editing mode by itself only for the classic editor. To use this plugin with other
		// integrations, listen to the `change:isSourceEditingMode` event and act accordingly.
		if ( this._isAllowedToHandleSourceEditingMode() ) {
			this.on( 'change:isSourceEditingMode', ( evt, name, isSourceEditingMode ) => {
				if ( isSourceEditingMode ) {
					this._showSourceEditing();
					this._disableCommands();
				} else {
					this._hideSourceEditing();
					this._enableCommands();
				}
			} );

			this.on( 'change:isEnabled', ( evt, name, isEnabled ) => this._handleReadOnlyMode( !isEnabled ) );

			this.listenTo( editor, 'change:isReadOnly', ( evt, name, isReadOnly ) => this._handleReadOnlyMode( isReadOnly ) );
		}

		// Update the editor data while calling editor.getData() in the source editing mode.
		editor.data.on( 'get', () => {
			if ( this.isSourceEditingMode ) {
				this._updateEditorData();
			}
		}, { priority: 'high' } );
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const editor = this.editor;

		const collaborationPluginNamesToWarn = [
			'RealTimeCollaborativeEditing',
			'CommentsEditing',
			'TrackChangesEditing',
			'RevisionHistory'
		];

		// Currently, the basic integration with Collaboration Features is to display a warning in the console.
		if ( collaborationPluginNamesToWarn.some( pluginName => editor.plugins.has( pluginName ) ) ) {
			console.warn(
				'You initialized the editor with the source editing feature and at least one of the collaboration features. ' +
				'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
				'that contains markers created by the collaboration features.'
			);
		}

		// Restricted Editing integration can also lead to problems. Warn the user accordingly.
		if ( editor.plugins.has( 'RestrictedEditingModeEditing' ) ) {
			console.warn(
				'You initialized the editor with the source editing feature and restricted editing feature. ' +
				'Please be advised that the source editing feature may not work, and be careful when editing document source ' +
				'that contains markers created by the restricted editing feature.'
			);
		}
	}

	/**
	 * Creates source editing wrappers that replace each editing root. Each wrapper contains the document source from the corresponding
	 * root.
	 *
	 * The wrapper element contains a textarea and it solves the problem, that the textarea element cannot auto expand its height based on
	 * the content it contains. The solution is to make the textarea more like a plain div element, which expands in height as much as it
	 * needs to, in order to display the whole document source without scrolling. The wrapper element is a parent for the textarea and for
	 * the pseudo-element `::after`, that replicates the look, content, and position of the textarea. The pseudo-element replica is hidden,
	 * but it is styled to be an identical visual copy of the textarea with the same content. Then, the wrapper is a grid container and both
	 * of its children (the textarea and the `::after` pseudo-element) are positioned within a CSS grid to occupy the same grid cell. The
	 * content in the pseudo-element `::after` is set in CSS and it stretches the grid to the appropriate size based on the textarea value.
	 * Since both children occupy the same grid cell, both have always the same height.
	 *
	 * @private
	 */
	_showSourceEditing() {
		const editor = this.editor;
		const editingView = editor.editing.view;
		const model = editor.model;

		model.change( writer => {
			writer.setSelection( null );
			writer.removeSelectionAttribute( model.document.selection.getAttributeKeys() );
		} );

		// It is not needed to iterate through all editing roots, as currently the plugin supports only the Classic Editor with a single
		// main root, but this code may help understand and use this feature in external integrations.
		for ( const [ rootName, domRootElement ] of editingView.domRoots ) {
			const data = formatSource( editor.data.get( { rootName } ) );

			const domSourceEditingElementTextarea = createElement( domRootElement.ownerDocument, 'textarea', {
				rows: '1',
				'aria-label': 'Source code editing area'
			} );

			const domSourceEditingElementWrapper = createElement( domRootElement.ownerDocument, 'div', {
				class: 'ck-source-editing-area',
				'data-value': data
			}, [ domSourceEditingElementTextarea ] );

			domSourceEditingElementTextarea.value = data;

			// Setting a value to textarea moves the input cursor to the end. We want the selection at the beginning.
			domSourceEditingElementTextarea.setSelectionRange( 0, 0 );

			// Bind the textarea's value to the wrapper's `data-value` property. Each change of the textarea's value updates the
			// wrapper's `data-value` property.
			domSourceEditingElementTextarea.addEventListener( 'input', () => {
				domSourceEditingElementWrapper.dataset.value = domSourceEditingElementTextarea.value;
			} );

			editingView.change( writer => {
				const viewRoot = editingView.document.getRoot( rootName );

				writer.addClass( 'ck-hidden', viewRoot );
			} );

			// Register the element so it becomes available for Alt+F10 and Esc navigation.
			editor.ui.setEditableElement( 'sourceEditing:' + rootName, domSourceEditingElementTextarea );

			this._replacedRoots.set( rootName, domSourceEditingElementWrapper );

			this._elementReplacer.replace( domRootElement, domSourceEditingElementWrapper );

			this._dataFromRoots.set( rootName, data );
		}

		this._focusSourceEditing();
	}

	/**
	 * Restores all hidden editing roots and sets the source data in them.
	 *
	 * @private
	 */
	_hideSourceEditing() {
		const editor = this.editor;
		const editingView = editor.editing.view;

		this._updateEditorData();

		editingView.change( writer => {
			for ( const [ rootName ] of this._replacedRoots ) {
				writer.removeClass( 'ck-hidden', editingView.document.getRoot( rootName ) );
			}
		} );

		this._elementReplacer.restore();

		this._replacedRoots.clear();
		this._dataFromRoots.clear();

		editingView.focus();
	}

	/**
	 * Updates the source data in all hidden editing roots.
	 *
	 * @private
	 */
	_updateEditorData() {
		const editor = this.editor;
		const data = {};

		for ( const [ rootName, domSourceEditingElementWrapper ] of this._replacedRoots ) {
			const oldData = this._dataFromRoots.get( rootName );
			const newData = domSourceEditingElementWrapper.dataset.value;

			// Do not set the data unless some changes have been made in the meantime.
			// This prevents empty undo steps after switching to the normal editor.
			if ( oldData !== newData ) {
				data[ rootName ] = newData;
			}
		}

		if ( Object.keys( data ).length ) {
			editor.data.set( data, { batchType: { isUndoable: true } } );
		}
	}

	/**
	 * Focuses the textarea containing document source from the first editing root.
	 *
	 * @private
	 */
	_focusSourceEditing() {
		const editor = this.editor;
		const [ domSourceEditingElementWrapper ] = this._replacedRoots.values();
		const textarea = domSourceEditingElementWrapper.querySelector( 'textarea' );

		// The FocusObserver was disabled by View.render() while the DOM root was getting hidden and the replacer
		// revealed the textarea. So it couldn't notice that the DOM root got blurred in the process.
		// Let's sync this state manually here because otherwise Renderer will attempt to render selection
		// in an invisible DOM root.
		editor.editing.view.document.isFocused = false;

		textarea.focus();
	}

	/**
	 * Disables all commands.
	 *
	 * @private
	 */
	_disableCommands() {
		const editor = this.editor;

		for ( const command of editor.commands.commands() ) {
			command.forceDisabled( COMMAND_FORCE_DISABLE_ID );
		}
	}

	/**
	 * Clears forced disable for all commands, that was previously set through {@link #_disableCommands}.
	 *
	 * @private
	 */
	_enableCommands() {
		const editor = this.editor;

		for ( const command of editor.commands.commands() ) {
			command.clearForceDisabled( COMMAND_FORCE_DISABLE_ID );
		}
	}

	/**
	 * Adds or removes the `readonly` attribute from the textarea from all roots, if document source mode is active.
	 *
	 * @param {Boolean} isReadOnly Indicates whether all textarea elements should be read-only.
	 */
	_handleReadOnlyMode( isReadOnly ) {
		if ( !this.isSourceEditingMode ) {
			return;
		}

		for ( const [ , domSourceEditingElementWrapper ] of this._replacedRoots ) {
			domSourceEditingElementWrapper.querySelector( 'textarea' ).readOnly = isReadOnly;
		}
	}

	/**
	 * Checks, if the plugin is allowed to handle the source editing mode by itself. Currently, the source editing mode is supported only
	 * for the {@link module:editor-classic/classiceditor~ClassicEditor classic editor}.
	 *
	 * @private
	 * @returns {Boolean}
	 */
	_isAllowedToHandleSourceEditingMode() {
		const editor = this.editor;
		const editable = editor.ui.view.editable;

		// Checks, if the editor's editable belongs to the editor's DOM tree.
		return editable && !editable._hasExternalElement;
	}
}

// Formats the content for a better readability.
//
// For a non-HTML source the unchanged input string is returned.
//
// @param {String} input Input string to check.
// @returns {Boolean}
function formatSource( input ) {
	if ( !isHtml( input ) ) {
		return input;
	}

	return formatHtml( input );
}

// Checks, if the document source is HTML. It is sufficient to just check the first character from the document data.
//
// @param {String} input Input string to check.
// @returns {Boolean}
function isHtml( input ) {
	return input.startsWith( '<' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/ui/specialcharactersnavigationview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/ui/specialcharactersnavigationview
 */




/**
 * A class representing the navigation part of the special characters UI. It is responsible
 * for describing the feature and allowing the user to select a particular character group.
 *
 * @extends module:ui/formheader/formheaderview~FormHeaderView
 */
class SpecialCharactersNavigationView extends FormHeaderView {
	/**
	 * Creates an instance of the {@link module:special-characters/ui/specialcharactersnavigationview~SpecialCharactersNavigationView}
	 * class.
	 *
	 * @param {module:utils/locale~Locale} locale The localization services instance.
	 * @param {Iterable.<String>} groupNames The names of the character groups.
	 */
	constructor( locale, groupNames ) {
		super( locale );

		const t = locale.t;

		this.set( 'class', 'ck-special-characters-navigation' );

		/**
		 * A dropdown that allows selecting a group of special characters to be displayed.
		 *
		 * @member {module:ui/dropdown/dropdownview~DropdownView}
		 */
		this.groupDropdownView = this._createGroupDropdown( groupNames );
		this.groupDropdownView.panelPosition = locale.uiLanguageDirection === 'rtl' ? 'se' : 'sw';

		/**
		 * @inheritDoc
		 */
		this.label = t( 'Special characters' );

		/**
		 * @inheritDoc
		 */
		this.children.add( this.groupDropdownView );
	}

	/**
	 * Returns the name of the character group currently selected in the {@link #groupDropdownView}.
	 *
	 * @type {String}
	 */
	get currentGroupName() {
		return this.groupDropdownView.value;
	}

	/**
	 * Focuses the character categories dropdown.
	 */
	focus() {
		this.groupDropdownView.focus();
	}

	/**
	 * Returns a dropdown that allows selecting character groups.
	 *
	 * @private
	 * @param {Iterable.<String>} groupNames The names of the character groups.
	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
	 */
	_createGroupDropdown( groupNames ) {
		const locale = this.locale;
		const t = locale.t;
		const dropdown = createDropdown( locale );
		const groupDefinitions = this._getCharacterGroupListItemDefinitions( dropdown, groupNames );

		dropdown.set( 'value', groupDefinitions.first.model.label );

		dropdown.buttonView.bind( 'label' ).to( dropdown, 'value' );

		dropdown.buttonView.set( {
			isOn: false,
			withText: true,
			tooltip: t( 'Character categories' ),
			class: [ 'ck-dropdown__button_label-width_auto' ]
		} );

		dropdown.on( 'execute', evt => {
			dropdown.value = evt.source.label;
		} );

		dropdown.delegate( 'execute' ).to( this );

		addListToDropdown( dropdown, groupDefinitions );

		return dropdown;
	}

	/**
	 * Returns list item definitions to be used in the character group dropdown
	 * representing specific character groups.
	 *
	 * @private
	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdown
	 * @param {Iterable.<String>} groupNames The names of the character groups.
	 * @returns {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>}
	 */
	_getCharacterGroupListItemDefinitions( dropdown, groupNames ) {
		const groupDefs = new Collection();

		for ( const name of groupNames ) {
			const definition = {
				type: 'button',
				model: new model_Model( {
					label: name,
					withText: true
				} )
			};

			definition.model.bind( 'isOn' ).to( dropdown, 'value', value => {
				return value === definition.model.label;
			} );

			groupDefs.add( definition );
		}

		return groupDefs;
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-special-characters/theme/charactergrid.css
var charactergrid = __webpack_require__(4046);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/theme/charactergrid.css

            

var charactergrid_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

charactergrid_options.insert = "head";
charactergrid_options.singleton = true;

var charactergrid_update = injectStylesIntoStyleTag_default()(charactergrid/* default */.Z, charactergrid_options);



/* harmony default export */ const theme_charactergrid = (charactergrid/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/ui/charactergridview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/ui/charactergridview
 */







/**
 * A grid of character tiles. It allows browsing special characters and selecting the character to
 * be inserted into the content.
 *
 * @extends module:ui/view~View
 */
class CharacterGridView extends src_view_View {
	/**
	 * Creates an instance of a character grid containing tiles representing special characters.
	 *
	 * @param {module:utils/locale~Locale} locale The localization services instance.
	 */
	constructor( locale ) {
		super( locale );

		/**
		 * A collection of the child tile views. Each tile represents a particular character.
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.tiles = this.createCollection();

		this.setTemplate( {
			tag: 'div',
			children: [
				{
					tag: 'div',
					attributes: {
						class: [
							'ck',
							'ck-character-grid__tiles'
						]
					},
					children: this.tiles
				}
			],
			attributes: {
				class: [
					'ck',
					'ck-character-grid'
				]
			}
		} );

		/**
		 * Tracks information about the DOM focus in the grid.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		addKeyboardHandlingForGrid( {
			keystrokeHandler: this.keystrokes,
			focusTracker: this.focusTracker,
			gridItems: this.tiles,
			numberOfColumns: () => dom_global.window.getComputedStyle( this.element.firstChild ) // Responsive .ck-character-grid__tiles
				.getPropertyValue( 'grid-template-columns' )
				.split( ' ' )
				.length
		} );

		/**
		 * Fired when any of {@link #tiles grid tiles} is clicked.
		 *
		 * @event execute
		 * @param {Object} data Additional information about the event.
		 * @param {String} data.name The name of the tile that caused the event (e.g. "greek small letter epsilon").
		 * @param {String} data.character A human-readable character displayed as the label (e.g. "ε").
		 */

		/**
		 * Fired when a mouse or another pointing device caused the cursor to move onto any {@link #tiles grid tile}
		 * (similar to the native `mouseover` DOM event).
		 *
		 * @event tileHover
		 * @param {Object} data Additional information about the event.
		 * @param {String} data.name The name of the tile that caused the event (e.g. "greek small letter epsilon").
		 * @param {String} data.character A human-readable character displayed as the label (e.g. "ε").
		 */
	}

	/**
	 * Creates a new tile for the grid.
	 *
	 * @param {String} character A human-readable character displayed as the label (e.g. "ε").
	 * @param {String} name The name of the character (e.g. "greek small letter epsilon").
	 * @returns {module:ui/button/buttonview~ButtonView}
	 */
	createTile( character, name ) {
		const tile = new buttonview_ButtonView( this.locale );

		tile.set( {
			label: character,
			withText: true,
			class: 'ck-character-grid__tile'
		} );

		// Labels are vital for the users to understand what character they're looking at.
		// For now we're using native title attribute for that, see #5817.
		tile.extendTemplate( {
			attributes: {
				title: name
			},
			on: {
				mouseover: tile.bindTemplate.to( 'mouseover' )
			}
		} );

		tile.on( 'mouseover', () => {
			this.fire( 'tileHover', { name, character } );
		} );

		tile.on( 'execute', () => {
			this.fire( 'execute', { name, character } );
		} );

		return tile;
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		for ( const item of this.tiles ) {
			this.focusTracker.add( item.element );
		}

		this.tiles.on( 'change', ( eventInfo, { added, removed } ) => {
			if ( added.length > 0 ) {
				for ( const item of added ) {
					this.focusTracker.add( item.element );
				}
			}
			if ( removed.length > 0 ) {
				for ( const item of removed ) {
					this.focusTracker.remove( item.element );
				}
			}
		} );

		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.keystrokes.destroy();
	}

	/**
	 * Focuses the first focusable in {@link #tiles}.
	 */
	focus() {
		this.tiles.get( 0 ).focus();
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-special-characters/theme/characterinfo.css
var characterinfo = __webpack_require__(4779);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/theme/characterinfo.css

            

var characterinfo_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

characterinfo_options.insert = "head";
characterinfo_options.singleton = true;

var characterinfo_update = injectStylesIntoStyleTag_default()(characterinfo/* default */.Z, characterinfo_options);



/* harmony default export */ const theme_characterinfo = (characterinfo/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/ui/characterinfoview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/ui/characterinfoview
 */





/**
 * The view displaying detailed information about a special character glyph, e.g. upon
 * hovering it with a mouse.
 *
 * @extends module:ui/view~View
 */
class CharacterInfoView extends src_view_View {
	constructor( locale ) {
		super( locale );

		const bind = this.bindTemplate;

		/**
		 * The character whose information is displayed by the view. For instance,
		 * "∑" or "¿".
		 *
		 * @observable
		 * @member {String|null} #character
		 */
		this.set( 'character', null );

		/**
		 * The name of the {@link #character}. For instance,
		 * "N-ary summation" or "Inverted question mark".
		 *
		 * @observable
		 * @member {String|null} #name
		 */
		this.set( 'name', null );

		/**
		 * The "Unicode string" of the {@link #character}. For instance,
		 * "U+0061".
		 *
		 * @observable
		 * @readonly
		 * @member {String} #code
		 */
		this.bind( 'code' ).to( this, 'character', characterToUnicodeString );

		this.setTemplate( {
			tag: 'div',
			children: [
				{
					tag: 'span',
					attributes: {
						class: [
							'ck-character-info__name'
						]
					},
					children: [
						{
							// Note: ZWSP to prevent vertical collapsing.
							text: bind.to( 'name', name => name ? name : '\u200B' )
						}
					]
				},
				{
					tag: 'span',
					attributes: {
						class: [
							'ck-character-info__code'
						]
					},
					children: [
						{
							text: bind.to( 'code' )
						}
					]
				}
			],
			attributes: {
				class: [
					'ck',
					'ck-character-info'
				]
			}
		} );
	}
}

// Converts a character into a "Unicode string", for instance:
//
//	"$" -> "U+0024"
//
// Returns an empty string when the character is `null`.
//
// @param {String} character
// @returns {String}
function characterToUnicodeString( character ) {
	if ( character === null ) {
		return '';
	}

	const hexCode = character.codePointAt( 0 ).toString( 16 );

	return 'U+' + ( '0000' + hexCode ).slice( -4 );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/ui/specialcharactersview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/ui/specialcharactersview
 */




/**
 * A view that glues pieces of the special characters dropdown panel together:
 *
 * * the navigation view (allows selecting the category),
 * * the grid view (displays characters as a grid),
 * * and the info view (displays detailed info about a specific character).
 *
 * @extends module:ui/view~View
 */
class SpecialCharactersView extends src_view_View {
	/**
	 * Creates an instance of the `SpecialCharactersView`.
	 *
	 * @param {module:utils/locale~Locale} locale The localization services instance.
	 * @param {module:special-characters/ui/specialcharactersnavigationview~SpecialCharactersNavigationView} navigationView
	 * @param {module:special-characters/ui/charactergridview~CharacterGridView} gridView
	 * @param {module:special-characters/ui/characterinfoview~CharacterInfoView} infoView
	 */
	constructor( locale, navigationView, gridView, infoView ) {
		super( locale );

		/**
		 * A collection of the focusable children of the view.
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.items = this.createCollection();

		/**
		 * Tracks information about the DOM focus in the view.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * Helps cycling over focusable {@link #items} in the view.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this.items,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				focusPrevious: 'shift + tab',
				focusNext: 'tab'
			}
		} );

		/**
		 * An instance of the `SpecialCharactersNavigationView`.
		 *
		 * @member {module:special-characters/ui/specialcharactersnavigationview~SpecialCharactersNavigationView}
		 */
		this.navigationView = navigationView;

		/**
		 * An instance of the `CharacterGridView`.
		 *
		 * @member {module:special-characters/ui/charactergridview~CharacterGridView}
		 */
		this.gridView = gridView;

		/**
		 * An instance of the `CharacterInfoView`.
		 *
		 * @member {module:special-characters/ui/characterinfoview~CharacterInfoView}
		 */
		this.infoView = infoView;

		this.setTemplate( {
			tag: 'div',
			children: [
				this.navigationView,
				this.gridView,
				this.infoView
			],
			attributes: {
				// Avoid focus loss when the user clicks the area of the grid that is not a button.
				// https://github.com/ckeditor/ckeditor5/pull/12319#issuecomment-1231779819
				tabindex: '-1'
			}
		} );

		this.items.add( this.navigationView.groupDropdownView.buttonView );
		this.items.add( this.gridView );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		this.focusTracker.add( this.navigationView.groupDropdownView.buttonView.element );
		this.focusTracker.add( this.gridView.element );

		// Start listening for the keystrokes coming from #element.
		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Focuses the first focusable in {@link #items}.
	 */
	focus() {
		this.navigationView.focus();
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/theme/icons/specialcharacters.svg
/* harmony default export */ const specialcharacters = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10 2.5a7.47 7.47 0 0 1 4.231 1.31 7.268 7.268 0 0 1 2.703 3.454 7.128 7.128 0 0 1 .199 4.353c-.39 1.436-1.475 2.72-2.633 3.677h2.013c0-.226.092-.443.254-.603a.876.876 0 0 1 1.229 0c.163.16.254.377.254.603v.853c0 .209-.078.41-.22.567a.873.873 0 0 1-.547.28l-.101.006h-4.695a.517.517 0 0 1-.516-.518v-1.265c0-.21.128-.398.317-.489a5.601 5.601 0 0 0 2.492-2.371 5.459 5.459 0 0 0 .552-3.693 5.53 5.53 0 0 0-1.955-3.2A5.71 5.71 0 0 0 10 4.206 5.708 5.708 0 0 0 6.419 5.46 5.527 5.527 0 0 0 4.46 8.663a5.457 5.457 0 0 0 .554 3.695 5.6 5.6 0 0 0 2.497 2.37.55.55 0 0 1 .317.49v1.264c0 .286-.23.518-.516.518H2.618a.877.877 0 0 1-.614-.25.845.845 0 0 1-.254-.603v-.853c0-.226.091-.443.254-.603a.876.876 0 0 1 1.228 0c.163.16.255.377.255.603h1.925c-1.158-.958-2.155-2.241-2.545-3.678a7.128 7.128 0 0 1 .199-4.352 7.268 7.268 0 0 1 2.703-3.455A7.475 7.475 0 0 1 10 2.5z\"/></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-special-characters/theme/specialcharacters.css
var theme_specialcharacters = __webpack_require__(8170);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/theme/specialcharacters.css

            

var specialcharacters_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

specialcharacters_options.insert = "head";
specialcharacters_options.singleton = true;

var specialcharacters_update = injectStylesIntoStyleTag_default()(theme_specialcharacters/* default */.Z, specialcharacters_options);



/* harmony default export */ const ckeditor5_special_characters_theme_specialcharacters = (theme_specialcharacters/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/specialcharacters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/specialcharacters
 */













const ALL_SPECIAL_CHARACTERS_GROUP = 'All';

/**
 * The special characters feature.
 *
 * Introduces the `'specialCharacters'` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class SpecialCharacters extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ Typing ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SpecialCharacters';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * Registered characters. A pair of a character name and its symbol.
		 *
		 * @private
		 * @member {Map.<String, String>} #_characters
		 */
		this._characters = new Map();

		/**
		 * Registered groups. Each group contains a collection with symbol names.
		 *
		 * @private
		 * @member {Map.<String, Set.<String>>} #_groups
		 */
		this._groups = new Map();
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		const inputCommand = editor.commands.get( 'input' );

		// Add the `specialCharacters` dropdown button to feature components.
		editor.ui.componentFactory.add( 'specialCharacters', locale => {
			const dropdownView = createDropdown( locale );
			let dropdownPanelContent;

			dropdownView.buttonView.set( {
				label: t( 'Special characters' ),
				icon: specialcharacters,
				tooltip: true
			} );

			dropdownView.bind( 'isEnabled' ).to( inputCommand );

			// Insert a special character when a tile was clicked.
			dropdownView.on( 'execute', ( evt, data ) => {
				editor.execute( 'insertText', { text: data.character } );
				editor.editing.view.focus();
			} );

			dropdownView.on( 'change:isOpen', () => {
				if ( !dropdownPanelContent ) {
					dropdownPanelContent = this._createDropdownPanelContent( locale, dropdownView );

					const specialCharactersView = new SpecialCharactersView(
						locale,
						dropdownPanelContent.navigationView,
						dropdownPanelContent.gridView,
						dropdownPanelContent.infoView
					);

					dropdownView.panelView.children.add( specialCharactersView );
				}

				dropdownPanelContent.infoView.set( {
					character: null,
					name: null
				} );
			} );

			return dropdownView;
		} );
	}

	/**
	 * Adds a collection of special characters to the specified group. The title of a special character must be unique.
	 *
	 * **Note:** The "All" category name is reserved by the plugin and cannot be used as a new name for a special
	 * characters category.
	 *
	 * @param {String} groupName
	 * @param {Array.<module:special-characters/specialcharacters~SpecialCharacterDefinition>} items
	 */
	addItems( groupName, items ) {
		if ( groupName === ALL_SPECIAL_CHARACTERS_GROUP ) {
			/**
			 * The name "All" for a special category group cannot be used because it is a special category that displays all
			 * available special characters.
			 *
			 * @error special-character-invalid-group-name
			 */
			throw new CKEditorError(
				`special-character-invalid-group-name: The name "${ ALL_SPECIAL_CHARACTERS_GROUP }" is reserved and cannot be used.`
			);
		}

		const group = this._getGroup( groupName );

		for ( const item of items ) {
			group.add( item.title );
			this._characters.set( item.title, item.character );
		}
	}

	/**
	 * Returns an iterator of special characters groups.
	 *
	 * @returns {Iterable.<String>}
	 */
	getGroups() {
		return this._groups.keys();
	}

	/**
	 * Returns a collection of special characters symbol names (titles).
	 *
	 * @param {String} groupName
	 * @returns {Set.<String>|undefined}
	 */
	getCharactersForGroup( groupName ) {
		if ( groupName === ALL_SPECIAL_CHARACTERS_GROUP ) {
			return new Set( this._characters.keys() );
		}

		return this._groups.get( groupName );
	}

	/**
	 * Returns the symbol of a special character for the specified name. If the special character could not be found, `undefined`
	 * is returned.
	 *
	 * @param {String} title The title of a special character.
	 * @returns {String|undefined}
	 */
	getCharacter( title ) {
		return this._characters.get( title );
	}

	/**
	 * Returns a group of special characters. If the group with the specified name does not exist, it will be created.
	 *
	 * @private
	 * @param {String} groupName The name of the group to create.
	 */
	_getGroup( groupName ) {
		if ( !this._groups.has( groupName ) ) {
			this._groups.set( groupName, new Set() );
		}

		return this._groups.get( groupName );
	}

	/**
	 * Updates the symbol grid depending on the currently selected character group.
	 *
	 * @private
	 * @param {String} currentGroupName
	 * @param {module:special-characters/ui/charactergridview~CharacterGridView} gridView
	 */
	_updateGrid( currentGroupName, gridView ) {
		// Updating the grid starts with removing all tiles belonging to the old group.
		gridView.tiles.clear();

		const characterTitles = this.getCharactersForGroup( currentGroupName );

		for ( const title of characterTitles ) {
			const character = this.getCharacter( title );

			gridView.tiles.add( gridView.createTile( character, title ) );
		}
	}

	/**
	 * Initializes the dropdown, used for lazy loading.
	 *
	 * @private
	 * @param {module:utils/locale~Locale} locale
	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
	 * @returns {Object} Returns an object with `navigationView`, `gridView` and `infoView` properties, containing UI parts.
	 */
	_createDropdownPanelContent( locale, dropdownView ) {
		const specialCharsGroups = [ ...this.getGroups() ];

		// Add a special group that shows all available special characters.
		specialCharsGroups.unshift( ALL_SPECIAL_CHARACTERS_GROUP );

		const navigationView = new SpecialCharactersNavigationView( locale, specialCharsGroups );
		const gridView = new CharacterGridView( locale );
		const infoView = new CharacterInfoView( locale );

		gridView.delegate( 'execute' ).to( dropdownView );

		gridView.on( 'tileHover', ( evt, data ) => {
			infoView.set( data );
		} );

		// Update the grid of special characters when a user changed the character group.
		navigationView.on( 'execute', () => {
			this._updateGrid( navigationView.currentGroupName, gridView );
		} );

		// Set the initial content of the special characters grid.
		this._updateGrid( navigationView.currentGroupName, gridView );

		return { navigationView, gridView, infoView };
	}
}

/**
 * @typedef {Object} module:special-characters/specialcharacters~SpecialCharacterDefinition
 *
 * @property {String} title A unique name of the character (e.g. "greek small letter epsilon").
 * @property {String} character A human-readable character displayed as the label (e.g. "ε").
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/specialcharactersarrows.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/specialcharactersarrows
 */



/**
 * A plugin that provides special characters for the "Arrows" category.
 *
 *		ClassicEditor
 *			.create( {
 *				plugins: [ ..., SpecialCharacters, SpecialCharactersArrows ],
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @extends module:core/plugin~Plugin
 */
class SpecialCharactersArrows extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SpecialCharactersArrows';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.plugins.get( 'SpecialCharacters' ).addItems( 'Arrows', [
			{ title: t( 'leftwards simple arrow' ), character: '←' },
			{ title: t( 'rightwards simple arrow' ), character: '→' },
			{ title: t( 'upwards simple arrow' ), character: '↑' },
			{ title: t( 'downwards simple arrow' ), character: '↓' },
			{ title: t( 'leftwards double arrow' ), character: '⇐' },
			{ title: t( 'rightwards double arrow' ), character: '⇒' },
			{ title: t( 'upwards double arrow' ), character: '⇑' },
			{ title: t( 'downwards double arrow' ), character: '⇓' },
			{ title: t( 'leftwards dashed arrow' ), character: '⇠' },
			{ title: t( 'rightwards dashed arrow' ), character: '⇢' },
			{ title: t( 'upwards dashed arrow' ), character: '⇡' },
			{ title: t( 'downwards dashed arrow' ), character: '⇣' },
			{ title: t( 'leftwards arrow to bar' ), character: '⇤' },
			{ title: t( 'rightwards arrow to bar' ), character: '⇥' },
			{ title: t( 'upwards arrow to bar' ), character: '⤒' },
			{ title: t( 'downwards arrow to bar' ), character: '⤓' },
			{ title: t( 'up down arrow with base' ), character: '↨' },
			{ title: t( 'back with leftwards arrow above' ), character: '🔙' },
			{ title: t( 'end with leftwards arrow above' ), character: '🔚' },
			{ title: t( 'on with exclamation mark with left right arrow above' ), character: '🔛' },
			{ title: t( 'soon with rightwards arrow above' ), character: '🔜' },
			{ title: t( 'top with upwards arrow above' ), character: '🔝' }
		] );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/specialcharacterscurrency.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/specialcharacterscurrency
 */



/**
 * A plugin that provides special characters for the "Currency" category.
 *
 *		ClassicEditor
 *			.create( {
 *				plugins: [ ..., SpecialCharacters, SpecialCharactersCurrency ],
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @extends module:core/plugin~Plugin
 */
class SpecialCharactersCurrency extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SpecialCharactersCurrency';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.plugins.get( 'SpecialCharacters' ).addItems( 'Currency', [
			{ character: '$', title: t( 'Dollar sign' ) },
			{ character: '€', title: t( 'Euro sign' ) },
			{ character: '¥', title: t( 'Yen sign' ) },
			{ character: '£', title: t( 'Pound sign' ) },
			{ character: '¢', title: t( 'Cent sign' ) },
			{ character: '₠', title: t( 'Euro-currency sign' ) },
			{ character: '₡', title: t( 'Colon sign' ) },
			{ character: '₢', title: t( 'Cruzeiro sign' ) },
			{ character: '₣', title: t( 'French franc sign' ) },
			{ character: '₤', title: t( 'Lira sign' ) },
			{ character: '¤', title: t( 'Currency sign' ) },
			{ character: '₿', title: t( 'Bitcoin sign' ) },
			{ character: '₥', title: t( 'Mill sign' ) },
			{ character: '₦', title: t( 'Naira sign' ) },
			{ character: '₧', title: t( 'Peseta sign' ) },
			{ character: '₨', title: t( 'Rupee sign' ) },
			{ character: '₩', title: t( 'Won sign' ) },
			{ character: '₪', title: t( 'New sheqel sign' ) },
			{ character: '₫', title: t( 'Dong sign' ) },
			{ character: '₭', title: t( 'Kip sign' ) },
			{ character: '₮', title: t( 'Tugrik sign' ) },
			{ character: '₯', title: t( 'Drachma sign' ) },
			{ character: '₰', title: t( 'German penny sign' ) },
			{ character: '₱', title: t( 'Peso sign' ) },
			{ character: '₲', title: t( 'Guarani sign' ) },
			{ character: '₳', title: t( 'Austral sign' ) },
			{ character: '₴', title: t( 'Hryvnia sign' ) },
			{ character: '₵', title: t( 'Cedi sign' ) },
			{ character: '₶', title: t( 'Livre tournois sign' ) },
			{ character: '₷', title: t( 'Spesmilo sign' ) },
			{ character: '₸', title: t( 'Tenge sign' ) },
			{ character: '₹', title: t( 'Indian rupee sign' ) },
			{ character: '₺', title: t( 'Turkish lira sign' ) },
			{ character: '₻', title: t( 'Nordic mark sign' ) },
			{ character: '₼', title: t( 'Manat sign' ) },
			{ character: '₽', title: t( 'Ruble sign' ) }
		] );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/specialcharactersmathematical.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/specialcharactersmathematical
 */



/**
 * A plugin that provides special characters for the "Mathematical" category.
 *
 *		ClassicEditor
 *			.create( {
 *				plugins: [ ..., SpecialCharacters, SpecialCharactersMathematical ],
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @extends module:core/plugin~Plugin
 */
class SpecialCharactersMathematical extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SpecialCharactersMathematical';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.plugins.get( 'SpecialCharacters' ).addItems( 'Mathematical', [
			{ character: '<', title: t( 'Less-than sign' ) },
			{ character: '>', title: t( 'Greater-than sign' ) },
			{ character: '≤', title: t( 'Less-than or equal to' ) },
			{ character: '≥', title: t( 'Greater-than or equal to' ) },
			{ character: '–', title: t( 'En dash' ) },
			{ character: '—', title: t( 'Em dash' ) },
			{ character: '¯', title: t( 'Macron' ) },
			{ character: '‾', title: t( 'Overline' ) },
			{ character: '°', title: t( 'Degree sign' ) },
			{ character: '−', title: t( 'Minus sign' ) },
			{ character: '±', title: t( 'Plus-minus sign' ) },
			{ character: '÷', title: t( 'Division sign' ) },
			{ character: '⁄', title: t( 'Fraction slash' ) },
			{ character: '×', title: t( 'Multiplication sign' ) },
			{ character: 'ƒ', title: t( 'Latin small letter f with hook' ) },
			{ character: '∫', title: t( 'Integral' ) },
			{ character: '∑', title: t( 'N-ary summation' ) },
			{ character: '∞', title: t( 'Infinity' ) },
			{ character: '√', title: t( 'Square root' ) },
			{ character: '∼', title: t( 'Tilde operator' ) },
			{ character: '≅', title: t( 'Approximately equal to' ) },
			{ character: '≈', title: t( 'Almost equal to' ) },
			{ character: '≠', title: t( 'Not equal to' ) },
			{ character: '≡', title: t( 'Identical to' ) },
			{ character: '∈', title: t( 'Element of' ) },
			{ character: '∉', title: t( 'Not an element of' ) },
			{ character: '∋', title: t( 'Contains as member' ) },
			{ character: '∏', title: t( 'N-ary product' ) },
			{ character: '∧', title: t( 'Logical and' ) },
			{ character: '∨', title: t( 'Logical or' ) },
			{ character: '¬', title: t( 'Not sign' ) },
			{ character: '∩', title: t( 'Intersection' ) },
			{ character: '∪', title: t( 'Union' ) },
			{ character: '∂', title: t( 'Partial differential' ) },
			{ character: '∀', title: t( 'For all' ) },
			{ character: '∃', title: t( 'There exists' ) },
			{ character: '∅', title: t( 'Empty set' ) },
			{ character: '∇', title: t( 'Nabla' ) },
			{ character: '∗', title: t( 'Asterisk operator' ) },
			{ character: '∝', title: t( 'Proportional to' ) },
			{ character: '∠', title: t( 'Angle' ) },
			{ character: '¼', title: t( 'Vulgar fraction one quarter' ) },
			{ character: '½', title: t( 'Vulgar fraction one half' ) },
			{ character: '¾', title: t( 'Vulgar fraction three quarters' ) }
		] );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/specialcharacterslatin.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/specialcharacterslatin
 */



/**
 * A plugin that provides special characters for the "Latin" category.
 *
 *		ClassicEditor
 *			.create( {
 *				plugins: [ ..., SpecialCharacters, SpecialCharactersLatin ],
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @extends module:core/plugin~Plugin
 */
class SpecialCharactersLatin extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SpecialCharactersLatin';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.plugins.get( 'SpecialCharacters' ).addItems( 'Latin', [
			{ character: 'Ā', title: t( 'Latin capital letter a with macron' ) },
			{ character: 'ā', title: t( 'Latin small letter a with macron' ) },
			{ character: 'Ă', title: t( 'Latin capital letter a with breve' ) },
			{ character: 'ă', title: t( 'Latin small letter a with breve' ) },
			{ character: 'Ą', title: t( 'Latin capital letter a with ogonek' ) },
			{ character: 'ą', title: t( 'Latin small letter a with ogonek' ) },
			{ character: 'Ć', title: t( 'Latin capital letter c with acute' ) },
			{ character: 'ć', title: t( 'Latin small letter c with acute' ) },
			{ character: 'Ĉ', title: t( 'Latin capital letter c with circumflex' ) },
			{ character: 'ĉ', title: t( 'Latin small letter c with circumflex' ) },
			{ character: 'Ċ', title: t( 'Latin capital letter c with dot above' ) },
			{ character: 'ċ', title: t( 'Latin small letter c with dot above' ) },
			{ character: 'Č', title: t( 'Latin capital letter c with caron' ) },
			{ character: 'č', title: t( 'Latin small letter c with caron' ) },
			{ character: 'Ď', title: t( 'Latin capital letter d with caron' ) },
			{ character: 'ď', title: t( 'Latin small letter d with caron' ) },
			{ character: 'Đ', title: t( 'Latin capital letter d with stroke' ) },
			{ character: 'đ', title: t( 'Latin small letter d with stroke' ) },
			{ character: 'Ē', title: t( 'Latin capital letter e with macron' ) },
			{ character: 'ē', title: t( 'Latin small letter e with macron' ) },
			{ character: 'Ĕ', title: t( 'Latin capital letter e with breve' ) },
			{ character: 'ĕ', title: t( 'Latin small letter e with breve' ) },
			{ character: 'Ė', title: t( 'Latin capital letter e with dot above' ) },
			{ character: 'ė', title: t( 'Latin small letter e with dot above' ) },
			{ character: 'Ę', title: t( 'Latin capital letter e with ogonek' ) },
			{ character: 'ę', title: t( 'Latin small letter e with ogonek' ) },
			{ character: 'Ě', title: t( 'Latin capital letter e with caron' ) },
			{ character: 'ě', title: t( 'Latin small letter e with caron' ) },
			{ character: 'Ĝ', title: t( 'Latin capital letter g with circumflex' ) },
			{ character: 'ĝ', title: t( 'Latin small letter g with circumflex' ) },
			{ character: 'Ğ', title: t( 'Latin capital letter g with breve' ) },
			{ character: 'ğ', title: t( 'Latin small letter g with breve' ) },
			{ character: 'Ġ', title: t( 'Latin capital letter g with dot above' ) },
			{ character: 'ġ', title: t( 'Latin small letter g with dot above' ) },
			{ character: 'Ģ', title: t( 'Latin capital letter g with cedilla' ) },
			{ character: 'ģ', title: t( 'Latin small letter g with cedilla' ) },
			{ character: 'Ĥ', title: t( 'Latin capital letter h with circumflex' ) },
			{ character: 'ĥ', title: t( 'Latin small letter h with circumflex' ) },
			{ character: 'Ħ', title: t( 'Latin capital letter h with stroke' ) },
			{ character: 'ħ', title: t( 'Latin small letter h with stroke' ) },
			{ character: 'Ĩ', title: t( 'Latin capital letter i with tilde' ) },
			{ character: 'ĩ', title: t( 'Latin small letter i with tilde' ) },
			{ character: 'Ī', title: t( 'Latin capital letter i with macron' ) },
			{ character: 'ī', title: t( 'Latin small letter i with macron' ) },
			{ character: 'Ĭ', title: t( 'Latin capital letter i with breve' ) },
			{ character: 'ĭ', title: t( 'Latin small letter i with breve' ) },
			{ character: 'Į', title: t( 'Latin capital letter i with ogonek' ) },
			{ character: 'į', title: t( 'Latin small letter i with ogonek' ) },
			{ character: 'İ', title: t( 'Latin capital letter i with dot above' ) },
			{ character: 'ı', title: t( 'Latin small letter dotless i' ) },
			{ character: 'Ĳ', title: t( 'Latin capital ligature ij' ) },
			{ character: 'ĳ', title: t( 'Latin small ligature ij' ) },
			{ character: 'Ĵ', title: t( 'Latin capital letter j with circumflex' ) },
			{ character: 'ĵ', title: t( 'Latin small letter j with circumflex' ) },
			{ character: 'Ķ', title: t( 'Latin capital letter k with cedilla' ) },
			{ character: 'ķ', title: t( 'Latin small letter k with cedilla' ) },
			{ character: 'ĸ', title: t( 'Latin small letter kra' ) },
			{ character: 'Ĺ', title: t( 'Latin capital letter l with acute' ) },
			{ character: 'ĺ', title: t( 'Latin small letter l with acute' ) },
			{ character: 'Ļ', title: t( 'Latin capital letter l with cedilla' ) },
			{ character: 'ļ', title: t( 'Latin small letter l with cedilla' ) },
			{ character: 'Ľ', title: t( 'Latin capital letter l with caron' ) },
			{ character: 'ľ', title: t( 'Latin small letter l with caron' ) },
			{ character: 'Ŀ', title: t( 'Latin capital letter l with middle dot' ) },
			{ character: 'ŀ', title: t( 'Latin small letter l with middle dot' ) },
			{ character: 'Ł', title: t( 'Latin capital letter l with stroke' ) },
			{ character: 'ł', title: t( 'Latin small letter l with stroke' ) },
			{ character: 'Ń', title: t( 'Latin capital letter n with acute' ) },
			{ character: 'ń', title: t( 'Latin small letter n with acute' ) },
			{ character: 'Ņ', title: t( 'Latin capital letter n with cedilla' ) },
			{ character: 'ņ', title: t( 'Latin small letter n with cedilla' ) },
			{ character: 'Ň', title: t( 'Latin capital letter n with caron' ) },
			{ character: 'ň', title: t( 'Latin small letter n with caron' ) },
			{ character: 'ŉ', title: t( 'Latin small letter n preceded by apostrophe' ) },
			{ character: 'Ŋ', title: t( 'Latin capital letter eng' ) },
			{ character: 'ŋ', title: t( 'Latin small letter eng' ) },
			{ character: 'Ō', title: t( 'Latin capital letter o with macron' ) },
			{ character: 'ō', title: t( 'Latin small letter o with macron' ) },
			{ character: 'Ŏ', title: t( 'Latin capital letter o with breve' ) },
			{ character: 'ŏ', title: t( 'Latin small letter o with breve' ) },
			{ character: 'Ő', title: t( 'Latin capital letter o with double acute' ) },
			{ character: 'ő', title: t( 'Latin small letter o with double acute' ) },
			{ character: 'Œ', title: t( 'Latin capital ligature oe' ) },
			{ character: 'œ', title: t( 'Latin small ligature oe' ) },
			{ character: 'Ŕ', title: t( 'Latin capital letter r with acute' ) },
			{ character: 'ŕ', title: t( 'Latin small letter r with acute' ) },
			{ character: 'Ŗ', title: t( 'Latin capital letter r with cedilla' ) },
			{ character: 'ŗ', title: t( 'Latin small letter r with cedilla' ) },
			{ character: 'Ř', title: t( 'Latin capital letter r with caron' ) },
			{ character: 'ř', title: t( 'Latin small letter r with caron' ) },
			{ character: 'Ś', title: t( 'Latin capital letter s with acute' ) },
			{ character: 'ś', title: t( 'Latin small letter s with acute' ) },
			{ character: 'Ŝ', title: t( 'Latin capital letter s with circumflex' ) },
			{ character: 'ŝ', title: t( 'Latin small letter s with circumflex' ) },
			{ character: 'Ş', title: t( 'Latin capital letter s with cedilla' ) },
			{ character: 'ş', title: t( 'Latin small letter s with cedilla' ) },
			{ character: 'Š', title: t( 'Latin capital letter s with caron' ) },
			{ character: 'š', title: t( 'Latin small letter s with caron' ) },
			{ character: 'Ţ', title: t( 'Latin capital letter t with cedilla' ) },
			{ character: 'ţ', title: t( 'Latin small letter t with cedilla' ) },
			{ character: 'Ť', title: t( 'Latin capital letter t with caron' ) },
			{ character: 'ť', title: t( 'Latin small letter t with caron' ) },
			{ character: 'Ŧ', title: t( 'Latin capital letter t with stroke' ) },
			{ character: 'ŧ', title: t( 'Latin small letter t with stroke' ) },
			{ character: 'Ũ', title: t( 'Latin capital letter u with tilde' ) },
			{ character: 'ũ', title: t( 'Latin small letter u with tilde' ) },
			{ character: 'Ū', title: t( 'Latin capital letter u with macron' ) },
			{ character: 'ū', title: t( 'Latin small letter u with macron' ) },
			{ character: 'Ŭ', title: t( 'Latin capital letter u with breve' ) },
			{ character: 'ŭ', title: t( 'Latin small letter u with breve' ) },
			{ character: 'Ů', title: t( 'Latin capital letter u with ring above' ) },
			{ character: 'ů', title: t( 'Latin small letter u with ring above' ) },
			{ character: 'Ű', title: t( 'Latin capital letter u with double acute' ) },
			{ character: 'ű', title: t( 'Latin small letter u with double acute' ) },
			{ character: 'Ų', title: t( 'Latin capital letter u with ogonek' ) },
			{ character: 'ų', title: t( 'Latin small letter u with ogonek' ) },
			{ character: 'Ŵ', title: t( 'Latin capital letter w with circumflex' ) },
			{ character: 'ŵ', title: t( 'Latin small letter w with circumflex' ) },
			{ character: 'Ŷ', title: t( 'Latin capital letter y with circumflex' ) },
			{ character: 'ŷ', title: t( 'Latin small letter y with circumflex' ) },
			{ character: 'Ÿ', title: t( 'Latin capital letter y with diaeresis' ) },
			{ character: 'Ź', title: t( 'Latin capital letter z with acute' ) },
			{ character: 'ź', title: t( 'Latin small letter z with acute' ) },
			{ character: 'Ż', title: t( 'Latin capital letter z with dot above' ) },
			{ character: 'ż', title: t( 'Latin small letter z with dot above' ) },
			{ character: 'Ž', title: t( 'Latin capital letter z with caron' ) },
			{ character: 'ž', title: t( 'Latin small letter z with caron' ) },
			{ character: 'ſ', title: t( 'Latin small letter long s' ) }
		] );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/specialcharacterstext.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/specialcharacterstext
 */



/**
 * A plugin that provides special characters for the "Text" category.
 *
 *		ClassicEditor
 *			.create( {
 *				plugins: [ ..., SpecialCharacters, SpecialCharactersText ],
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @extends module:core/plugin~Plugin
 */
class SpecialCharactersText extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SpecialCharactersText';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.plugins.get( 'SpecialCharacters' ).addItems( 'Text', [
			{ character: '‹', title: t( 'Single left-pointing angle quotation mark' ) },
			{ character: '›', title: t( 'Single right-pointing angle quotation mark' ) },
			{ character: '«', title: t( 'Left-pointing double angle quotation mark' ) },
			{ character: '»', title: t( 'Right-pointing double angle quotation mark' ) },
			{ character: '‘', title: t( 'Left single quotation mark' ) },
			{ character: '’', title: t( 'Right single quotation mark' ) },
			{ character: '“', title: t( 'Left double quotation mark' ) },
			{ character: '”', title: t( 'Right double quotation mark' ) },
			{ character: '‚', title: t( 'Single low-9 quotation mark' ) },
			{ character: '„', title: t( 'Double low-9 quotation mark' ) },
			{ character: '¡', title: t( 'Inverted exclamation mark' ) },
			{ character: '¿', title: t( 'Inverted question mark' ) },
			{ character: '‥', title: t( 'Two dot leader' ) },
			{ character: '…', title: t( 'Horizontal ellipsis' ) },
			{ character: '‡', title: t( 'Double dagger' ) },
			{ character: '‰', title: t( 'Per mille sign' ) },
			{ character: '‱', title: t( 'Per ten thousand sign' ) },
			{ character: '‼', title: t( 'Double exclamation mark' ) },
			{ character: '⁈', title: t( 'Question exclamation mark' ) },
			{ character: '⁉', title: t( 'Exclamation question mark' ) },
			{ character: '⁇', title: t( 'Double question mark' ) },
			{ character: '©', title: t( 'Copyright sign' ) },
			{ character: '®', title: t( 'Registered sign' ) },
			{ character: '™', title: t( 'Trade mark sign' ) },
			{ character: '§', title: t( 'Section sign' ) },
			{ character: '¶', title: t( 'Paragraph sign' ) },
			{ character: '⁋', title: t( 'Reversed paragraph sign' ) }
		] );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-special-characters/src/specialcharactersessentials.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module special-characters/specialcharactersessentials
 */









/**
 * A plugin combining a basic set of characters for the special characters plugin.
 *
 *		ClassicEditor
 *			.create( {
 *				plugins: [ ..., SpecialCharacters, SpecialCharactersEssentials ],
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * @extends module:core/plugin~Plugin
 */
class SpecialCharactersEssentials extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [
			SpecialCharactersCurrency,
			SpecialCharactersText,
			SpecialCharactersMathematical,
			SpecialCharactersArrows,
			SpecialCharactersLatin
		];
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-restricted-editing/src/restrictededitingexceptioncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module restricted-editing/restrictededitingexceptioncommand
 */



/**
 * @extends module:core/command~Command
 */
class RestrictedEditingExceptionCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const doc = model.document;

		this.value = !!doc.selection.getAttribute( 'restrictedEditingException' );

		this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'restrictedEditingException' );
	}

	/**
	 * @inheritDoc
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;
		const selection = document.selection;
		const valueToSet = ( options.forceValue === undefined ) ? !this.value : options.forceValue;

		model.change( writer => {
			const ranges = model.schema.getValidRanges( selection.getRanges(), 'restrictedEditingException' );

			if ( selection.isCollapsed ) {
				if ( valueToSet ) {
					writer.setSelectionAttribute( 'restrictedEditingException', valueToSet );
				} else {
					const isSameException = value => value.item.getAttribute( 'restrictedEditingException' ) === this.value;
					const exceptionStart = selection.focus.getLastMatchingPosition( isSameException, { direction: 'backward' } );
					const exceptionEnd = selection.focus.getLastMatchingPosition( isSameException );
					const focus = selection.focus;

					writer.removeSelectionAttribute( 'restrictedEditingException' );

					if ( !( focus.isEqual( exceptionStart ) || focus.isEqual( exceptionEnd ) ) ) {
						writer.removeAttribute( 'restrictedEditingException', writer.createRange( exceptionStart, exceptionEnd ) );
					}
				}
			} else {
				for ( const range of ranges ) {
					if ( valueToSet ) {
						writer.setAttribute( 'restrictedEditingException', valueToSet, range );
					} else {
						writer.removeAttribute( 'restrictedEditingException', range );
					}
				}
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-restricted-editing/src/standardeditingmodeediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module restricted-editing/standardeditingmodeediting
 */





/**
 * The standard editing mode editing feature.
 *
 * * It introduces the `restrictedEditingException` text attribute that is rendered as
 * a `<span>` element with the `restricted-editing-exception` CSS class.
 * * It registers the `'restrictedEditingException'` command.
 *
 * @extends module:core/plugin~Plugin
 */
class StandardEditingModeEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'StandardEditingModeEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		editor.model.schema.extend( '$text', { allowAttributes: [ 'restrictedEditingException' ] } );

		editor.conversion.for( 'upcast' ).elementToAttribute( {
			model: 'restrictedEditingException',
			view: {
				name: 'span',
				classes: 'restricted-editing-exception'
			}
		} );

		editor.conversion.for( 'downcast' ).attributeToElement( {
			model: 'restrictedEditingException',
			view: ( modelAttributeValue, { writer } ) => {
				if ( modelAttributeValue ) {
					// Make the restricted editing <span> outer-most in the view.
					return writer.createAttributeElement( 'span', { class: 'restricted-editing-exception' }, { priority: -10 } );
				}
			}
		} );

		editor.commands.add( 'restrictedEditingException', new RestrictedEditingExceptionCommand( editor ) );

		editor.editing.view.change( writer => {
			for ( const root of editor.editing.view.document.roots ) {
				writer.addClass( 'ck-restricted-editing_mode_standard', root );
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-restricted-editing/theme/icons/contentunlock.svg
/* harmony default export */ const contentunlock = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6.25 16a.75.75 0 1 1 0 1.5H.75a.75.75 0 1 1 0-1.5h5.5zm0-5a.75.75 0 1 1 0 1.5H.75a.75.75 0 1 1 0-1.5h5.5zm3-5a.75.75 0 0 1 0 1.5H.75a.75.75 0 0 1 0-1.5h8.5zm6-5a.75.75 0 1 1 0 1.5H.75a.75.75 0 0 1 0-1.5h14.5zm.25 5.5a3.5 3.5 0 0 1 3.143 1.959.75.75 0 0 1-1.36.636A2 2 0 0 0 13.5 10v2H19a1 1 0 0 1 1 1v5a1 1 0 0 1-1 1h-7a1 1 0 0 1-1-1v-5a1 1 0 0 1 1-1v-2l.005-.192A3.5 3.5 0 0 1 15.5 6.5zm0 7.5a.5.5 0 0 0-.492.41L15 14.5v2a.5.5 0 0 0 .992.09L16 16.5v-2a.5.5 0 0 0-.5-.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-restricted-editing/src/standardeditingmodeui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module restricted-editing/standardeditingmodeui
 */






/**
 * The standard editing mode UI feature.
 *
 * It introduces the `'restrictedEditingException'` button that marks text as unrestricted for editing.
 *
 * @extends module:core/plugin~Plugin
 */
class StandardEditingModeUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		editor.ui.componentFactory.add( 'restrictedEditingException', locale => {
			const command = editor.commands.get( 'restrictedEditingException' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				icon: contentunlock,
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
			view.bind( 'label' ).to( command, 'value', value => {
				return value ? t( 'Disable editing' ) : t( 'Enable editing' );
			} );

			this.listenTo( view, 'execute', () => {
				editor.execute( 'restrictedEditingException' );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-restricted-editing/theme/restrictedediting.css
var restrictedediting = __webpack_require__(2353);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-restricted-editing/theme/restrictedediting.css

            

var restrictedediting_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

restrictedediting_options.insert = "head";
restrictedediting_options.singleton = true;

var restrictedediting_update = injectStylesIntoStyleTag_default()(restrictedediting/* default */.Z, restrictedediting_options);



/* harmony default export */ const theme_restrictedediting = (restrictedediting/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-restricted-editing/src/standardeditingmode.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module restricted-editing/standardeditingmode
 */








/**
 * The standard editing mode plugin.
 *
 * This is a "glue" plugin that loads the following plugins:
 *
 * * The {@link module:restricted-editing/standardeditingmodeediting~StandardEditingModeEditing standard mode editing feature}.
 * * The {@link module:restricted-editing/standardeditingmodeui~StandardEditingModeUI standard mode UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class StandardEditingMode extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'StandardEditingMode';
	}

	static get requires() {
		return [ StandardEditingModeEditing, StandardEditingModeUI ];
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/strikethrough/strikethroughediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/strikethrough/strikethroughediting
 */




const STRIKETHROUGH = 'strikethrough';

/**
 * The strikethrough editing feature.
 *
 * It registers the `'strikethrough'` command, the <kbd>Ctrl+Shift+X</kbd> keystroke and introduces the
 * `strikethroughsthrough` attribute in the model which renders to the view
 * as a `<s>` element.
 *
 * @extends module:core/plugin~Plugin
 */
class StrikethroughEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'StrikethroughEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow strikethrough attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: STRIKETHROUGH } );
		editor.model.schema.setAttributeProperties( STRIKETHROUGH, {
			isFormatting: true,
			copyOnEnter: true
		} );

		editor.conversion.attributeToElement( {
			model: STRIKETHROUGH,
			view: 's',
			upcastAlso: [
				'del',
				'strike',
				{
					styles: {
						'text-decoration': 'line-through'
					}
				}
			]
		} );

		// Create strikethrough command.
		editor.commands.add( STRIKETHROUGH, new AttributeCommand( editor, STRIKETHROUGH ) );

		// Set the Ctrl+Shift+X keystroke.
		editor.keystrokes.set( 'CTRL+SHIFT+X', 'strikethrough' );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/strikethrough.svg
/* harmony default export */ const strikethrough = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M7 16.4c-.8-.4-1.5-.9-2.2-1.5a.6.6 0 0 1-.2-.5l.3-.6h1c1 1.2 2.1 1.7 3.7 1.7 1 0 1.8-.3 2.3-.6.6-.4.6-1.2.6-1.3.2-1.2-.9-2.1-.9-2.1h2.1c.3.7.4 1.2.4 1.7v.8l-.6 1.2c-.6.8-1.1 1-1.6 1.2a6 6 0 0 1-2.4.6c-1 0-1.8-.3-2.5-.6zM6.8 9 6 8.3c-.4-.5-.5-.8-.5-1.6 0-.7.1-1.3.5-1.8.4-.6 1-1 1.6-1.3a6.3 6.3 0 0 1 4.7 0 4 4 0 0 1 1.7 1l.3.7c0 .1.2.4-.2.7-.4.2-.9.1-1 0a3 3 0 0 0-1.2-1c-.4-.2-1-.3-2-.4-.7 0-1.4.2-2 .6-.8.6-1 .8-1 1.5 0 .8.5 1 1.2 1.5.6.4 1.1.7 1.9 1H6.8z\"/><path d=\"M3 10.5V9h14v1.5z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/strikethrough/strikethroughui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/strikethrough/strikethroughui
 */






const strikethroughui_STRIKETHROUGH = 'strikethrough';

/**
 * The strikethrough UI feature. It introduces the Strikethrough button.
 *
 * @extends module:core/plugin~Plugin
 */
class StrikethroughUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'StrikethroughUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add strikethrough button to feature components.
		editor.ui.componentFactory.add( strikethroughui_STRIKETHROUGH, locale => {
			const command = editor.commands.get( strikethroughui_STRIKETHROUGH );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Strikethrough' ),
				icon: strikethrough,
				keystroke: 'CTRL+SHIFT+X',
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( strikethroughui_STRIKETHROUGH );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/strikethrough.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/strikethrough
 */





/**
 * The strikethrough feature.
 *
 * For a detailed overview check the {@glink features/basic-styles Basic styles feature documentation}
 * and the {@glink api/basic-styles package page}.
 *
 * This is a "glue" plugin which loads the {@link module:basic-styles/strikethrough/strikethroughediting~StrikethroughEditing} and
 * {@link module:basic-styles/strikethrough/strikethroughui~StrikethroughUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Strikethrough extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ StrikethroughEditing, StrikethroughUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Strikethrough';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/subscript/subscriptediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/subscript/subscriptediting
 */




const SUBSCRIPT = 'subscript';

/**
 * The subscript editing feature.
 *
 * It registers the `sub` command and introduces the `sub` attribute in the model which renders to the view
 * as a `<sub>` element.
 *
 * @extends module:core/plugin~Plugin
 */
class SubscriptEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SubscriptEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		// Allow sub attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: SUBSCRIPT } );
		editor.model.schema.setAttributeProperties( SUBSCRIPT, {
			isFormatting: true,
			copyOnEnter: true
		} );

		// Build converter from model to view for data and editing pipelines.

		editor.conversion.attributeToElement( {
			model: SUBSCRIPT,
			view: 'sub',
			upcastAlso: [
				{
					styles: {
						'vertical-align': 'sub'
					}
				}
			]
		} );

		// Create sub command.
		editor.commands.add( SUBSCRIPT, new AttributeCommand( editor, SUBSCRIPT ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/subscript.svg
/* harmony default export */ const subscript = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m7.03 10.349 3.818-3.819a.8.8 0 1 1 1.132 1.132L8.16 11.48l3.819 3.818a.8.8 0 1 1-1.132 1.132L7.03 12.61l-3.818 3.82a.8.8 0 1 1-1.132-1.132L5.9 11.48 2.08 7.662A.8.8 0 1 1 3.212 6.53l3.818 3.82zm8.147 7.829h2.549c.254 0 .447.05.58.152a.49.49 0 0 1 .201.413.54.54 0 0 1-.159.393c-.105.108-.266.162-.48.162h-3.594c-.245 0-.435-.066-.572-.197a.621.621 0 0 1-.205-.463c0-.114.044-.265.132-.453a1.62 1.62 0 0 1 .288-.444c.433-.436.824-.81 1.172-1.122.348-.312.597-.517.747-.615.267-.183.49-.368.667-.553.177-.185.312-.375.405-.57.093-.194.139-.384.139-.57a1.008 1.008 0 0 0-.554-.917 1.197 1.197 0 0 0-.56-.133c-.426 0-.761.182-1.005.546a2.332 2.332 0 0 0-.164.39 1.609 1.609 0 0 1-.258.488c-.096.114-.237.17-.423.17a.558.558 0 0 1-.405-.156.568.568 0 0 1-.161-.427c0-.218.05-.446.151-.683.101-.238.252-.453.452-.646s.454-.349.762-.467a2.998 2.998 0 0 1 1.081-.178c.498 0 .923.076 1.274.228a1.916 1.916 0 0 1 1.004 1.032 1.984 1.984 0 0 1-.156 1.794c-.2.32-.405.572-.613.754-.208.182-.558.468-1.048.857-.49.39-.826.691-1.008.906a2.703 2.703 0 0 0-.24.309z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/subscript/subscriptui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/subscript/subscriptui
 */






const subscriptui_SUBSCRIPT = 'subscript';

/**
 * The subscript UI feature. It introduces the Subscript button.
 *
 * @extends module:core/plugin~Plugin
 */
class SubscriptUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SubscriptUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add subscript button to feature components.
		editor.ui.componentFactory.add( subscriptui_SUBSCRIPT, locale => {
			const command = editor.commands.get( subscriptui_SUBSCRIPT );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Subscript' ),
				icon: subscript,
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( subscriptui_SUBSCRIPT );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/subscript.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/subscript
 */





/**
 * The subscript feature.
 *
 * It loads the {@link module:basic-styles/subscript/subscriptediting~SubscriptEditing} and
 * {@link module:basic-styles/subscript/subscriptui~SubscriptUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Subscript extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ SubscriptEditing, SubscriptUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Subscript';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/superscript/superscriptediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/superscript/superscriptediting
 */




const SUPERSCRIPT = 'superscript';

/**
 * The superscript editing feature.
 *
 * It registers the `super` command and introduces the `super` attribute in the model which renders to the view
 * as a `<super>` element.
 *
 * @extends module:core/plugin~Plugin
 */
class SuperscriptEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SuperscriptEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		// Allow super attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: SUPERSCRIPT } );
		editor.model.schema.setAttributeProperties( SUPERSCRIPT, {
			isFormatting: true,
			copyOnEnter: true
		} );

		// Build converter from model to view for data and editing pipelines.

		editor.conversion.attributeToElement( {
			model: SUPERSCRIPT,
			view: 'sup',
			upcastAlso: [
				{
					styles: {
						'vertical-align': 'super'
					}
				}
			]
		} );

		// Create super command.
		editor.commands.add( SUPERSCRIPT, new AttributeCommand( editor, SUPERSCRIPT ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/superscript.svg
/* harmony default export */ const superscript = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M15.677 8.678h2.549c.254 0 .447.05.58.152a.49.49 0 0 1 .201.413.54.54 0 0 1-.159.393c-.105.108-.266.162-.48.162h-3.594c-.245 0-.435-.066-.572-.197a.621.621 0 0 1-.205-.463c0-.114.044-.265.132-.453a1.62 1.62 0 0 1 .288-.444c.433-.436.824-.81 1.172-1.122.348-.312.597-.517.747-.615.267-.183.49-.368.667-.553.177-.185.312-.375.405-.57.093-.194.139-.384.139-.57a1.008 1.008 0 0 0-.554-.917 1.197 1.197 0 0 0-.56-.133c-.426 0-.761.182-1.005.546a2.332 2.332 0 0 0-.164.39 1.609 1.609 0 0 1-.258.488c-.096.114-.237.17-.423.17a.558.558 0 0 1-.405-.156.568.568 0 0 1-.161-.427c0-.218.05-.446.151-.683.101-.238.252-.453.452-.646s.454-.349.762-.467a2.998 2.998 0 0 1 1.081-.178c.498 0 .923.076 1.274.228a1.916 1.916 0 0 1 1.004 1.032 1.984 1.984 0 0 1-.156 1.794c-.2.32-.405.572-.613.754-.208.182-.558.468-1.048.857-.49.39-.826.691-1.008.906a2.703 2.703 0 0 0-.24.309zM7.03 10.349l3.818-3.819a.8.8 0 1 1 1.132 1.132L8.16 11.48l3.819 3.818a.8.8 0 1 1-1.132 1.132L7.03 12.61l-3.818 3.82a.8.8 0 1 1-1.132-1.132L5.9 11.48 2.08 7.662A.8.8 0 1 1 3.212 6.53l3.818 3.82z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/superscript/superscriptui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/superscript/superscriptui
 */






const superscriptui_SUPERSCRIPT = 'superscript';

/**
 * The superscript UI feature. It introduces the Superscript button.
 *
 * @extends module:core/plugin~Plugin
 */
class SuperscriptUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'SuperscriptUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add superscript button to feature components.
		editor.ui.componentFactory.add( superscriptui_SUPERSCRIPT, locale => {
			const command = editor.commands.get( superscriptui_SUPERSCRIPT );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Superscript' ),
				icon: superscript,
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( superscriptui_SUPERSCRIPT );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/superscript.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/superscript
 */





/**
 * The superscript feature.
 *
 * It loads the {@link module:basic-styles/superscript/superscriptediting~SuperscriptEditing} and
 * {@link module:basic-styles/superscript/superscriptui~SuperscriptUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Superscript extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ SuperscriptEditing, SuperscriptUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Superscript';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/tableproperties.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/tableproperites
 */

/**
 * Conversion helper for upcasting attributes using normalized styles.
 *
 * @param {module:engine/conversion/conversion~Conversion} conversion
 * @param {Object} options
 * @param {String} options.modelAttribute The attribute to set.
 * @param {String} options.styleName The style name to convert.
 * @param {String} options.viewElement The view element name that should be converted.
 * @param {String} options.defaultValue The default value for the specified `modelAttribute`.
 * @param {Boolean} [options.reduceBoxSides=false]
 */
function upcastStyleToAttribute( conversion, options ) {
	const { viewElement, defaultValue, modelAttribute, styleName, reduceBoxSides = false } = options;

	conversion.for( 'upcast' ).attributeToAttribute( {
		view: {
			name: viewElement,
			styles: {
				[ styleName ]: /[\s\S]+/
			}
		},
		model: {
			key: modelAttribute,
			value: viewElement => {
				const normalized = viewElement.getNormalizedStyle( styleName );
				const value = reduceBoxSides ? reduceBoxSidesValue( normalized ) : normalized;

				if ( defaultValue !== value ) {
					return value;
				}
			}
		}
	} );
}

/**
 * Conversion helper for upcasting border styles for view elements.
 *
 * @param {module:engine/conversion/conversion~Conversion} conversion
 * @param {String} viewElementName
 * @param {Object} modelAttributes
 * @param {Object} defaultBorder The default border values.
 * @param {String} defaultBorder.color The default `borderColor` value.
 * @param {String} defaultBorder.style The default `borderStyle` value.
 * @param {String} defaultBorder.width The default `borderWidth` value.
 */
function upcastBorderStyles( conversion, viewElementName, modelAttributes, defaultBorder ) {
	conversion.for( 'upcast' ).add( dispatcher => dispatcher.on( 'element:' + viewElementName, ( evt, data, conversionApi ) => {
		// If the element was not converted by element-to-element converter,
		// we should not try to convert the style. See #8393.
		if ( !data.modelRange ) {
			return;
		}

		// Check the most detailed properties. These will be always set directly or
		// when using the "group" properties like: `border-(top|right|bottom|left)` or `border`.
		const stylesToConsume = [
			'border-top-width',
			'border-top-color',
			'border-top-style',
			'border-bottom-width',
			'border-bottom-color',
			'border-bottom-style',
			'border-right-width',
			'border-right-color',
			'border-right-style',
			'border-left-width',
			'border-left-color',
			'border-left-style'
		].filter( styleName => data.viewItem.hasStyle( styleName ) );

		if ( !stylesToConsume.length ) {
			return;
		}

		const matcherPattern = {
			styles: stylesToConsume
		};

		// Try to consume appropriate values from consumable values list.
		if ( !conversionApi.consumable.test( data.viewItem, matcherPattern ) ) {
			return;
		}

		const modelElement = [ ...data.modelRange.getItems( { shallow: true } ) ].pop();

		conversionApi.consumable.consume( data.viewItem, matcherPattern );

		const normalizedBorder = {
			style: data.viewItem.getNormalizedStyle( 'border-style' ),
			color: data.viewItem.getNormalizedStyle( 'border-color' ),
			width: data.viewItem.getNormalizedStyle( 'border-width' )
		};

		const reducedBorder = {
			style: reduceBoxSidesValue( normalizedBorder.style ),
			color: reduceBoxSidesValue( normalizedBorder.color ),
			width: reduceBoxSidesValue( normalizedBorder.width )
		};

		if ( reducedBorder.style !== defaultBorder.style ) {
			conversionApi.writer.setAttribute( modelAttributes.style, reducedBorder.style, modelElement );
		}

		if ( reducedBorder.color !== defaultBorder.color ) {
			conversionApi.writer.setAttribute( modelAttributes.color, reducedBorder.color, modelElement );
		}

		if ( reducedBorder.width !== defaultBorder.width ) {
			conversionApi.writer.setAttribute( modelAttributes.width, reducedBorder.width, modelElement );
		}
	} ) );
}

/**
 * Conversion helper for downcasting an attribute to a style.
 *
 * @param {module:engine/conversion/conversion~Conversion} conversion
 * @param {Object} options
 * @param {String} options.modelElement
 * @param {String} options.modelAttribute
 * @param {String} options.styleName
 */
function downcastAttributeToStyle( conversion, { modelElement, modelAttribute, styleName } ) {
	conversion.for( 'downcast' ).attributeToAttribute( {
		model: {
			name: modelElement,
			key: modelAttribute
		},
		view: modelAttributeValue => ( {
			key: 'style',
			value: {
				[ styleName ]: modelAttributeValue
			}
		} )
	} );
}

/**
 * Conversion helper for downcasting attributes from the model table to a view table (not to `<figure>`).
 *
 * @param {module:engine/conversion/conversion~Conversion} conversion
 * @param {Object} options
 * @param {String} options.modelAttribute
 * @param {String} options.styleName
 */
function downcastTableAttribute( conversion, { modelAttribute, styleName } ) {
	conversion.for( 'downcast' ).add( dispatcher => dispatcher.on( `attribute:${ modelAttribute }:table`, ( evt, data, conversionApi ) => {
		const { item, attributeNewValue } = data;
		const { mapper, writer } = conversionApi;

		if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
			return;
		}

		const table = [ ...mapper.toViewElement( item ).getChildren() ].find( child => child.is( 'element', 'table' ) );

		if ( attributeNewValue ) {
			writer.setStyle( styleName, attributeNewValue, table );
		} else {
			writer.removeStyle( styleName, table );
		}
	} ) );
}

// Reduces the full top, right, bottom, left object to a single string if all sides are equal.
function reduceBoxSidesValue( style ) {
	if ( !style ) {
		return;
	}

	const commonValue = [ 'top', 'right', 'bottom', 'left' ]
		.map( side => style[ side ] )
		.reduce( ( result, side ) => result == side ? result : null );

	return commonValue || style;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/utils/common.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/utils/common
 */



/**
 * A common method to update the numeric value. If a value is the default one, it will be unset.
 *
 * @param {String} key An attribute key.
 * @param {*} value The new attribute value.
 * @param {module:engine/model/item~Item} item A model item on which the attribute will be set.
 * @param {module:engine/model/writer~Writer} writer
 * @param {*} defaultValue The default attribute value. If a value is lower or equal, it will be unset.
 */
function updateNumericAttribute( key, value, item, writer, defaultValue = 1 ) {
	if ( value > defaultValue ) {
		writer.setAttribute( key, value, item );
	} else {
		writer.removeAttribute( key, item );
	}
}

/**
 * A common method to create an empty table cell. It creates a proper model structure as a table cell must have at least one block inside.
 *
 * @param {module:engine/model/writer~Writer} writer The model writer.
 * @param {module:engine/model/position~Position} insertPosition The position at which the table cell should be inserted.
 * @param {Object} attributes The element attributes.
 * @returns {module:engine/model/element~Element} Created table cell.
 */
function createEmptyTableCell( writer, insertPosition, attributes = {} ) {
	const tableCell = writer.createElement( 'tableCell', attributes );

	writer.insertElement( 'paragraph', tableCell );
	writer.insert( tableCell, insertPosition );

	return tableCell;
}

/**
 * Checks if a table cell belongs to the heading column section.
 *
 * @param {module:table/tableutils~TableUtils} tableUtils
 * @param {module:engine/model/element~Element} tableCell
 * @returns {Boolean}
 */
function isHeadingColumnCell( tableUtils, tableCell ) {
	const table = tableCell.parent.parent;
	const headingColumns = parseInt( table.getAttribute( 'headingColumns' ) || 0 );
	const { column } = tableUtils.getCellLocation( tableCell );

	return !!headingColumns && column < headingColumns;
}

/**
 * Enables conversion for an attribute for simple view-model mappings.
 *
 * @param {module:engine/model/schema~Schema} schema
 * @param {module:engine/conversion/conversion~Conversion} conversion
 * @param {Object} options
 * @param {String} options.modelAttribute
 * @param {String} options.styleName
 * @param {String} options.defaultValue The default value for the specified `modelAttribute`.
 * @param {Boolean} [options.reduceBoxSides=false]
 */
function enableProperty( schema, conversion, options ) {
	const { modelAttribute } = options;

	schema.extend( 'tableCell', {
		allowAttributes: [ modelAttribute ]
	} );

	upcastStyleToAttribute( conversion, { viewElement: /^(td|th)$/, ...options } );
	downcastAttributeToStyle( conversion, { modelElement: 'tableCell', ...options } );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/upcasttable.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/upcasttable
 */




/**
 * Returns a function that converts the table view representation:
 *
 *		<figure class="table"><table>...</table></figure>
 *
 * to the model representation:
 *
 *		<table></table>
 *
 * @returns {Function}
 */
function upcastTableFigure() {
	return dispatcher => {
		dispatcher.on( 'element:figure', ( evt, data, conversionApi ) => {
			// Do not convert if this is not a "table figure".
			if ( !conversionApi.consumable.test( data.viewItem, { name: true, classes: 'table' } ) ) {
				return;
			}

			// Find an table element inside the figure element.
			const viewTable = getViewTableFromFigure( data.viewItem );

			// Do not convert if table element is absent or was already converted.
			if ( !viewTable || !conversionApi.consumable.test( viewTable, { name: true } ) ) {
				return;
			}

			// Consume the figure to prevent other converters from processing it again.
			conversionApi.consumable.consume( data.viewItem, { name: true, classes: 'table' } );

			// Convert view table to model table.
			const conversionResult = conversionApi.convertItem( viewTable, data.modelCursor );

			// Get table element from conversion result.
			const modelTable = first_first( conversionResult.modelRange.getItems() );

			// When table wasn't successfully converted then finish conversion.
			if ( !modelTable ) {
				// Revert consumed figure so other features can convert it.
				conversionApi.consumable.revert( data.viewItem, { name: true, classes: 'table' } );

				return;
			}

			conversionApi.convertChildren( data.viewItem, conversionApi.writer.createPositionAt( modelTable, 'end' ) );
			conversionApi.updateConversionResult( modelTable, data );
		} );
	};
}

/**
 * View table element to model table element conversion helper.
 *
 * This conversion helper converts the table element as well as table rows.
 *
 * @returns {Function} Conversion helper.
 */
function upcastTable() {
	return dispatcher => {
		dispatcher.on( 'element:table', ( evt, data, conversionApi ) => {
			const viewTable = data.viewItem;

			// When element was already consumed then skip it.
			if ( !conversionApi.consumable.test( viewTable, { name: true } ) ) {
				return;
			}

			const { rows, headingRows, headingColumns } = scanTable( viewTable );

			// Only set attributes if values is greater then 0.
			const attributes = {};

			if ( headingColumns ) {
				attributes.headingColumns = headingColumns;
			}

			if ( headingRows ) {
				attributes.headingRows = headingRows;
			}

			const table = conversionApi.writer.createElement( 'table', attributes );

			if ( !conversionApi.safeInsert( table, data.modelCursor ) ) {
				return;
			}

			conversionApi.consumable.consume( viewTable, { name: true } );

			// Upcast table rows in proper order (heading rows first).
			rows.forEach( row => conversionApi.convertItem( row, conversionApi.writer.createPositionAt( table, 'end' ) ) );

			// Convert everything else.
			conversionApi.convertChildren( viewTable, conversionApi.writer.createPositionAt( table, 'end' ) );

			// Create one row and one table cell for empty table.
			if ( table.isEmpty ) {
				const row = conversionApi.writer.createElement( 'tableRow' );
				conversionApi.writer.insert( row, conversionApi.writer.createPositionAt( table, 'end' ) );

				createEmptyTableCell( conversionApi.writer, conversionApi.writer.createPositionAt( row, 'end' ) );
			}

			conversionApi.updateConversionResult( table, data );
		} );
	};
}

/**
 * A conversion helper that skips empty <tr> elements from upcasting at the beginning of the table.
 *
 * An empty row is considered a table model error but when handling clipboard data there could be rows that contain only row-spanned cells
 * and empty TR-s are used to maintain the table structure (also {@link module:table/tablewalker~TableWalker} assumes that there are only
 * rows that have related `tableRow` elements).
 *
 * *Note:* Only the first empty rows are removed because they have no meaning and it solves the issue
 * of an improper table with all empty rows.
 *
 * @returns {Function} Conversion helper.
 */
function skipEmptyTableRow() {
	return dispatcher => {
		dispatcher.on( 'element:tr', ( evt, data ) => {
			if ( data.viewItem.isEmpty && data.modelCursor.index == 0 ) {
				evt.stop();
			}
		}, { priority: 'high' } );
	};
}

/**
 * A converter that ensures an empty paragraph is inserted in a table cell if no other content was converted.
 *
 * @returns {Function} Conversion helper.
 */
function ensureParagraphInTableCell( elementName ) {
	return dispatcher => {
		dispatcher.on( `element:${ elementName }`, ( evt, data, conversionApi ) => {
			// The default converter will create a model range on converted table cell.
			if ( !data.modelRange ) {
				return;
			}

			// Ensure a paragraph in the model for empty table cells for converted table cells.
			if ( data.viewItem.isEmpty ) {
				const tableCell = data.modelRange.start.nodeAfter;
				const modelCursor = conversionApi.writer.createPositionAt( tableCell, 0 );

				conversionApi.writer.insertElement( 'paragraph', modelCursor );
			}
		}, { priority: 'low' } );
	};
}

// Get view `<table>` element from the view widget (`<figure>`).
//
// @private
// @param {module:engine/view/element~Element} figureView
// @returns {module:engine/view/element~Element}
function getViewTableFromFigure( figureView ) {
	for ( const figureChild of figureView.getChildren() ) {
		if ( figureChild.is( 'element', 'table' ) ) {
			return figureChild;
		}
	}
}

// Scans table rows and extracts required metadata from the table:
//
// headingRows    - The number of rows that go as table headers.
// headingColumns - The maximum number of row headings.
// rows           - Sorted `<tr>` elements as they should go into the model - ie. if `<thead>` is inserted after `<tbody>` in the view.
//
// @private
// @param {module:engine/view/element~Element} viewTable
// @returns {{headingRows, headingColumns, rows}}
function scanTable( viewTable ) {
	const tableMeta = {
		headingRows: 0,
		headingColumns: 0
	};

	// The `<tbody>` and `<thead>` sections in the DOM do not have to be in order `<thead>` -> `<tbody>` and there might be more than one
	// of them.
	// As the model does not have these sections, rows from different sections must be sorted.
	// For example, below is a valid HTML table:
	//
	//		<table>
	//			<tbody><tr><td>2</td></tr></tbody>
	//			<thead><tr><td>1</td></tr></thead>
	//			<tbody><tr><td>3</td></tr></tbody>
	//		</table>
	//
	// But browsers will render rows in order as: 1 as the heading and 2 and 3 as the body.
	const headRows = [];
	const bodyRows = [];

	// Currently the editor does not support more then one <thead> section.
	// Only the first <thead> from the view will be used as a heading row and the others will be converted to body rows.
	let firstTheadElement;

	for ( const tableChild of Array.from( viewTable.getChildren() ) ) {
		// Only `<thead>`, `<tbody>` & `<tfoot>` from allowed table children can have `<tr>`s.
		// The else is for future purposes (mainly `<caption>`).
		if ( tableChild.name === 'tbody' || tableChild.name === 'thead' || tableChild.name === 'tfoot' ) {
			// Save the first `<thead>` in the table as table header - all other ones will be converted to table body rows.
			if ( tableChild.name === 'thead' && !firstTheadElement ) {
				firstTheadElement = tableChild;
			}

			// There might be some extra empty text nodes between the `<tr>`s.
			// Make sure further code operates on `tr`s only. (#145)
			const trs = Array.from( tableChild.getChildren() ).filter( el => el.is( 'element', 'tr' ) );

			for ( const tr of trs ) {
				// This <tr> is a child of a first <thead> element.
				if ( tr.parent.name === 'thead' && tr.parent === firstTheadElement ) {
					tableMeta.headingRows++;
					headRows.push( tr );
				} else {
					bodyRows.push( tr );
					// For other rows check how many column headings this row has.

					const headingCols = scanRowForHeadingColumns( tr, tableMeta, firstTheadElement );

					if ( headingCols > tableMeta.headingColumns ) {
						tableMeta.headingColumns = headingCols;
					}
				}
			}
		}
	}

	tableMeta.rows = [ ...headRows, ...bodyRows ];

	return tableMeta;
}

// Scans a `<tr>` element and its children for metadata:
// - For heading row:
//     - Adds this row to either the heading or the body rows.
//     - Updates the number of heading rows.
// - For body rows:
//     - Calculates the number of column headings.
//
// @private
// @param {module:engine/view/element~Element} tr
// @returns {Number}
function scanRowForHeadingColumns( tr ) {
	let headingColumns = 0;
	let index = 0;

	// Filter out empty text nodes from tr children.
	const children = Array.from( tr.getChildren() )
		.filter( child => child.name === 'th' || child.name === 'td' );

	// Count starting adjacent <th> elements of a <tr>.
	while ( index < children.length && children[ index ].name === 'th' ) {
		const th = children[ index ];

		// Adjust columns calculation by the number of spanned columns.
		const colspan = parseInt( th.getAttribute( 'colspan' ) || 1 );

		headingColumns = headingColumns + colspan;
		index++;
	}

	return headingColumns;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablewalker.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablewalker
 */

// @if CK_DEBUG // import { CKEditorError } from 'ckeditor5/src/utils';

/**
 * The table iterator class. It allows to iterate over table cells. For each cell the iterator yields
 * {@link module:table/tablewalker~TableSlot} with proper table cell attributes.
 */
class TableWalker {
	/**
	 * Creates an instance of the table walker.
	 *
	 * The table walker iterates internally by traversing the table from row index = 0 and column index = 0.
	 * It walks row by row and column by column in order to output values defined in the constructor.
	 * By default it will output only the locations that are occupied by a cell. To include also spanned rows and columns,
	 * pass the `includeAllSlots` option to the constructor.
	 *
	 * The most important values of the iterator are column and row indexes of a cell.
	 *
	 * See {@link module:table/tablewalker~TableSlot} what values are returned by the table walker.
	 *
	 * To iterate over a given row:
	 *
	 *		const tableWalker = new TableWalker( table, { startRow: 1, endRow: 2 } );
	 *
	 *		for ( const tableSlot of tableWalker ) {
	 *			console.log( 'A cell at row', tableSlot.row, 'and column', tableSlot.column );
	 *		}
	 *
	 * For instance the code above for the following table:
	 *
	 *		+----+----+----+----+----+----+
	 *		| 00      | 02 | 03 | 04 | 05 |
	 *		|         +----+----+----+----+
	 *		|         | 12      | 14 | 15 |
	 *		|         +----+----+----+    +
	 *		|         | 22           |    |
	 *		|----+----+----+----+----+    +
	 *		| 30 | 31 | 32 | 33 | 34 |    |
	 *		+----+----+----+----+----+----+
	 *
	 * will log in the console:
	 *
	 *		'A cell at row 1 and column 2'
	 *		'A cell at row 1 and column 4'
	 *		'A cell at row 1 and column 5'
	 *		'A cell at row 2 and column 2'
	 *
	 * To also iterate over spanned cells:
	 *
	 *		const tableWalker = new TableWalker( table, { row: 1, includeAllSlots: true } );
	 *
	 *		for ( const tableSlot of tableWalker ) {
	 *			console.log( 'Slot at', tableSlot.row, 'x', tableSlot.column, ':', tableSlot.isAnchor ? 'is anchored' : 'is spanned' );
	 *		}
	 *
	 * will log in the console for the table from the previous example:
	 *
	 *		'Cell at 1 x 0 : is spanned'
	 *		'Cell at 1 x 1 : is spanned'
	 *		'Cell at 1 x 2 : is anchored'
	 *		'Cell at 1 x 3 : is spanned'
	 *		'Cell at 1 x 4 : is anchored'
	 *		'Cell at 1 x 5 : is anchored'
	 *
	 * **Note**: Option `row` is a shortcut that sets both `startRow` and `endRow` to the same row.
	 * (Use either `row` or `startRow` and `endRow` but never together). Similarly the `column` option sets both `startColumn`
	 * and `endColumn` to the same column (Use either `column` or `startColumn` and `endColumn` but never together).
	 *
	 * @constructor
	 * @param {module:engine/model/element~Element} table A table over which the walker iterates.
	 * @param {Object} [options={}] An object with configuration.
	 * @param {Number} [options.row] A row index for which this iterator will output cells.
	 * Can't be used together with `startRow` and `endRow`.
	 * @param {Number} [options.startRow=0] A row index from which this iterator should start. Can't be used together with `row`.
	 * @param {Number} [options.endRow] A row index at which this iterator should end. Can't be used together with `row`.
	 * @param {Number} [options.column] A column index for which this iterator will output cells.
	 * Can't be used together with `startColumn` and `endColumn`.
	 * @param {Number} [options.startColumn=0] A column index from which this iterator should start. Can't be used together with `column`.
	 * @param {Number} [options.endColumn] A column index at which this iterator should end. Can't be used together with `column`.
	 * @param {Boolean} [options.includeAllSlots=false] Also return values for spanned cells.
	 */
	constructor( table, options = {} ) {
		/**
		 * The walker's table element.
		 *
		 * @readonly
		 * @member {module:engine/model/element~Element}
		 * @protected
		 */
		this._table = table;

		/**
		 * A row index from which this iterator will start.
		 *
		 * @readonly
		 * @member {Number}
		 * @private
		 */
		this._startRow = options.row !== undefined ? options.row : options.startRow || 0;

		/**
		 * A row index at which this iterator will end.
		 *
		 * @readonly
		 * @member {Number}
		 * @private
		 */
		this._endRow = options.row !== undefined ? options.row : options.endRow;

		/**
		 * If set, the table walker will only output cells from a given column and following ones or cells that overlap them.
		 *
		 * @readonly
		 * @member {Number}
		 * @private
		 */
		this._startColumn = options.column !== undefined ? options.column : options.startColumn || 0;

		/**
		 * If set, the table walker will only output cells up to a given column.
		 *
		 * @readonly
		 * @member {Number}
		 * @private
		 */
		this._endColumn = options.column !== undefined ? options.column : options.endColumn;

		/**
		 * Enables output of spanned cells that are normally not yielded.
		 *
		 * @readonly
		 * @member {Boolean}
		 * @private
		 */
		this._includeAllSlots = !!options.includeAllSlots;

		/**
		 * Row indexes to skip from the iteration.
		 *
		 * @readonly
		 * @member {Set<Number>}
		 * @private
		 */
		this._skipRows = new Set();

		/**
		 * The current row index.
		 *
		 * @member {Number}
		 * @protected
		 */
		this._row = 0;

		/**
		 * The index of the current row element in the table.
		 *
		 * @type {Number}
		 * @protected
		 */
		this._rowIndex = 0;

		/**
		 * The current column index.
		 *
		 * @member {Number}
		 * @protected
		 */
		this._column = 0;

		/**
		 * The cell index in a parent row. For spanned cells when {@link #_includeAllSlots} is set to `true`,
		 * this represents the index of the next table cell.
		 *
		 * @member {Number}
		 * @protected
		 */
		this._cellIndex = 0;

		/**
		 * Holds a map of spanned cells in a table.
		 *
		 * @readonly
		 * @member {Map.<Number, Map.<Number, Object>>}
		 * @private
		 */
		this._spannedCells = new Map();

		/**
		 * Index of the next column where a cell is anchored.
		 *
		 * @member {Number}
		 * @private
		 */
		this._nextCellAtColumn = -1;
	}

	/**
	 * Iterable interface.
	 *
	 * @returns {Iterable.<module:table/tablewalker~TableSlot>}
	 */
	[ Symbol.iterator ]() {
		return this;
	}

	/**
	 * Gets the next table walker's value.
	 *
	 * @returns {module:table/tablewalker~TableSlot} The next table walker's value.
	 */
	next() {
		const row = this._table.getChild( this._rowIndex );

		// Iterator is done when there's no row (table ended) or the row is after `endRow` limit.
		if ( !row || this._isOverEndRow() ) {
			return { done: true };
		}

		// We step over current element when it is not a tableRow instance.
		if ( !row.is( 'element', 'tableRow' ) ) {
			this._rowIndex++;

			return this.next();
		}

		if ( this._isOverEndColumn() ) {
			return this._advanceToNextRow();
		}

		let outValue = null;

		const spanData = this._getSpanned();

		if ( spanData ) {
			if ( this._includeAllSlots && !this._shouldSkipSlot() ) {
				outValue = this._formatOutValue( spanData.cell, spanData.row, spanData.column );
			}
		} else {
			const cell = row.getChild( this._cellIndex );

			if ( !cell ) {
				// If there are no more cells left in row advance to the next row.
				return this._advanceToNextRow();
			}

			const colspan = parseInt( cell.getAttribute( 'colspan' ) || 1 );
			const rowspan = parseInt( cell.getAttribute( 'rowspan' ) || 1 );

			// Record this cell spans if it's not 1x1 cell.
			if ( colspan > 1 || rowspan > 1 ) {
				this._recordSpans( cell, rowspan, colspan );
			}

			if ( !this._shouldSkipSlot() ) {
				outValue = this._formatOutValue( cell );
			}

			this._nextCellAtColumn = this._column + colspan;
		}

		// Advance to the next column before returning value.
		this._column++;

		if ( this._column == this._nextCellAtColumn ) {
			this._cellIndex++;
		}

		// The current value will be returned only if current row and column are not skipped.
		return outValue || this.next();
	}

	/**
	 * Marks a row to skip in the next iteration. It will also skip cells from the current row if there are any cells from the current row
	 * to output.
	 *
	 * @param {Number} row The row index to skip.
	 */
	skipRow( row ) {
		this._skipRows.add( row );
	}

	/**
	 * Advances internal cursor to the next row.
	 *
	 * @private
	 * @returns {module:table/tablewalker~TableSlot}
	 */
	_advanceToNextRow() {
		this._row++;
		this._rowIndex++;
		this._column = 0;
		this._cellIndex = 0;
		this._nextCellAtColumn = -1;

		return this.next();
	}

	/**
	 * Checks if the current row is over {@link #_endRow}.
	 *
	 * @private
	 * @returns {Boolean}
	 */
	_isOverEndRow() {
		// If #_endRow is defined skip all rows after it.
		return this._endRow !== undefined && this._row > this._endRow;
	}

	/**
	 * Checks if the current cell is over {@link #_endColumn}
	 *
	 * @private
	 * @returns {Boolean}
	 */
	_isOverEndColumn() {
		// If #_endColumn is defined skip all cells after it.
		return this._endColumn !== undefined && this._column > this._endColumn;
	}

	/**
	 * A common method for formatting the iterator's output value.
	 *
	 * @private
	 * @param {module:engine/model/element~Element} cell The table cell to output.
	 * @param {Number} [anchorRow] The row index of a cell anchor slot.
	 * @param {Number} [anchorColumn] The column index of a cell anchor slot.
	 * @returns {{done: Boolean, value: {cell: *, row: Number, column: *, rowspan: *, colspan: *, cellIndex: Number}}}
	 */
	_formatOutValue( cell, anchorRow = this._row, anchorColumn = this._column ) {
		return {
			done: false,
			value: new TableSlot( this, cell, anchorRow, anchorColumn )
		};
	}

	/**
	 * Checks if the current slot should be skipped.
	 *
	 * @private
	 * @returns {Boolean}
	 */
	_shouldSkipSlot() {
		const rowIsMarkedAsSkipped = this._skipRows.has( this._row );
		const rowIsBeforeStartRow = this._row < this._startRow;

		const columnIsBeforeStartColumn = this._column < this._startColumn;
		const columnIsAfterEndColumn = this._endColumn !== undefined && this._column > this._endColumn;

		return rowIsMarkedAsSkipped || rowIsBeforeStartRow || columnIsBeforeStartColumn || columnIsAfterEndColumn;
	}

	/**
	 * Returns the cell element that is spanned over the current cell location.
	 *
	 * @private
	 * @returns {module:engine/model/element~Element}
	 */
	_getSpanned() {
		const rowMap = this._spannedCells.get( this._row );

		// No spans for given row.
		if ( !rowMap ) {
			return null;
		}

		// If spans for given rows has entry for column it means that this location if spanned by other cell.
		return rowMap.get( this._column ) || null;
	}

	/**
	 * Updates spanned cells map relative to the current cell location and its span dimensions.
	 *
	 * @private
	 * @param {module:engine/model/element~Element} cell A cell that is spanned.
	 * @param {Number} rowspan Cell height.
	 * @param {Number} colspan Cell width.
	 */
	_recordSpans( cell, rowspan, colspan ) {
		const data = {
			cell,
			row: this._row,
			column: this._column
		};

		for ( let rowToUpdate = this._row; rowToUpdate < this._row + rowspan; rowToUpdate++ ) {
			for ( let columnToUpdate = this._column; columnToUpdate < this._column + colspan; columnToUpdate++ ) {
				if ( rowToUpdate != this._row || columnToUpdate != this._column ) {
					this._markSpannedCell( rowToUpdate, columnToUpdate, data );
				}
			}
		}
	}

	/**
	 * Marks the cell location as spanned by another cell.
	 *
	 * @private
	 * @param {Number} row The row index of the cell location.
	 * @param {Number} column The column index of the cell location.
	 * @param {Object} data A spanned cell details (cell element, anchor row and column).
	 */
	_markSpannedCell( row, column, data ) {
		if ( !this._spannedCells.has( row ) ) {
			this._spannedCells.set( row, new Map() );
		}

		const rowSpans = this._spannedCells.get( row );

		rowSpans.set( column, data );
	}
}

/**
 * An object returned by {@link module:table/tablewalker~TableWalker} when traversing table cells.
 */
class TableSlot {
	/**
	 * Creates an instance of the table walker value.
	 *
	 * @protected
	 * @param {module:table/tablewalker~TableWalker} tableWalker The table walker instance.
	 * @param {module:engine/model/element~Element} cell The current table cell.
	 * @param {Number} anchorRow The row index of a cell anchor slot.
	 * @param {Number} anchorColumn The column index of a cell anchor slot.
	 */
	constructor( tableWalker, cell, anchorRow, anchorColumn ) {
		/**
		 * The current table cell.
		 *
		 * @readonly
		 * @member {module:engine/model/element~Element}
		 */
		this.cell = cell;

		/**
		 * The row index of a table slot.
		 *
		 * @readonly
		 * @member {Number}
		 */
		this.row = tableWalker._row;

		/**
		 * The column index of a table slot.
		 *
		 * @readonly
		 * @member {Number}
		 */
		this.column = tableWalker._column;

		/**
		 * The row index of a cell anchor slot.
		 *
		 * @readonly
		 * @member {Number}
		 */
		this.cellAnchorRow = anchorRow;

		/**
		 * The column index of a cell anchor slot.
		 *
		 * @readonly
		 * @member {Number}
		 */
		this.cellAnchorColumn = anchorColumn;

		/**
		 * The index of the current cell in the parent row.
		 *
		 * @readonly
		 * @member {Number}
		 * @private
		 */
		this._cellIndex = tableWalker._cellIndex;

		/**
		 * The index of the current row element in the table.
		 *
		 * @readonly
		 * @member {Number}
		 * @private
		 */
		this._rowIndex = tableWalker._rowIndex;

		/**
		 * The table element.
		 *
		 * @readonly
		 * @member {module:engine/model/element~Element}
		 * @private
		 */
		this._table = tableWalker._table;
	}

	/**
	 * Whether the cell is anchored in the current slot.
	 *
	 * @readonly
	 * @returns {Boolean}
	 */
	get isAnchor() {
		return this.row === this.cellAnchorRow && this.column === this.cellAnchorColumn;
	}

	/**
	 * The width of a cell defined by a `colspan` attribute. If the model attribute is not present, it is set to `1`.
	 *
	 * @readonly
	 * @returns {Number}
	 */
	get cellWidth() {
		return parseInt( this.cell.getAttribute( 'colspan' ) || 1 );
	}

	/**
	 * The height of a cell defined by a `rowspan` attribute. If the model attribute is not present, it is set to `1`.
	 *
	 * @readonly
	 * @returns {Number}
	 */
	get cellHeight() {
		return parseInt( this.cell.getAttribute( 'rowspan' ) || 1 );
	}

	/**
	 * The index of the current row element in the table.
	 *
	 * @readonly
	 * @returns {Number}
	 */
	get rowIndex() {
		return this._rowIndex;
	}

	/**
	 * Returns the {@link module:engine/model/position~Position} before the table slot.
	 *
	 * @returns {module:engine/model/position~Position}
	 */
	getPositionBefore() {
		const model = this._table.root.document.model;

		return model.createPositionAt( this._table.getChild( this.row ), this._cellIndex );
	}

	// @if CK_DEBUG // get isSpanned() { throwMissingGetterError( 'isSpanned' ); }
	// @if CK_DEBUG // get colspan() { throwMissingGetterError( 'colspan' ); }
	// @if CK_DEBUG // get rowspan() { throwMissingGetterError( 'rowspan' ); }
	// @if CK_DEBUG // get cellIndex() { throwMissingGetterError( 'cellIndex' ); }
}

/**
 * This `TableSlot`'s getter (property) was removed in CKEditor 5 v20.0.0.
 *
 * Check out the new `TableWalker`'s API in the documentation.
 *
 * @error tableslot-getter-removed
 * @param {String} getterName
 */

// @if CK_DEBUG // function throwMissingGetterError( getterName ) {
// @if CK_DEBUG //		throw new CKEditorError( 'tableslot-getter-removed', this, {
// @if CK_DEBUG //			getterName
// @if CK_DEBUG //		} );
// @if CK_DEBUG // }

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/downcast.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/downcast
 */




/**
 * Model table element to view table element conversion helper.
 *
 * @param {module:table/tableutils~TableUtils} tableUtils The `TableUtils` plugin instance.
 * @param {Object} [options]
 * @param {Boolean} [options.asWidget] If set to `true`, the downcast conversion will produce a widget.
 * @returns {Function} Element creator.
 */
function downcastTable( tableUtils, options = {} ) {
	return ( table, { writer } ) => {
		const headingRows = table.getAttribute( 'headingRows' ) || 0;
		const tableSections = [];

		// Table head slot.
		if ( headingRows > 0 ) {
			tableSections.push(
				writer.createContainerElement( 'thead', null,
					writer.createSlot( element => element.is( 'element', 'tableRow' ) && element.index < headingRows )
				)
			);
		}

		// Table body slot.
		if ( headingRows < tableUtils.getRows( table ) ) {
			tableSections.push(
				writer.createContainerElement( 'tbody', null,
					writer.createSlot( element => element.is( 'element', 'tableRow' ) && element.index >= headingRows )
				)
			);
		}

		const figureElement = writer.createContainerElement( 'figure', { class: 'table' }, [
			// Table with proper sections (thead, tbody).
			writer.createContainerElement( 'table', null, tableSections ),

			// Slot for the rest (for example caption).
			writer.createSlot( element => !element.is( 'element', 'tableRow' ) )
		] );

		return options.asWidget ? toTableWidget( figureElement, writer ) : figureElement;
	};
}

/**
 * Model table row element to view `<tr>` element conversion helper.
 *
 * @returns {Function} Element creator.
 */
function downcastRow() {
	return ( tableRow, { writer } ) => {
		return tableRow.isEmpty ?
			writer.createEmptyElement( 'tr' ) :
			writer.createContainerElement( 'tr' );
	};
}

/**
 * Model table cell element to view `<td>` or `<th>` element conversion helper.
 *
 * This conversion helper will create proper `<th>` elements for table cells that are in the heading section (heading row or column)
 * and `<td>` otherwise.
 *
 * @param {Object} [options]
 * @param {Boolean} [options.asWidget] If set to `true`, the downcast conversion will produce a widget.
 * @returns {Function} Element creator.
 */
function downcastCell( options = {} ) {
	return ( tableCell, { writer } ) => {
		const tableRow = tableCell.parent;
		const table = tableRow.parent;
		const rowIndex = table.getChildIndex( tableRow );

		const tableWalker = new TableWalker( table, { row: rowIndex } );
		const headingRows = table.getAttribute( 'headingRows' ) || 0;
		const headingColumns = table.getAttribute( 'headingColumns' ) || 0;

		// We need to iterate over a table in order to get proper row & column values from a walker.
		for ( const tableSlot of tableWalker ) {
			if ( tableSlot.cell == tableCell ) {
				const isHeading = tableSlot.row < headingRows || tableSlot.column < headingColumns;
				const cellElementName = isHeading ? 'th' : 'td';

				return options.asWidget ?
					toWidgetEditable( writer.createEditableElement( cellElementName ), writer ) :
					writer.createContainerElement( cellElementName );
			}
		}
	};
}

/**
 * Overrides paragraph inside table cell conversion.
 *
 * This converter:
 * * should be used to override default paragraph conversion.
 * * It will only convert `<paragraph>` placed directly inside `<tableCell>`.
 * * For a single paragraph without attributes it returns `<span>` to simulate data table.
 * * For all other cases it returns `<p>` element.
 *
 * @param {Object} [options]
 * @param {Boolean} [options.asWidget] If set to `true`, the downcast conversion will produce a widget.
 * @returns {Function} Element creator.
 */
function convertParagraphInTableCell( options = {} ) {
	return ( modelElement, { writer, consumable, mapper } ) => {
		if ( !modelElement.parent.is( 'element', 'tableCell' ) ) {
			return;
		}

		if ( !isSingleParagraphWithoutAttributes( modelElement ) ) {
			return;
		}

		if ( options.asWidget ) {
			return writer.createContainerElement( 'span', { class: 'ck-table-bogus-paragraph' } );
		} else {
			// Additional requirement for data pipeline to have backward compatible data tables.
			consumable.consume( modelElement, 'insert' );
			mapper.bindElements( modelElement, mapper.toViewElement( modelElement.parent ) );
		}
	};
}

/**
 * Checks if given model `<paragraph>` is an only child of a parent (`<tableCell>`) and if it has any attribute set.
 *
 * The paragraph should be converted in the editing view to:
 *
 * * If returned `true` - to a `<span class="ck-table-bogus-paragraph">`
 * * If returned `false` - to a `<p>`
 *
 * @param {module:engine/model/element~Element} modelElement
 * @returns {Boolean}
 */
function isSingleParagraphWithoutAttributes( modelElement ) {
	const tableCell = modelElement.parent;

	const isSingleParagraph = tableCell.childCount == 1;

	return isSingleParagraph && !downcast_hasAnyAttribute( modelElement );
}

// Converts a given {@link module:engine/view/element~Element} to a table widget:
// * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to recognize the table widget element.
// * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
//
// @param {module:engine/view/element~Element} viewElement
// @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
// @param {String} label The element's label. It will be concatenated with the table `alt` attribute if one is present.
// @returns {module:engine/view/element~Element}
function toTableWidget( viewElement, writer ) {
	writer.setCustomProperty( 'table', true, viewElement );

	return toWidget( viewElement, writer, { hasSelectionHandle: true } );
}

// Checks if an element has any attributes set.
//
// @param {module:engine/model/element~Element element
// @returns {Boolean}
function downcast_hasAnyAttribute( element ) {
	return !![ ...element.getAttributeKeys() ].length;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/inserttablecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/inserttablecommand
 */



/**
 * The insert table command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'insertTable'` editor command.
 *
 * To insert a table at the current selection, execute the command and specify the dimensions:
 *
 *		editor.execute( 'insertTable', { rows: 20, columns: 5 } );
 *
 * @extends module:core/command~Command
 */
class InsertTableCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const selection = model.document.selection;
		const schema = model.schema;

		this.isEnabled = inserttablecommand_isAllowedInParent( selection, schema );
	}

	/**
	 * Executes the command.
	 *
	 * Inserts a table with the given number of rows and columns into the editor.
	 *
	 * @param {Object} options
	 * @param {Number} [options.rows=2] The number of rows to create in the inserted table.
	 * @param {Number} [options.columns=2] The number of columns to create in the inserted table.
	 * @param {Number} [options.headingRows] The number of heading rows.
	 * If not provided it will default to {@link module:table/table~TableConfig#defaultHeadings `config.table.defaultHeadings.rows`}
	 * table config.
	 * @param {Number} [options.headingColumns] The number of heading columns.
	 * If not provided it will default to {@link module:table/table~TableConfig#defaultHeadings `config.table.defaultHeadings.columns`}
	 * table config.
	 * @fires execute
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const config = this.editor.config.get( 'table' );

		const defaultRows = config.defaultHeadings.rows;
		const defaultColumns = config.defaultHeadings.columns;

		if ( options.headingRows === undefined && defaultRows ) {
			options.headingRows = defaultRows;
		}

		if ( options.headingColumns === undefined && defaultColumns ) {
			options.headingColumns = defaultColumns;
		}

		model.change( writer => {
			const table = tableUtils.createTable( writer, options );

			model.insertObject( table, null, null, { findOptimalPosition: 'auto' } );

			writer.setSelection( writer.createPositionAt( table.getNodeByPath( [ 0, 0, 0 ] ), 0 ) );
		} );
	}
}

// Checks if the table is allowed in the parent.
//
// @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
// @param {module:engine/model/schema~Schema} schema
// @returns {Boolean}
function inserttablecommand_isAllowedInParent( selection, schema ) {
	const positionParent = selection.getFirstPosition().parent;
	const validParent = positionParent === positionParent.root ? positionParent : positionParent.parent;

	return schema.checkChild( validParent, 'table' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/insertrowcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/insertrowcommand
 */



/**
 * The insert row command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'insertTableRowBelow'` and
 * `'insertTableRowAbove'` editor commands.
 *
 * To insert a row below the selected cell, execute the following command:
 *
 *		editor.execute( 'insertTableRowBelow' );
 *
 * To insert a row above the selected cell, execute the following command:
 *
 *		editor.execute( 'insertTableRowAbove' );
 *
 * @extends module:core/command~Command
 */
class InsertRowCommand extends command_Command {
	/**
	 * Creates a new `InsertRowCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor on which this command will be used.
	 * @param {Object} options
	 * @param {String} [options.order="below"] The order of insertion relative to the row in which the caret is located.
	 * Possible values: `"above"` and `"below"`.
	 */
	constructor( editor, options = {} ) {
		super( editor );

		/**
		 * The order of insertion relative to the row in which the caret is located.
		 *
		 * @readonly
		 * @member {String} module:table/commands/insertrowcommand~InsertRowCommand#order
		 */
		this.order = options.order || 'below';
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const selection = this.editor.model.document.selection;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const isAnyCellSelected = !!tableUtils.getSelectionAffectedTableCells( selection ).length;

		this.isEnabled = isAnyCellSelected;
	}

	/**
	 * Executes the command.
	 *
	 * Depending on the command's {@link #order} value, it inserts a row `'below'` or `'above'` the row in which selection is set.
	 *
	 * @fires execute
	 */
	execute() {
		const editor = this.editor;
		const selection = editor.model.document.selection;
		const tableUtils = editor.plugins.get( 'TableUtils' );
		const insertAbove = this.order === 'above';

		const affectedTableCells = tableUtils.getSelectionAffectedTableCells( selection );
		const rowIndexes = tableUtils.getRowIndexes( affectedTableCells );

		const row = insertAbove ? rowIndexes.first : rowIndexes.last;
		const table = affectedTableCells[ 0 ].findAncestor( 'table' );

		tableUtils.insertRows( table, { at: insertAbove ? row : row + 1, copyStructureFromAbove: !insertAbove } );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/insertcolumncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/insertcolumncommand
 */



/**
 * The insert column command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'insertTableColumnLeft'` and
 * `'insertTableColumnRight'` editor commands.
 *
 * To insert a column to the left of the selected cell, execute the following command:
 *
 *		editor.execute( 'insertTableColumnLeft' );
 *
 * To insert a column to the right of the selected cell, execute the following command:
 *
 *		editor.execute( 'insertTableColumnRight' );
 *
 * @extends module:core/command~Command
 */
class InsertColumnCommand extends command_Command {
	/**
	 * Creates a new `InsertColumnCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor on which this command will be used.
	 * @param {Object} options
	 * @param {String} [options.order="right"] The order of insertion relative to the column in which the caret is located.
	 * Possible values: `"left"` and `"right"`.
	 */
	constructor( editor, options = {} ) {
		super( editor );

		/**
		 * The order of insertion relative to the column in which the caret is located.
		 *
		 * @readonly
		 * @member {String} module:table/commands/insertcolumncommand~InsertColumnCommand#order
		 */
		this.order = options.order || 'right';
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const selection = this.editor.model.document.selection;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const isAnyCellSelected = !!tableUtils.getSelectionAffectedTableCells( selection ).length;

		this.isEnabled = isAnyCellSelected;
	}

	/**
	 * Executes the command.
	 *
	 * Depending on the command's {@link #order} value, it inserts a column to the `'left'` or `'right'` of the column
	 * in which the selection is set.
	 *
	 * @fires execute
	 */
	execute() {
		const editor = this.editor;
		const selection = editor.model.document.selection;
		const tableUtils = editor.plugins.get( 'TableUtils' );
		const insertBefore = this.order === 'left';

		const affectedTableCells = tableUtils.getSelectionAffectedTableCells( selection );
		const columnIndexes = tableUtils.getColumnIndexes( affectedTableCells );

		const column = insertBefore ? columnIndexes.first : columnIndexes.last;
		const table = affectedTableCells[ 0 ].findAncestor( 'table' );

		tableUtils.insertColumns( table, { columns: 1, at: insertBefore ? column : column + 1 } );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/splitcellcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/splitcellcommand
 */



/**
 * The split cell command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'splitTableCellVertically'`
 * and `'splitTableCellHorizontally'`  editor commands.
 *
 * You can split any cell vertically or horizontally by executing this command. For example, to split the selected table cell vertically:
 *
 *		editor.execute( 'splitTableCellVertically' );
 *
 * @extends module:core/command~Command
 */
class SplitCellCommand extends command_Command {
	/**
	 * Creates a new `SplitCellCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor on which this command will be used.
	 * @param {Object} options
	 * @param {String} options.direction Indicates whether the command should split cells `'horizontally'` or `'vertically'`.
	 */
	constructor( editor, options = {} ) {
		super( editor );

		/**
		 * The direction that indicates which cell will be split.
		 *
		 * @readonly
		 * @member {String} #direction
		 */
		this.direction = options.direction || 'horizontally';
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const selectedCells = tableUtils.getSelectionAffectedTableCells( this.editor.model.document.selection );

		this.isEnabled = selectedCells.length === 1;
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const tableCell = tableUtils.getSelectionAffectedTableCells( this.editor.model.document.selection )[ 0 ];
		const isHorizontal = this.direction === 'horizontally';

		if ( isHorizontal ) {
			tableUtils.splitCellHorizontally( tableCell, 2 );
		} else {
			tableUtils.splitCellVertically( tableCell, 2 );
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/utils/structure.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/utils/structure
 */




/**
 * Returns a cropped table according to given dimensions.

 * To return a cropped table that starts at first row and first column and end in third row and column:
 *
 *		const croppedTable = cropTableToDimensions( table, {
 *			startRow: 1,
 *			endRow: 3,
 *			startColumn: 1,
 *			endColumn: 3
 *		}, writer );
 *
 * Calling the code above for the table below:
 *
 *		      0   1   2   3   4                      0   1   2
 *		    ┌───┬───┬───┬───┬───┐
 *		 0  │ a │ b │ c │ d │ e │
 *		    ├───┴───┤   ├───┴───┤                  ┌───┬───┬───┐
 *		 1  │ f     │   │ g     │                  │   │   │ g │  0
 *		    ├───┬───┴───┼───┬───┤   will return:   ├───┴───┼───┤
 *		 2  │ h │ i     │ j │ k │                  │ i     │ j │  1
 *		    ├───┤       ├───┤   │                  │       ├───┤
 *		 3  │ l │       │ m │   │                  │       │ m │  2
 *		    ├───┼───┬───┤   ├───┤                  └───────┴───┘
 *		 4  │ n │ o │ p │   │ q │
 *		    └───┴───┴───┴───┴───┘
 *
 * @param {module:engine/model/element~Element} sourceTable
 * @param {Object} cropDimensions
 * @param {Number} cropDimensions.startRow
 * @param {Number} cropDimensions.startColumn
 * @param {Number} cropDimensions.endRow
 * @param {Number} cropDimensions.endColumn
 * @param {module:engine/model/writer~Writer} writer
 * @returns {module:engine/model/element~Element}
 */
function cropTableToDimensions( sourceTable, cropDimensions, writer ) {
	const { startRow, startColumn, endRow, endColumn } = cropDimensions;

	// Create empty table with empty rows equal to crop height.
	const croppedTable = writer.createElement( 'table' );
	const cropHeight = endRow - startRow + 1;

	for ( let i = 0; i < cropHeight; i++ ) {
		writer.insertElement( 'tableRow', croppedTable, 'end' );
	}

	const tableMap = [ ...new TableWalker( sourceTable, { startRow, endRow, startColumn, endColumn, includeAllSlots: true } ) ];

	// Iterate over source table slots (including empty - spanned - ones).
	for ( const { row: sourceRow, column: sourceColumn, cell: tableCell, isAnchor, cellAnchorRow, cellAnchorColumn } of tableMap ) {
		// Row index in cropped table.
		const rowInCroppedTable = sourceRow - startRow;
		const row = croppedTable.getChild( rowInCroppedTable );

		// For empty slots: fill the gap with empty table cell.
		if ( !isAnchor ) {
			// But fill the gap only if the spanning cell is anchored outside cropped area.
			// In the table from method jsdoc those cells are: "c" & "f".
			if ( cellAnchorRow < startRow || cellAnchorColumn < startColumn ) {
				createEmptyTableCell( writer, writer.createPositionAt( row, 'end' ) );
			}
		}
		// Otherwise clone the cell with all children and trim if it exceeds cropped area.
		else {
			const tableCellCopy = writer.cloneElement( tableCell );

			writer.append( tableCellCopy, row );

			// Trim table if it exceeds cropped area.
			// In the table from method jsdoc those cells are: "g" & "m".
			trimTableCellIfNeeded( tableCellCopy, sourceRow, sourceColumn, endRow, endColumn, writer );
		}
	}

	// Adjust heading rows & columns in cropped table if crop selection includes headings parts.
	addHeadingsToCroppedTable( croppedTable, sourceTable, startRow, startColumn, writer );

	return croppedTable;
}

/**
 * Returns slot info of cells that starts above and overlaps a given row.
 *
 * In a table below, passing `overlapRow = 3`
 *
 *		   ┌───┬───┬───┬───┬───┐
 *		0  │ a │ b │ c │ d │ e │
 *		   │   ├───┼───┼───┼───┤
 *		1  │   │ f │ g │ h │ i │
 *		   ├───┤   ├───┼───┤   │
 *		2  │ j │   │ k │ l │   │
 *		   │   │   │   ├───┼───┤
 *		3  │   │   │   │ m │ n │  <- overlap row to check
 *		   ├───┼───┤   │   ├───│
 *		4  │ o │ p │   │   │ q │
 *		   └───┴───┴───┴───┴───┘
 *
 * will return slot info for cells: "j", "f", "k".
 *
 * @param {module:engine/model/element~Element} table The table to check.
 * @param {Number} overlapRow The index of the row to check.
 * @param {Number} [startRow=0] A row to start analysis. Use it when it is known that the cells above that row will not overlap.
 * @returns {Array.<module:table/tablewalker~TableSlot>}
 */
function getVerticallyOverlappingCells( table, overlapRow, startRow = 0 ) {
	const cells = [];

	const tableWalker = new TableWalker( table, { startRow, endRow: overlapRow - 1 } );

	for ( const slotInfo of tableWalker ) {
		const { row, cellHeight } = slotInfo;
		const cellEndRow = row + cellHeight - 1;

		if ( row < overlapRow && overlapRow <= cellEndRow ) {
			cells.push( slotInfo );
		}
	}

	return cells;
}

/**
 * Splits the table cell horizontally.
 *
 * @param {module:engine/model/element~Element} tableCell
 * @param {Number} splitRow
 * @param {module:engine/model/writer~Writer} writer
 * @returns {module:engine/model/element~Element} Created table cell.
 */
function splitHorizontally( tableCell, splitRow, writer ) {
	const tableRow = tableCell.parent;
	const table = tableRow.parent;
	const rowIndex = tableRow.index;

	const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) );
	const newRowspan = splitRow - rowIndex;

	const newCellAttributes = {};
	const newCellRowSpan = rowspan - newRowspan;

	if ( newCellRowSpan > 1 ) {
		newCellAttributes.rowspan = newCellRowSpan;
	}

	const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );

	if ( colspan > 1 ) {
		newCellAttributes.colspan = colspan;
	}

	const startRow = rowIndex;
	const endRow = startRow + newRowspan;
	const tableMap = [ ...new TableWalker( table, { startRow, endRow, includeAllSlots: true } ) ];

	let newCell = null;
	let columnIndex;

	for ( const tableSlot of tableMap ) {
		const { row, column, cell } = tableSlot;

		if ( cell === tableCell && columnIndex === undefined ) {
			columnIndex = column;
		}

		if ( columnIndex !== undefined && columnIndex === column && row === endRow ) {
			newCell = createEmptyTableCell( writer, tableSlot.getPositionBefore(), newCellAttributes );
		}
	}

	// Update the rowspan attribute after updating table.
	updateNumericAttribute( 'rowspan', newRowspan, tableCell, writer );

	return newCell;
}

/**
 * Returns slot info of cells that starts before and overlaps a given column.
 *
 * In a table below, passing `overlapColumn = 3`
 *
 *		  0   1   2   3   4
 *		┌───────┬───────┬───┐
 *		│ a     │ b     │ c │
 *		│───┬───┴───────┼───┤
 *		│ d │ e         │ f │
 *		├───┼───┬───────┴───┤
 *		│ g │ h │ i         │
 *		├───┼───┼───┬───────┤
 *		│ j │ k │ l │ m     │
 *		├───┼───┴───┼───┬───┤
 *		│ n │ o     │ p │ q │
 *		└───┴───────┴───┴───┘
 *		              ^
 *		              Overlap column to check
 *
 * will return slot info for cells: "b", "e", "i".
 *
 * @param {module:engine/model/element~Element} table The table to check.
 * @param {Number} overlapColumn The index of the column to check.
 * @returns {Array.<module:table/tablewalker~TableSlot>}
 */
function getHorizontallyOverlappingCells( table, overlapColumn ) {
	const cellsToSplit = [];

	const tableWalker = new TableWalker( table );

	for ( const slotInfo of tableWalker ) {
		const { column, cellWidth } = slotInfo;
		const cellEndColumn = column + cellWidth - 1;

		if ( column < overlapColumn && overlapColumn <= cellEndColumn ) {
			cellsToSplit.push( slotInfo );
		}
	}

	return cellsToSplit;
}

/**
 * Splits the table cell vertically.
 *
 * @param {module:engine/model/element~Element} tableCell
 * @param {Number} columnIndex The table cell column index.
 * @param {Number} splitColumn The index of column to split cell on.
 * @param {module:engine/model/writer~Writer} writer
 * @returns {module:engine/model/element~Element} Created table cell.
 */
function splitVertically( tableCell, columnIndex, splitColumn, writer ) {
	const colspan = parseInt( tableCell.getAttribute( 'colspan' ) );
	const newColspan = splitColumn - columnIndex;

	const newCellAttributes = {};
	const newCellColSpan = colspan - newColspan;

	if ( newCellColSpan > 1 ) {
		newCellAttributes.colspan = newCellColSpan;
	}

	const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );

	if ( rowspan > 1 ) {
		newCellAttributes.rowspan = rowspan;
	}

	const newCell = createEmptyTableCell( writer, writer.createPositionAfter( tableCell ), newCellAttributes );

	// Update the colspan attribute after updating table.
	updateNumericAttribute( 'colspan', newColspan, tableCell, writer );

	return newCell;
}

/**
 * Adjusts table cell dimensions to not exceed limit row and column.
 *
 * If table cell width (or height) covers a column (or row) that is after a limit column (or row)
 * this method will trim "colspan" (or "rowspan") attribute so the table cell will fit in a defined limits.
 *
 * @param {module:engine/model/element~Element} tableCell
 * @param {Number} cellRow
 * @param {Number} cellColumn
 * @param {Number} limitRow
 * @param {Number} limitColumn
 * @param {module:engine/model/writer~Writer} writer
 */
function trimTableCellIfNeeded( tableCell, cellRow, cellColumn, limitRow, limitColumn, writer ) {
	const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
	const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );

	const endColumn = cellColumn + colspan - 1;

	if ( endColumn > limitColumn ) {
		const trimmedSpan = limitColumn - cellColumn + 1;

		updateNumericAttribute( 'colspan', trimmedSpan, tableCell, writer, 1 );
	}

	const endRow = cellRow + rowspan - 1;

	if ( endRow > limitRow ) {
		const trimmedSpan = limitRow - cellRow + 1;

		updateNumericAttribute( 'rowspan', trimmedSpan, tableCell, writer, 1 );
	}
}

// Sets proper heading attributes to a cropped table.
function addHeadingsToCroppedTable( croppedTable, sourceTable, startRow, startColumn, writer ) {
	const headingRows = parseInt( sourceTable.getAttribute( 'headingRows' ) || 0 );

	if ( headingRows > 0 ) {
		const headingRowsInCrop = headingRows - startRow;
		updateNumericAttribute( 'headingRows', headingRowsInCrop, croppedTable, writer, 0 );
	}

	const headingColumns = parseInt( sourceTable.getAttribute( 'headingColumns' ) || 0 );

	if ( headingColumns > 0 ) {
		const headingColumnsInCrop = headingColumns - startColumn;
		updateNumericAttribute( 'headingColumns', headingColumnsInCrop, croppedTable, writer, 0 );
	}
}

/**
 * Removes columns that have no cells anchored.
 *
 * In table below:
 *
 *     +----+----+----+----+----+----+----+
 *     | 00 | 01      | 03 | 04      | 06 |
 *     +----+----+----+----+         +----+
 *     | 10 | 11      | 13 |         | 16 |
 *     +----+----+----+----+----+----+----+
 *     | 20 | 21      | 23 | 24      | 26 |
 *     +----+----+----+----+----+----+----+
 *                  ^--- empty ---^
 *
 * Will remove columns 2 and 5.
 *
 * **Note:** This is a low-level helper method for clearing invalid model state when doing table modifications.
 * To remove a column from a table use {@link module:table/tableutils~TableUtils#removeColumns `TableUtils.removeColumns()`}.
 *
 * @protected
 * @param {module:engine/model/element~Element} table
 * @param {module:table/tableutils~TableUtils} tableUtils
 * @returns {Boolean} True if removed some columns.
 */
function removeEmptyColumns( table, tableUtils ) {
	const width = tableUtils.getColumns( table );
	const columnsMap = new Array( width ).fill( 0 );

	for ( const { column } of new TableWalker( table ) ) {
		columnsMap[ column ]++;
	}

	const emptyColumns = columnsMap.reduce( ( result, cellsCount, column ) => {
		return cellsCount ? result : [ ...result, column ];
	}, [] );

	if ( emptyColumns.length > 0 ) {
		// Remove only last empty column because it will recurrently trigger removing empty rows.
		const emptyColumn = emptyColumns[ emptyColumns.length - 1 ];

		// @if CK_DEBUG_TABLE // console.log( `Removing empty column: ${ emptyColumn }.` );
		tableUtils.removeColumns( table, { at: emptyColumn } );

		return true;
	}

	return false;
}

/**
 * Removes rows that have no cells anchored.
 *
 * In table below:
 *
 *     +----+----+----+
 *     | 00 | 01 | 02 |
 *     +----+----+----+
 *     | 10 | 11 | 12 |
 *     +    +    +    +
 *     |    |    |    | <-- empty
 *     +----+----+----+
 *     | 30 | 31 | 32 |
 *     +----+----+----+
 *     | 40      | 42 |
 *     +         +    +
 *     |         |    | <-- empty
 *     +----+----+----+
 *     | 60 | 61 | 62 |
 *     +----+----+----+
 *
 * Will remove rows 2 and 5.
 *
 * **Note:** This is a low-level helper method for clearing invalid model state when doing table modifications.
 * To remove a row from a table use {@link module:table/tableutils~TableUtils#removeRows `TableUtils.removeRows()`}.
 *
 * @protected
 * @param {module:engine/model/element~Element} table
 * @param {module:table/tableutils~TableUtils} tableUtils
 * @returns {Boolean} True if removed some rows.
 */
function removeEmptyRows( table, tableUtils ) {
	const emptyRows = [];
	const tableRowCount = tableUtils.getRows( table );

	for ( let rowIndex = 0; rowIndex < tableRowCount; rowIndex++ ) {
		const tableRow = table.getChild( rowIndex );

		if ( tableRow.isEmpty ) {
			emptyRows.push( rowIndex );
		}
	}

	if ( emptyRows.length > 0 ) {
		// Remove only last empty row because it will recurrently trigger removing empty columns.
		const emptyRow = emptyRows[ emptyRows.length - 1 ];

		// @if CK_DEBUG_TABLE // console.log( `Removing empty row: ${ emptyRow }.` );
		tableUtils.removeRows( table, { at: emptyRow } );

		return true;
	}

	return false;
}

/**
 * Removes rows and columns that have no cells anchored.
 *
 * In table below:
 *
 *     +----+----+----+----+
 *     | 00      | 02      |
 *     +----+----+         +
 *     | 10      |         |
 *     +----+----+----+----+
 *     | 20      | 22 | 23 |
 *     +         +    +    +
 *     |         |    |    | <-- empty row
 *     +----+----+----+----+
 *             ^--- empty column
 *
 * Will remove row 3 and column 1.
 *
 * **Note:** This is a low-level helper method for clearing invalid model state when doing table modifications.
 * To remove a rows from a table use {@link module:table/tableutils~TableUtils#removeRows `TableUtils.removeRows()`} and
 * {@link module:table/tableutils~TableUtils#removeColumns `TableUtils.removeColumns()`} to remove a column.
 *
 * @protected
 * @param {module:engine/model/element~Element} table
 * @param {module:table/tableutils~TableUtils} tableUtils
 */
function removeEmptyRowsColumns( table, tableUtils ) {
	const removedColumns = removeEmptyColumns( table, tableUtils );

	// If there was some columns removed then cleaning empty rows was already triggered.
	if ( !removedColumns ) {
		removeEmptyRows( table, tableUtils );
	}
}

/**
 * Returns adjusted last row index if selection covers part of a row with empty slots (spanned by other cells).
 * The `dimensions.lastRow` is equal to last row index but selection might be bigger.
 *
 * This happens *only* on rectangular selection so we analyze a case like this:
 *
 *        +---+---+---+---+
 *      0 | a | b | c | d |
 *        +   +   +---+---+
 *      1 |   | e | f | g |
 *        +   +---+   +---+
 *      2 |   | h |   | i | <- last row, each cell has rowspan = 2,
 *        +   +   +   +   +    so we need to return 3, not 2
 *      3 |   |   |   |   |
 *        +---+---+---+---+
 *
 * @param {module:engine/model/element~Element} table
 * @param {Object} dimensions
 * @param {Number} dimensions.firstRow
 * @param {Number} dimensions.firstColumn
 * @param {Number} dimensions.lastRow
 * @param {Number} dimensions.lastColumn
 * @returns {Number} Adjusted last row index.
 */
function adjustLastRowIndex( table, dimensions ) {
	const lastRowMap = Array.from( new TableWalker( table, {
		startColumn: dimensions.firstColumn,
		endColumn: dimensions.lastColumn,
		row: dimensions.lastRow
	} ) );

	const everyCellHasSingleRowspan = lastRowMap.every( ( { cellHeight } ) => cellHeight === 1 );

	// It is a "flat" row, so the last row index is OK.
	if ( everyCellHasSingleRowspan ) {
		return dimensions.lastRow;
	}

	// Otherwise get any cell's rowspan and adjust the last row index.
	const rowspanAdjustment = lastRowMap[ 0 ].cellHeight - 1;
	return dimensions.lastRow + rowspanAdjustment;
}

/**
 * Returns adjusted last column index if selection covers part of a column with empty slots (spanned by other cells).
 * The `dimensions.lastColumn` is equal to last column index but selection might be bigger.
 *
 * This happens *only* on rectangular selection so we analyze a case like this:
 *
 *       0   1   2   3
 *     +---+---+---+---+
 *     | a             |
 *     +---+---+---+---+
 *     | b | c | d     |
 *     +---+---+---+---+
 *     | e     | f     |
 *     +---+---+---+---+
 *     | g | h         |
 *     +---+---+---+---+
 *               ^
 *              last column, each cell has colspan = 2, so we need to return 3, not 2
 *
 * @param {module:engine/model/element~Element} table
 * @param {Object} dimensions
 * @param {Number} dimensions.firstRow
 * @param {Number} dimensions.firstColumn
 * @param {Number} dimensions.lastRow
 * @param {Number} dimensions.lastColumn
 * @returns {Number} Adjusted last column index.
 */
function adjustLastColumnIndex( table, dimensions ) {
	const lastColumnMap = Array.from( new TableWalker( table, {
		startRow: dimensions.firstRow,
		endRow: dimensions.lastRow,
		column: dimensions.lastColumn
	} ) );

	const everyCellHasSingleColspan = lastColumnMap.every( ( { cellWidth } ) => cellWidth === 1 );

	// It is a "flat" column, so the last column index is OK.
	if ( everyCellHasSingleColspan ) {
		return dimensions.lastColumn;
	}

	// Otherwise get any cell's colspan and adjust the last column index.
	const colspanAdjustment = lastColumnMap[ 0 ].cellWidth - 1;
	return dimensions.lastColumn + colspanAdjustment;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/mergecellcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/mergecellcommand
 */






/**
 * The merge cell command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'mergeTableCellRight'`, `'mergeTableCellLeft'`,
 * `'mergeTableCellUp'` and `'mergeTableCellDown'` editor commands.
 *
 * To merge a table cell at the current selection with another cell, execute the command corresponding with the preferred direction.
 *
 * For example, to merge with a cell to the right:
 *
 *		editor.execute( 'mergeTableCellRight' );
 *
 * **Note**: If a table cell has a different [`rowspan`](https://www.w3.org/TR/html50/tabular-data.html#attr-tdth-rowspan)
 * (for `'mergeTableCellRight'` and `'mergeTableCellLeft'`) or [`colspan`](https://www.w3.org/TR/html50/tabular-data.html#attr-tdth-colspan)
 * (for `'mergeTableCellUp'` and `'mergeTableCellDown'`), the command will be disabled.
 *
 * @extends module:core/command~Command
 */
class MergeCellCommand extends command_Command {
	/**
	 * Creates a new `MergeCellCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor on which this command will be used.
	 * @param {Object} options
	 * @param {String} options.direction Indicates which cell to merge with the currently selected one.
	 * Possible values are: `'left'`, `'right'`, `'up'` and `'down'`.
	 */
	constructor( editor, options ) {
		super( editor );

		/**
		 * The direction that indicates which cell will be merged with the currently selected one.
		 *
		 * @readonly
		 * @member {String} #direction
		 */
		this.direction = options.direction;

		/**
		 * Whether the merge is horizontal (left/right) or vertical (up/down).
		 *
		 * @readonly
		 * @member {Boolean} #isHorizontal
		 */
		this.isHorizontal = this.direction == 'right' || this.direction == 'left';
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const cellToMerge = this._getMergeableCell();

		this.value = cellToMerge;
		this.isEnabled = !!cellToMerge;
	}

	/**
	 * Executes the command.
	 *
	 * Depending on the command's {@link #direction} value, it will merge the cell that is to the `'left'`, `'right'`, `'up'` or `'down'`.
	 *
	 * @fires execute
	 */
	execute() {
		const model = this.editor.model;
		const doc = model.document;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const tableCell = tableUtils.getTableCellsContainingSelection( doc.selection )[ 0 ];

		const cellToMerge = this.value;
		const direction = this.direction;

		model.change( writer => {
			const isMergeNext = direction == 'right' || direction == 'down';

			// The merge mechanism is always the same so sort cells to be merged.
			const cellToExpand = isMergeNext ? tableCell : cellToMerge;
			const cellToRemove = isMergeNext ? cellToMerge : tableCell;

			// Cache the parent of cell to remove for later check.
			const removedTableCellRow = cellToRemove.parent;

			mergeTableCells( cellToRemove, cellToExpand, writer );

			const spanAttribute = this.isHorizontal ? 'colspan' : 'rowspan';
			const cellSpan = parseInt( tableCell.getAttribute( spanAttribute ) || 1 );
			const cellToMergeSpan = parseInt( cellToMerge.getAttribute( spanAttribute ) || 1 );

			// Update table cell span attribute and merge set selection on merged contents.
			writer.setAttribute( spanAttribute, cellSpan + cellToMergeSpan, cellToExpand );
			writer.setSelection( writer.createRangeIn( cellToExpand ) );

			const tableUtils = this.editor.plugins.get( 'TableUtils' );
			const table = removedTableCellRow.findAncestor( 'table' );

			// Remove empty rows and columns after merging.
			removeEmptyRowsColumns( table, tableUtils );
		} );
	}

	/**
	 * Returns a cell that can be merged with the current cell depending on the command's direction.
	 *
	 * @returns {module:engine/model/element~Element|undefined}
	 * @private
	 */
	_getMergeableCell() {
		const model = this.editor.model;
		const doc = model.document;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const tableCell = tableUtils.getTableCellsContainingSelection( doc.selection )[ 0 ];

		if ( !tableCell ) {
			return;
		}

		// First get the cell on proper direction.
		const cellToMerge = this.isHorizontal ?
			getHorizontalCell( tableCell, this.direction, tableUtils ) :
			getVerticalCell( tableCell, this.direction, tableUtils );

		if ( !cellToMerge ) {
			return;
		}

		// If found check if the span perpendicular to merge direction is equal on both cells.
		const spanAttribute = this.isHorizontal ? 'rowspan' : 'colspan';
		const span = parseInt( tableCell.getAttribute( spanAttribute ) || 1 );

		const cellToMergeSpan = parseInt( cellToMerge.getAttribute( spanAttribute ) || 1 );

		if ( cellToMergeSpan === span ) {
			return cellToMerge;
		}
	}
}

// Returns the cell that can be merged horizontally.
//
// @param {module:engine/model/element~Element} tableCell
// @param {String} direction
// @param {module:table/tableutils~TableUtils} tableUtils
// @returns {module:engine/model/node~Node|null}
function getHorizontalCell( tableCell, direction, tableUtils ) {
	const tableRow = tableCell.parent;
	const table = tableRow.parent;
	const horizontalCell = direction == 'right' ? tableCell.nextSibling : tableCell.previousSibling;
	const hasHeadingColumns = ( table.getAttribute( 'headingColumns' ) || 0 ) > 0;

	if ( !horizontalCell ) {
		return;
	}

	// Sort cells:
	const cellOnLeft = direction == 'right' ? tableCell : horizontalCell;
	const cellOnRight = direction == 'right' ? horizontalCell : tableCell;

	// Get their column indexes:
	const { column: leftCellColumn } = tableUtils.getCellLocation( cellOnLeft );
	const { column: rightCellColumn } = tableUtils.getCellLocation( cellOnRight );

	const leftCellSpan = parseInt( cellOnLeft.getAttribute( 'colspan' ) || 1 );

	const isCellOnLeftInHeadingColumn = isHeadingColumnCell( tableUtils, cellOnLeft, table );
	const isCellOnRightInHeadingColumn = isHeadingColumnCell( tableUtils, cellOnRight, table );

	// We cannot merge heading columns cells with regular cells.
	if ( hasHeadingColumns && isCellOnLeftInHeadingColumn != isCellOnRightInHeadingColumn ) {
		return;
	}

	// The cell on the right must have index that is distant to the cell on the left by the left cell's width (colspan).
	const cellsAreTouching = leftCellColumn + leftCellSpan === rightCellColumn;

	// If the right cell's column index is different it means that there are rowspanned cells between them.
	return cellsAreTouching ? horizontalCell : undefined;
}

// Returns the cell that can be merged vertically.
//
// @param {module:engine/model/element~Element} tableCell
// @param {String} direction
// @param {module:table/tableutils~TableUtils} tableUtils
// @returns {module:engine/model/node~Node|null}
function getVerticalCell( tableCell, direction, tableUtils ) {
	const tableRow = tableCell.parent;
	const table = tableRow.parent;

	const rowIndex = table.getChildIndex( tableRow );

	// Don't search for mergeable cell if direction points out of the table.
	if ( ( direction == 'down' && rowIndex === tableUtils.getRows( table ) - 1 ) || ( direction == 'up' && rowIndex === 0 ) ) {
		return;
	}

	const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
	const headingRows = table.getAttribute( 'headingRows' ) || 0;

	const isMergeWithBodyCell = direction == 'down' && ( rowIndex + rowspan ) === headingRows;
	const isMergeWithHeadCell = direction == 'up' && rowIndex === headingRows;

	// Don't search for mergeable cell if direction points out of the current table section.
	if ( headingRows && ( isMergeWithBodyCell || isMergeWithHeadCell ) ) {
		return;
	}

	const currentCellRowSpan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
	const rowOfCellToMerge = direction == 'down' ? rowIndex + currentCellRowSpan : rowIndex;

	const tableMap = [ ...new TableWalker( table, { endRow: rowOfCellToMerge } ) ];

	const currentCellData = tableMap.find( value => value.cell === tableCell );
	const mergeColumn = currentCellData.column;

	const cellToMergeData = tableMap.find( ( { row, cellHeight, column } ) => {
		if ( column !== mergeColumn ) {
			return false;
		}

		if ( direction == 'down' ) {
			// If merging a cell below the mergeRow is already calculated.
			return row === rowOfCellToMerge;
		} else {
			// If merging a cell above calculate if it spans to mergeRow.
			return rowOfCellToMerge === row + cellHeight;
		}
	} );

	return cellToMergeData && cellToMergeData.cell;
}

// Merges two table cells. It will ensure that after merging cells with an empty paragraph, the resulting table cell will only have one
// paragraph. If one of the merged table cells is empty, the merged table cell will have the contents of the non-empty table cell.
// If both are empty, the merged table cell will have only one empty paragraph.
//
// @param {module:engine/model/element~Element} cellToRemove
// @param {module:engine/model/element~Element} cellToExpand
// @param {module:engine/model/writer~Writer} writer
function mergeTableCells( cellToRemove, cellToExpand, writer ) {
	if ( !isEmpty( cellToRemove ) ) {
		if ( isEmpty( cellToExpand ) ) {
			writer.remove( writer.createRangeIn( cellToExpand ) );
		}

		writer.move( writer.createRangeIn( cellToRemove ), writer.createPositionAt( cellToExpand, 'end' ) );
	}

	// Remove merged table cell.
	writer.remove( cellToRemove );
}

// Checks if the passed table cell contains an empty paragraph.
//
// @param {module:engine/model/element~Element} tableCell
// @returns {Boolean}
function isEmpty( tableCell ) {
	return tableCell.childCount == 1 && tableCell.getChild( 0 ).is( 'element', 'paragraph' ) && tableCell.getChild( 0 ).isEmpty;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/removerowcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/removerowcommand
 */



/**
 * The remove row command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'removeTableRow'` editor command.
 *
 * To remove the row containing the selected cell, execute the command:
 *
 *		editor.execute( 'removeTableRow' );
 *
 * @extends module:core/command~Command
 */
class RemoveRowCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const selectedCells = tableUtils.getSelectionAffectedTableCells( this.editor.model.document.selection );
		const firstCell = selectedCells[ 0 ];

		if ( firstCell ) {
			const table = firstCell.findAncestor( 'table' );
			const tableRowCount = this.editor.plugins.get( 'TableUtils' ).getRows( table );
			const lastRowIndex = tableRowCount - 1;

			const selectedRowIndexes = tableUtils.getRowIndexes( selectedCells );

			const areAllRowsSelected = selectedRowIndexes.first === 0 && selectedRowIndexes.last === lastRowIndex;

			// Disallow selecting whole table -> delete whole table should be used instead.
			this.isEnabled = !areAllRowsSelected;
		} else {
			this.isEnabled = false;
		}
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const model = this.editor.model;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );

		const referenceCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const removedRowIndexes = tableUtils.getRowIndexes( referenceCells );

		const firstCell = referenceCells[ 0 ];
		const table = firstCell.findAncestor( 'table' );

		const columnIndexToFocus = tableUtils.getCellLocation( firstCell ).column;

		model.change( writer => {
			const rowsToRemove = removedRowIndexes.last - removedRowIndexes.first + 1;

			tableUtils.removeRows( table, {
				at: removedRowIndexes.first,
				rows: rowsToRemove
			} );

			const cellToFocus = getCellToFocus( table, removedRowIndexes.first, columnIndexToFocus, tableUtils.getRows( table ) );

			writer.setSelection( writer.createPositionAt( cellToFocus, 0 ) );
		} );
	}
}

// Returns a cell that should be focused before removing the row, belonging to the same column as the currently focused cell.
// * If the row was not the last one, the cell to focus will be in the row that followed it (before removal).
// * If the row was the last one, the cell to focus will be in the row that preceded it (before removal).
function getCellToFocus( table, removedRowIndex, columnToFocus, tableRowCount ) {
	// Don't go beyond last row's index.
	const row = table.getChild( Math.min( removedRowIndex, tableRowCount - 1 ) );

	// Default to first table cell.
	let cellToFocus = row.getChild( 0 );
	let column = 0;

	for ( const tableCell of row.getChildren() ) {
		if ( column > columnToFocus ) {
			return cellToFocus;
		}

		cellToFocus = tableCell;
		column += parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
	}

	return cellToFocus;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/removecolumncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/removecolumncommand
 */





/**
 * The remove column command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'removeTableColumn'` editor command.
 *
 * To remove the column containing the selected cell, execute the command:
 *
 *		editor.execute( 'removeTableColumn' );
 *
 * @extends module:core/command~Command
 */
class RemoveColumnCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const selectedCells = tableUtils.getSelectionAffectedTableCells( this.editor.model.document.selection );
		const firstCell = selectedCells[ 0 ];

		if ( firstCell ) {
			const table = firstCell.findAncestor( 'table' );
			const tableColumnCount = tableUtils.getColumns( table );

			const { first, last } = tableUtils.getColumnIndexes( selectedCells );

			this.isEnabled = last - first < ( tableColumnCount - 1 );
		} else {
			this.isEnabled = false;
		}
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const [ firstCell, lastCell ] = getBoundaryCells( this.editor.model.document.selection, tableUtils );
		const table = firstCell.parent.parent;

		// Cache the table before removing or updating colspans.
		const tableMap = [ ...new TableWalker( table ) ];

		// Store column indexes of removed columns.
		const removedColumnIndexes = {
			first: tableMap.find( value => value.cell === firstCell ).column,
			last: tableMap.find( value => value.cell === lastCell ).column
		};

		const cellToFocus = removecolumncommand_getCellToFocus( tableMap, firstCell, lastCell, removedColumnIndexes );

		this.editor.model.change( writer => {
			const columnsToRemove = removedColumnIndexes.last - removedColumnIndexes.first + 1;

			this.editor.plugins.get( 'TableUtils' ).removeColumns( table, {
				at: removedColumnIndexes.first,
				columns: columnsToRemove
			} );

			writer.setSelection( writer.createPositionAt( cellToFocus, 0 ) );
		} );
	}
}

// Returns a proper table cell to focus after removing a column.
// - selection is on last table cell it will return previous cell.
function removecolumncommand_getCellToFocus( tableMap, firstCell, lastCell, removedColumnIndexes ) {
	const colspan = parseInt( lastCell.getAttribute( 'colspan' ) || 1 );

	// If the table cell is spanned over 2+ columns - it will be truncated so the selection should
	// stay in that cell.
	if ( colspan > 1 ) {
		return lastCell;
	}
	// Normally, look for the cell in the same row that precedes the first cell to put selection there ("column on the left").
	// If the deleted column is the first column of the table, there will be no predecessor: use the cell
	// from the column that follows then (also in the same row).
	else if ( firstCell.previousSibling || lastCell.nextSibling ) {
		return lastCell.nextSibling || firstCell.previousSibling;
	}
	// It can happen that table cells have no siblings in a row, for instance, when there are row spans
	// in the table (in the previous row). Then just look for the closest cell that is in a column
	// that will not be removed to put the selection there.
	else {
		// Look for any cell in a column that precedes the first removed column.
		if ( removedColumnIndexes.first ) {
			return tableMap.reverse().find( ( { column } ) => {
				return column < removedColumnIndexes.first;
			} ).cell;
		}
		// If the first removed column is the first column of the table, then
		// look for any cell that is in a column that follows the last removed column.
		else {
			return tableMap.reverse().find( ( { column } ) => {
				return column > removedColumnIndexes.last;
			} ).cell;
		}
	}
}

// Returns helper object returning the first and the last cell contained in given selection, based on DOM order.
function getBoundaryCells( selection, tableUtils ) {
	const referenceCells = tableUtils.getSelectionAffectedTableCells( selection );
	const firstCell = referenceCells[ 0 ];
	const lastCell = referenceCells.pop();

	const returnValue = [ firstCell, lastCell ];

	return firstCell.isBefore( lastCell ) ? returnValue : returnValue.reverse();
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/setheaderrowcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/setheaderrowcommand
 */






/**
 * The header row command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'setTableColumnHeader'` editor command.
 *
 * You can make the row containing the selected cell a [header](https://www.w3.org/TR/html50/tabular-data.html#the-th-element) by executing:
 *
 *		editor.execute( 'setTableRowHeader' );
 *
 * **Note:** All preceding rows will also become headers. If the current row is already a header, executing this command
 * will make it a regular row back again (including the following rows).
 *
 * @extends module:core/command~Command
 */
class SetHeaderRowCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const model = this.editor.model;
		const selectedCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const isInTable = selectedCells.length > 0;

		this.isEnabled = isInTable;

		/**
		 * Flag indicating whether the command is active. The command is active when the
		 * {@link module:engine/model/selection~Selection} is in a header row.
		 *
		 * @observable
		 * @readonly
		 * @member {Boolean} #value
		 */
		this.value = isInTable && selectedCells.every( cell => this._isInHeading( cell, cell.parent.parent ) );
	}

	/**
	 * Executes the command.
	 *
	 * When the selection is in a non-header row, the command will set the `headingRows` table attribute to cover that row.
	 *
	 * When the selection is already in a header row, it will set `headingRows` so the heading section will end before that row.
	 *
	 * @fires execute
	 * @param {Object} options
	 * @param {Boolean} [options.forceValue] If set, the command will set (`true`) or unset (`false`) the header rows according to
	 * the `forceValue` parameter instead of the current model state.
	 */
	execute( options = {} ) {
		if ( options.forceValue === this.value ) {
			return;
		}

		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const model = this.editor.model;

		const selectedCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const table = selectedCells[ 0 ].findAncestor( 'table' );

		const { first, last } = tableUtils.getRowIndexes( selectedCells );
		const headingRowsToSet = this.value ? first : last + 1;
		const currentHeadingRows = table.getAttribute( 'headingRows' ) || 0;

		model.change( writer => {
			if ( headingRowsToSet ) {
				// Changing heading rows requires to check if any of a heading cell is overlapping vertically the table head.
				// Any table cell that has a rowspan attribute > 1 will not exceed the table head so we need to fix it in rows below.
				const startRow = headingRowsToSet > currentHeadingRows ? currentHeadingRows : 0;
				const overlappingCells = getVerticallyOverlappingCells( table, headingRowsToSet, startRow );

				for ( const { cell } of overlappingCells ) {
					splitHorizontally( cell, headingRowsToSet, writer );
				}
			}

			updateNumericAttribute( 'headingRows', headingRowsToSet, table, writer, 0 );
		} );
	}

	/**
	 * Checks if a table cell is in the heading section.
	 *
	 * @param {module:engine/model/element~Element} tableCell
	 * @param {module:engine/model/element~Element} table
	 * @returns {Boolean}
	 * @private
	 */
	_isInHeading( tableCell, table ) {
		const headingRows = parseInt( table.getAttribute( 'headingRows' ) || 0 );

		return !!headingRows && tableCell.parent.index < headingRows;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/setheadercolumncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/setheadercolumncommand
 */






/**
 * The header column command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'setTableColumnHeader'` editor command.
 *
 * You can make the column containing the selected cell a [header](https://www.w3.org/TR/html50/tabular-data.html#the-th-element)
 * by executing:
 *
 *		editor.execute( 'setTableColumnHeader' );
 *
 * **Note:** All preceding columns will also become headers. If the current column is already a header, executing this command
 * will make it a regular column back again (including the following columns).
 *
 * @extends module:core/command~Command
 */
class SetHeaderColumnCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );

		const selectedCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const isInTable = selectedCells.length > 0;

		this.isEnabled = isInTable;

		/**
		 * Flag indicating whether the command is active. The command is active when the
		 * {@link module:engine/model/selection~Selection} is in a header column.
		 *
		 * @observable
		 * @readonly
		 * @member {Boolean} #value
		 */
		this.value = isInTable && selectedCells.every( cell => isHeadingColumnCell( tableUtils, cell ) );
	}

	/**
	 * Executes the command.
	 *
	 * When the selection is in a non-header column, the command will set the `headingColumns` table attribute to cover that column.
	 *
	 * When the selection is already in a header column, it will set `headingColumns` so the heading section will end before that column.
	 *
	 * @fires execute
	 * @param {Object} [options]
	 * @param {Boolean} [options.forceValue] If set, the command will set (`true`) or unset (`false`) the header columns according to
	 * the `forceValue` parameter instead of the current model state.
	 */
	execute( options = {} ) {
		if ( options.forceValue === this.value ) {
			return;
		}

		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const model = this.editor.model;
		const selectedCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const table = selectedCells[ 0 ].findAncestor( 'table' );

		const { first, last } = tableUtils.getColumnIndexes( selectedCells );
		const headingColumnsToSet = this.value ? first : last + 1;

		model.change( writer => {
			if ( headingColumnsToSet ) {
				// Changing heading columns requires to check if any of a heading cell is overlapping horizontally the table head.
				// Any table cell that has a colspan attribute > 1 will not exceed the table head so we need to fix it in columns before.
				const overlappingCells = getHorizontallyOverlappingCells( table, headingColumnsToSet );

				for ( const { cell, column } of overlappingCells ) {
					splitVertically( cell, column, headingColumnsToSet, writer );
				}
			}

			updateNumericAttribute( 'headingColumns', headingColumnsToSet, table, writer, 0 );
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableutils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableutils
 */








/**
 * The table utilities plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableUtils extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableUtils';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		this.decorate( 'insertColumns' );
		this.decorate( 'insertRows' );
	}

	/**
	 * Returns the table cell location as an object with table row and table column indexes.
	 *
	 * For instance, in the table below:
	 *
	 *		    0   1   2   3
	 *		  +---+---+---+---+
	 *		0 | a     | b | c |
	 *		  +       +   +---+
	 *		1 |       |   | d |
	 *		  +---+---+   +---+
	 *		2 | e     |   | f |
	 *		  +---+---+---+---+
	 *
	 * the method will return:
	 *
	 *		const cellA = table.getNodeByPath( [ 0, 0 ] );
	 *		editor.plugins.get( 'TableUtils' ).getCellLocation( cellA );
	 *		// will return { row: 0, column: 0 }
	 *
	 *		const cellD = table.getNodeByPath( [ 1, 0 ] );
	 *		editor.plugins.get( 'TableUtils' ).getCellLocation( cellD );
	 *		// will return { row: 1, column: 3 }
	 *
	 * @param {module:engine/model/element~Element} tableCell
	 * @returns {Object} Returns a `{row, column}` object.
	 */
	getCellLocation( tableCell ) {
		const tableRow = tableCell.parent;
		const table = tableRow.parent;

		const rowIndex = table.getChildIndex( tableRow );

		const tableWalker = new TableWalker( table, { row: rowIndex } );

		for ( const { cell, row, column } of tableWalker ) {
			if ( cell === tableCell ) {
				return { row, column };
			}
		}
	}

	/**
	 * Creates an empty table with a proper structure. The table needs to be inserted into the model,
	 * for example, by using the {@link module:engine/model/model~Model#insertContent} function.
	 *
	 *		model.change( ( writer ) => {
	 *			// Create a table of 2 rows and 7 columns:
	 *			const table = tableUtils.createTable( writer, { rows: 2, columns: 7 } );
	 *
	 *			// Insert a table to the model at the best position taking the current selection:
	 *			model.insertContent( table );
	 *		}
	 *
	 * @param {module:engine/model/writer~Writer} writer The model writer.
	 * @param {Object} options
	 * @param {Number} [options.rows=2] The number of rows to create.
	 * @param {Number} [options.columns=2] The number of columns to create.
	 * @param {Number} [options.headingRows=0] The number of heading rows.
	 * @param {Number} [options.headingColumns=0] The number of heading columns.
	 * @returns {module:engine/model/element~Element} The created table element.
	 */
	createTable( writer, options ) {
		const table = writer.createElement( 'table' );

		const rows = parseInt( options.rows ) || 2;
		const columns = parseInt( options.columns ) || 2;

		createEmptyRows( writer, table, 0, rows, columns );

		if ( options.headingRows ) {
			updateNumericAttribute( 'headingRows', Math.min( options.headingRows, rows ), table, writer, 0 );
		}

		if ( options.headingColumns ) {
			updateNumericAttribute( 'headingColumns', Math.min( options.headingColumns, columns ), table, writer, 0 );
		}

		return table;
	}

	/**
	 * Inserts rows into a table.
	 *
	 *		editor.plugins.get( 'TableUtils' ).insertRows( table, { at: 1, rows: 2 } );
	 *
	 * Assuming the table on the left, the above code will transform it to the table on the right:
	 *
	 *		row index
	 *		  0 +---+---+---+       `at` = 1,      +---+---+---+ 0
	 *		    | a | b | c |       `rows` = 2,    | a | b | c |
	 *		  1 +   +---+---+   <-- insert here    +   +---+---+ 1
	 *		    |   | d | e |                      |   |   |   |
	 *		  2 +   +---+---+       will give:     +   +---+---+ 2
	 *		    |   | f | g |                      |   |   |   |
	 *		  3 +---+---+---+                      +   +---+---+ 3
	 *		                                       |   | d | e |
	 *		                                       +   +---+---+ 4
	 *		                                       +   + f | g |
	 *		                                       +---+---+---+ 5
	 *
	 * @param {module:engine/model/element~Element} table The table model element where the rows will be inserted.
	 * @param {Object} options
	 * @param {Number} [options.at=0] The row index at which the rows will be inserted.
	 * @param {Number} [options.rows=1] The number of rows to insert.
	 * @param {Boolean|undefined} [options.copyStructureFromAbove] The flag for copying row structure. Note that
	 * the row structure will not be copied if this option is not provided.
	 */
	insertRows( table, options = {} ) {
		const model = this.editor.model;

		const insertAt = options.at || 0;
		const rowsToInsert = options.rows || 1;
		const isCopyStructure = options.copyStructureFromAbove !== undefined;
		const copyStructureFrom = options.copyStructureFromAbove ? insertAt - 1 : insertAt;

		const rows = this.getRows( table );
		const columns = this.getColumns( table );

		if ( insertAt > rows ) {
			/**
			 * The `options.at` points at a row position that does not exist.
			 *
			 * @error tableutils-insertrows-insert-out-of-range
			 */
			throw new CKEditorError(
				'tableutils-insertrows-insert-out-of-range',
				this,
				{ options }
			);
		}

		model.change( writer => {
			const headingRows = table.getAttribute( 'headingRows' ) || 0;

			// Inserting rows inside heading section requires to update `headingRows` attribute as the heading section will grow.
			if ( headingRows > insertAt ) {
				updateNumericAttribute( 'headingRows', headingRows + rowsToInsert, table, writer, 0 );
			}

			// Inserting at the end or at the beginning of a table doesn't require to calculate anything special.
			if ( !isCopyStructure && ( insertAt === 0 || insertAt === rows ) ) {
				createEmptyRows( writer, table, insertAt, rowsToInsert, columns );

				return;
			}

			// Iterate over all the rows above the inserted rows in order to check for the row-spanned cells.
			const walkerEndRow = isCopyStructure ? Math.max( insertAt, copyStructureFrom ) : insertAt;
			const tableIterator = new TableWalker( table, { endRow: walkerEndRow } );

			// Store spans of the reference row to reproduce it's structure. This array is column number indexed.
			const rowColSpansMap = new Array( columns ).fill( 1 );

			for ( const { row, column, cellHeight, cellWidth, cell } of tableIterator ) {
				const lastCellRow = row + cellHeight - 1;

				const isOverlappingInsertedRow = row < insertAt && insertAt <= lastCellRow;
				const isReferenceRow = row <= copyStructureFrom && copyStructureFrom <= lastCellRow;

				// If the cell is row-spanned and overlaps the inserted row, then reserve space for it in the row map.
				if ( isOverlappingInsertedRow ) {
					// This cell overlaps the inserted rows so we need to expand it further.
					writer.setAttribute( 'rowspan', cellHeight + rowsToInsert, cell );

					// Mark this cell with negative number to indicate how many cells should be skipped when adding the new cells.
					rowColSpansMap[ column ] = -cellWidth;
				}
				// Store the colspan from reference row.
				else if ( isCopyStructure && isReferenceRow ) {
					rowColSpansMap[ column ] = cellWidth;
				}
			}

			for ( let rowIndex = 0; rowIndex < rowsToInsert; rowIndex++ ) {
				const tableRow = writer.createElement( 'tableRow' );

				writer.insert( tableRow, table, insertAt );

				for ( let cellIndex = 0; cellIndex < rowColSpansMap.length; cellIndex++ ) {
					const colspan = rowColSpansMap[ cellIndex ];
					const insertPosition = writer.createPositionAt( tableRow, 'end' );

					// Insert the empty cell only if this slot is not row-spanned from any other cell.
					if ( colspan > 0 ) {
						createEmptyTableCell( writer, insertPosition, colspan > 1 ? { colspan } : null );
					}

					// Skip the col-spanned slots, there won't be any cells.
					cellIndex += Math.abs( colspan ) - 1;
				}
			}
		} );
	}

	/**
	 * Inserts columns into a table.
	 *
	 *		editor.plugins.get( 'TableUtils' ).insertColumns( table, { at: 1, columns: 2 } );
	 *
	 * Assuming the table on the left, the above code will transform it to the table on the right:
	 *
	 *		0   1   2   3                   0   1   2   3   4   5
	 *		+---+---+---+                   +---+---+---+---+---+
	 *		| a     | b |                   | a             | b |
	 *		+       +---+                   +               +---+
	 *		|       | c |                   |               | c |
	 *		+---+---+---+     will give:    +---+---+---+---+---+
	 *		| d | e | f |                   | d |   |   | e | f |
	 *		+---+   +---+                   +---+---+---+   +---+
	 *		| g |   | h |                   | g |   |   |   | h |
	 *		+---+---+---+                   +---+---+---+---+---+
	 *		| i         |                   | i                 |
	 *		+---+---+---+                   +---+---+---+---+---+
	 *		    ^---- insert here, `at` = 1, `columns` = 2
	 *
	 * @param {module:engine/model/element~Element} table The table model element where the columns will be inserted.
	 * @param {Object} options
	 * @param {Number} [options.at=0] The column index at which the columns will be inserted.
	 * @param {Number} [options.columns=1] The number of columns to insert.
	 */
	insertColumns( table, options = {} ) {
		const model = this.editor.model;

		const insertAt = options.at || 0;
		const columnsToInsert = options.columns || 1;

		model.change( writer => {
			const headingColumns = table.getAttribute( 'headingColumns' );

			// Inserting columns inside heading section requires to update `headingColumns` attribute as the heading section will grow.
			if ( insertAt < headingColumns ) {
				writer.setAttribute( 'headingColumns', headingColumns + columnsToInsert, table );
			}

			const tableColumns = this.getColumns( table );

			// Inserting at the end and at the beginning of a table doesn't require to calculate anything special.
			if ( insertAt === 0 || tableColumns === insertAt ) {
				for ( const tableRow of table.getChildren() ) {
					// Ignore non-row elements inside the table (e.g. caption).
					if ( !tableRow.is( 'element', 'tableRow' ) ) {
						continue;
					}

					createCells( columnsToInsert, writer, writer.createPositionAt( tableRow, insertAt ? 'end' : 0 ) );
				}

				return;
			}

			const tableWalker = new TableWalker( table, { column: insertAt, includeAllSlots: true } );

			for ( const tableSlot of tableWalker ) {
				const { row, cell, cellAnchorColumn, cellAnchorRow, cellWidth, cellHeight } = tableSlot;

				// When iterating over column the table walker outputs either:
				// - cells at given column index (cell "e" from method docs),
				// - spanned columns (spanned cell from row between cells "g" and "h" - spanned by "e", only if `includeAllSlots: true`),
				// - or a cell from the same row which spans over this column (cell "a").

				if ( cellAnchorColumn < insertAt ) {
					// If cell is anchored in previous column, it is a cell that spans over an inserted column (cell "a" & "i").
					// For such cells expand them by a number of columns inserted.
					writer.setAttribute( 'colspan', cellWidth + columnsToInsert, cell );

					// This cell will overlap cells in rows below so skip them (because of `includeAllSlots` option) - (cell "a")
					const lastCellRow = cellAnchorRow + cellHeight - 1;

					for ( let i = row; i <= lastCellRow; i++ ) {
						tableWalker.skipRow( i );
					}
				} else {
					// It's either cell at this column index or spanned cell by a row-spanned cell from row above.
					// In table above it's cell "e" and a spanned position from row below (empty cell between cells "g" and "h")
					createCells( columnsToInsert, writer, tableSlot.getPositionBefore() );
				}
			}
		} );
	}

	/**
	 * Removes rows from the given `table`.
	 *
	 * This method re-calculates the table geometry including `rowspan` attribute of table cells overlapping removed rows
	 * and table headings values.
	 *
	 *		editor.plugins.get( 'TableUtils' ).removeRows( table, { at: 1, rows: 2 } );
	 *
	 * Executing the above code in the context of the table on the left will transform its structure as presented on the right:
	 *
	 *		row index
	 *		    ┌───┬───┬───┐        `at` = 1        ┌───┬───┬───┐
	 *		  0 │ a │ b │ c │        `rows` = 2      │ a │ b │ c │ 0
	 *		    │   ├───┼───┤                        │   ├───┼───┤
	 *		  1 │   │ d │ e │  <-- remove from here  │   │ d │ g │ 1
	 *		    │   │   ├───┤        will give:      ├───┼───┼───┤
	 *		  2 │   │   │ f │                        │ h │ i │ j │ 2
	 *		    │   │   ├───┤                        └───┴───┴───┘
	 *		  3 │   │   │ g │
	 *		    ├───┼───┼───┤
	 *		  4 │ h │ i │ j │
	 *		    └───┴───┴───┘
	 *
	 * @param {module:engine/model/element~Element} table
	 * @param {Object} options
	 * @param {Number} options.at The row index at which the removing rows will start.
	 * @param {Number} [options.rows=1] The number of rows to remove.
	 */
	removeRows( table, options ) {
		const model = this.editor.model;

		const rowsToRemove = options.rows || 1;
		const rowCount = this.getRows( table );
		const first = options.at;
		const last = first + rowsToRemove - 1;

		if ( last > rowCount - 1 ) {
			/**
			 * The `options.at` param must point at existing row and `options.rows` must not exceed the rows in the table.
			 *
			 * @error tableutils-removerows-row-index-out-of-range
			 */
			throw new CKEditorError(
				'tableutils-removerows-row-index-out-of-range',
				this,
				{ table, options }
			);
		}

		model.change( writer => {
			// Removing rows from the table require that most calculations to be done prior to changing table structure.
			// Preparations must be done in the same enqueueChange callback to use the current table structure.

			// 1. Preparation - get row-spanned cells that have to be modified after removing rows.
			const { cellsToMove, cellsToTrim } = getCellsToMoveAndTrimOnRemoveRow( table, first, last );

			// 2. Execution

			// 2a. Move cells from removed rows that extends over a removed section - must be done before removing rows.
			// This will fill any gaps in a rows below that previously were empty because of row-spanned cells.
			if ( cellsToMove.size ) {
				const rowAfterRemovedSection = last + 1;
				moveCellsToRow( table, rowAfterRemovedSection, cellsToMove, writer );
			}

			// 2b. Remove all required rows.
			for ( let i = last; i >= first; i-- ) {
				writer.remove( table.getChild( i ) );
			}

			// 2c. Update cells from rows above that overlap removed section. Similar to step 2 but does not involve moving cells.
			for ( const { rowspan, cell } of cellsToTrim ) {
				updateNumericAttribute( 'rowspan', rowspan, cell, writer );
			}

			// 2d. Adjust heading rows if removed rows were in a heading section.
			updateHeadingRows( table, first, last, writer );

			// 2e. Remove empty columns (without anchored cells) if there are any.
			if ( !removeEmptyColumns( table, this ) ) {
				// If there wasn't any empty columns then we still need to check if this wasn't called
				// because of cleaning empty rows and we only removed one of them.
				removeEmptyRows( table, this );
			}
		} );
	}

	/**
	 * Removes columns from the given `table`.
	 *
	 * This method re-calculates the table geometry including the `colspan` attribute of table cells overlapping removed columns
	 * and table headings values.
	 *
	 *		editor.plugins.get( 'TableUtils' ).removeColumns( table, { at: 1, columns: 2 } );
	 *
	 * Executing the above code in the context of the table on the left will transform its structure as presented on the right:
	 *
	 *		  0   1   2   3   4                       0   1   2
	 *		┌───────────────┬───┐                   ┌───────┬───┐
	 *		│ a             │ b │                   │ a     │ b │
	 *		│               ├───┤                   │       ├───┤
	 *		│               │ c │                   │       │ c │
	 *		├───┬───┬───┬───┼───┤     will give:    ├───┬───┼───┤
	 *		│ d │ e │ f │ g │ h │                   │ d │ g │ h │
	 *		├───┼───┼───┤   ├───┤                   ├───┤   ├───┤
	 *		│ i │ j │ k │   │ l │                   │ i │   │ l │
	 *		├───┴───┴───┴───┴───┤                   ├───┴───┴───┤
	 *		│ m                 │                   │ m         │
	 *		└───────────────────┘                   └───────────┘
	 *		      ^---- remove from here, `at` = 1, `columns` = 2
	 *
	 * @param {module:engine/model/element~Element} table
	 * @param {Object} options
	 * @param {Number} options.at The row index at which the removing columns will start.
	 * @param {Number} [options.columns=1] The number of columns to remove.
	 */
	removeColumns( table, options ) {
		const model = this.editor.model;
		const first = options.at;
		const columnsToRemove = options.columns || 1;
		const last = options.at + columnsToRemove - 1;

		model.change( writer => {
			adjustHeadingColumns( table, { first, last }, writer );

			for ( let removedColumnIndex = last; removedColumnIndex >= first; removedColumnIndex-- ) {
				for ( const { cell, column, cellWidth } of [ ...new TableWalker( table ) ] ) {
					// If colspaned cell overlaps removed column decrease its span.
					if ( column <= removedColumnIndex && cellWidth > 1 && column + cellWidth > removedColumnIndex ) {
						updateNumericAttribute( 'colspan', cellWidth - 1, cell, writer );
					} else if ( column === removedColumnIndex ) {
						// The cell in removed column has colspan of 1.
						writer.remove( cell );
					}
				}
			}

			// Remove empty rows that could appear after removing columns.
			if ( !removeEmptyRows( table, this ) ) {
				// If there wasn't any empty rows then we still need to check if this wasn't called
				// because of cleaning empty columns and we only removed one of them.
				removeEmptyColumns( table, this );
			}
		} );
	}

	/**
	 * Divides a table cell vertically into several ones.
	 *
	 * The cell will be visually split into more cells by updating colspans of other cells in a column
	 * and inserting cells (columns) after that cell.
	 *
	 * In the table below, if cell "a" is split into 3 cells:
	 *
	 *		+---+---+---+
	 *		| a | b | c |
	 *		+---+---+---+
	 *		| d | e | f |
	 *		+---+---+---+
	 *
	 * it will result in the table below:
	 *
	 *		+---+---+---+---+---+
	 *		| a |   |   | b | c |
	 *		+---+---+---+---+---+
	 *		| d         | e | f |
	 *		+---+---+---+---+---+
	 *
	 * So cell "d" will get its `colspan` updated to `3` and 2 cells will be added (2 columns will be created).
	 *
	 * Splitting a cell that already has a `colspan` attribute set will distribute the cell `colspan` evenly and the remainder
	 * will be left to the original cell:
	 *
	 *		+---+---+---+
	 *		| a         |
	 *		+---+---+---+
	 *		| b | c | d |
	 *		+---+---+---+
	 *
	 * Splitting cell "a" with `colspan=3` into 2 cells will create 1 cell with a `colspan=a` and cell "a" that will have `colspan=2`:
	 *
	 *		+---+---+---+
	 *		| a     |   |
	 *		+---+---+---+
	 *		| b | c | d |
	 *		+---+---+---+
	 *
	 * @param {module:engine/model/element~Element} tableCell
	 * @param {Number} numberOfCells
	 */
	splitCellVertically( tableCell, numberOfCells = 2 ) {
		const model = this.editor.model;
		const tableRow = tableCell.parent;
		const table = tableRow.parent;

		const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
		const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );

		model.change( writer => {
			// First check - the cell spans over multiple rows so before doing anything else just split this cell.
			if ( colspan > 1 ) {
				// Get spans of new (inserted) cells and span to update of split cell.
				const { newCellsSpan, updatedSpan } = breakSpanEvenly( colspan, numberOfCells );

				updateNumericAttribute( 'colspan', updatedSpan, tableCell, writer );

				// Each inserted cell will have the same attributes:
				const newCellsAttributes = {};

				// Do not store default value in the model.
				if ( newCellsSpan > 1 ) {
					newCellsAttributes.colspan = newCellsSpan;
				}

				// Copy rowspan of split cell.
				if ( rowspan > 1 ) {
					newCellsAttributes.rowspan = rowspan;
				}

				const cellsToInsert = colspan > numberOfCells ? numberOfCells - 1 : colspan - 1;
				createCells( cellsToInsert, writer, writer.createPositionAfter( tableCell ), newCellsAttributes );
			}

			// Second check - the cell has colspan of 1 or we need to create more cells then the currently one spans over.
			if ( colspan < numberOfCells ) {
				const cellsToInsert = numberOfCells - colspan;

				// First step: expand cells on the same column as split cell.
				const tableMap = [ ...new TableWalker( table ) ];

				// Get the column index of split cell.
				const { column: splitCellColumn } = tableMap.find( ( { cell } ) => cell === tableCell );

				// Find cells which needs to be expanded vertically - those on the same column or those that spans over split cell's column.
				const cellsToUpdate = tableMap.filter( ( { cell, cellWidth, column } ) => {
					const isOnSameColumn = cell !== tableCell && column === splitCellColumn;
					const spansOverColumn = ( column < splitCellColumn && column + cellWidth > splitCellColumn );

					return isOnSameColumn || spansOverColumn;
				} );

				// Expand cells vertically.
				for ( const { cell, cellWidth } of cellsToUpdate ) {
					writer.setAttribute( 'colspan', cellWidth + cellsToInsert, cell );
				}

				// Second step: create columns after split cell.

				// Each inserted cell will have the same attributes:
				const newCellsAttributes = {};

				// Do not store default value in the model.

				// Copy rowspan of split cell.
				if ( rowspan > 1 ) {
					newCellsAttributes.rowspan = rowspan;
				}

				createCells( cellsToInsert, writer, writer.createPositionAfter( tableCell ), newCellsAttributes );

				const headingColumns = table.getAttribute( 'headingColumns' ) || 0;

				// Update heading section if split cell is in heading section.
				if ( headingColumns > splitCellColumn ) {
					updateNumericAttribute( 'headingColumns', headingColumns + cellsToInsert, table, writer );
				}
			}
		} );
	}

	/**
	 * Divides a table cell horizontally into several ones.
	 *
	 * The cell will be visually split into more cells by updating rowspans of other cells in the row and inserting rows with a single cell
	 * below.
	 *
	 * If in the table below cell "b" is split into 3 cells:
	 *
	 *		+---+---+---+
	 *		| a | b | c |
	 *		+---+---+---+
	 *		| d | e | f |
	 *		+---+---+---+
	 *
	 * It will result in the table below:
	 *
	 *		+---+---+---+
	 *		| a | b | c |
	 *		+   +---+   +
	 *		|   |   |   |
	 *		+   +---+   +
	 *		|   |   |   |
	 *		+---+---+---+
	 *		| d | e | f |
	 *		+---+---+---+
	 *
	 * So cells "a" and "b" will get their `rowspan` updated to `3` and 2 rows with a single cell will be added.
	 *
	 * Splitting a cell that already has a `rowspan` attribute set will distribute the cell `rowspan` evenly and the remainder
	 * will be left to the original cell:
	 *
	 *		+---+---+---+
	 *		| a | b | c |
	 *		+   +---+---+
	 *		|   | d | e |
	 *		+   +---+---+
	 *		|   | f | g |
	 *		+   +---+---+
	 *		|   | h | i |
	 *		+---+---+---+
	 *
	 * Splitting cell "a" with `rowspan=4` into 3 cells will create 2 cells with a `rowspan=1` and cell "a" will have `rowspan=2`:
	 *
	 *		+---+---+---+
	 *		| a | b | c |
	 *		+   +---+---+
	 *		|   | d | e |
	 *		+---+---+---+
	 *		|   | f | g |
	 *		+---+---+---+
	 *		|   | h | i |
	 *		+---+---+---+
	 *
	 * @param {module:engine/model/element~Element} tableCell
	 * @param {Number} numberOfCells
	 */
	splitCellHorizontally( tableCell, numberOfCells = 2 ) {
		const model = this.editor.model;

		const tableRow = tableCell.parent;
		const table = tableRow.parent;
		const splitCellRow = table.getChildIndex( tableRow );

		const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
		const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );

		model.change( writer => {
			// First check - the cell spans over multiple rows so before doing anything else just split this cell.
			if ( rowspan > 1 ) {
				// Cache table map before updating table.
				const tableMap = [ ...new TableWalker( table, {
					startRow: splitCellRow,
					endRow: splitCellRow + rowspan - 1,
					includeAllSlots: true
				} ) ];

				// Get spans of new (inserted) cells and span to update of split cell.
				const { newCellsSpan, updatedSpan } = breakSpanEvenly( rowspan, numberOfCells );

				updateNumericAttribute( 'rowspan', updatedSpan, tableCell, writer );

				const { column: cellColumn } = tableMap.find( ( { cell } ) => cell === tableCell );

				// Each inserted cell will have the same attributes:
				const newCellsAttributes = {};

				// Do not store default value in the model.
				if ( newCellsSpan > 1 ) {
					newCellsAttributes.rowspan = newCellsSpan;
				}

				// Copy colspan of split cell.
				if ( colspan > 1 ) {
					newCellsAttributes.colspan = colspan;
				}

				for ( const tableSlot of tableMap ) {
					const { column, row } = tableSlot;

					// As both newly created cells and the split cell might have rowspan,
					// the insertion of new cells must go to appropriate rows:
					//
					// 1. It's a row after split cell + it's height.
					const isAfterSplitCell = row >= splitCellRow + updatedSpan;
					// 2. Is on the same column.
					const isOnSameColumn = column === cellColumn;
					// 3. And it's row index is after previous cell height.
					const isInEvenlySplitRow = ( row + splitCellRow + updatedSpan ) % newCellsSpan === 0;

					if ( isAfterSplitCell && isOnSameColumn && isInEvenlySplitRow ) {
						createCells( 1, writer, tableSlot.getPositionBefore(), newCellsAttributes );
					}
				}
			}

			// Second check - the cell has rowspan of 1 or we need to create more cells than the current cell spans over.
			if ( rowspan < numberOfCells ) {
				// We already split the cell in check one so here we split to the remaining number of cells only.
				const cellsToInsert = numberOfCells - rowspan;

				// This check is needed since we need to check if there are any cells from previous rows than spans over this cell's row.
				const tableMap = [ ...new TableWalker( table, { startRow: 0, endRow: splitCellRow } ) ];

				// First step: expand cells.
				for ( const { cell, cellHeight, row } of tableMap ) {
					// Expand rowspan of cells that are either:
					// - on the same row as current cell,
					// - or are below split cell row and overlaps that row.
					if ( cell !== tableCell && row + cellHeight > splitCellRow ) {
						const rowspanToSet = cellHeight + cellsToInsert;

						writer.setAttribute( 'rowspan', rowspanToSet, cell );
					}
				}

				// Second step: create rows with single cell below split cell.
				const newCellsAttributes = {};

				// Copy colspan of split cell.
				if ( colspan > 1 ) {
					newCellsAttributes.colspan = colspan;
				}

				createEmptyRows( writer, table, splitCellRow + 1, cellsToInsert, 1, newCellsAttributes );

				// Update heading section if split cell is in heading section.
				const headingRows = table.getAttribute( 'headingRows' ) || 0;

				if ( headingRows > splitCellRow ) {
					updateNumericAttribute( 'headingRows', headingRows + cellsToInsert, table, writer );
				}
			}
		} );
	}

	/**
	 * Returns the number of columns for a given table.
	 *
	 *		editor.plugins.get( 'TableUtils' ).getColumns( table );
	 *
	 * @param {module:engine/model/element~Element} table The table to analyze.
	 * @returns {Number}
	 */
	getColumns( table ) {
		// Analyze first row only as all the rows should have the same width.
		// Using the first row without checking if it's a tableRow because we expect
		// that table will have only tableRow model elements at the beginning.
		const row = table.getChild( 0 );

		return [ ...row.getChildren() ].reduce( ( columns, row ) => {
			const columnWidth = parseInt( row.getAttribute( 'colspan' ) || 1 );

			return columns + columnWidth;
		}, 0 );
	}

	/**
	 * Returns the number of rows for a given table. Any other element present in the table model is omitted.
	 *
	 *		editor.plugins.get( 'TableUtils' ).getRows( table );
	 *
	 * @param {module:engine/model/element~Element} table The table to analyze.
	 * @returns {Number}
	 */
	getRows( table ) {
		// Rowspan not included due to #6427.
		return Array.from( table.getChildren() )
			.reduce( ( rowCount, child ) => child.is( 'element', 'tableRow' ) ? rowCount + 1 : rowCount, 0 );
	}

	/**
	 * Creates an instance of the table walker.
	 *
	 * The table walker iterates internally by traversing the table from row index = 0 and column index = 0.
	 * It walks row by row and column by column in order to output values defined in the options.
	 * By default it will output only the locations that are occupied by a cell. To include also spanned rows and columns,
	 * pass the `includeAllSlots` option.
	 *
	 * @protected
	 * @param {module:engine/model/element~Element} table A table over which the walker iterates.
	 * @param {Object} [options={}] An object with configuration.
	 * @param {Number} [options.row] A row index for which this iterator will output cells.
	 * Can't be used together with `startRow` and `endRow`.
	 * @param {Number} [options.startRow=0] A row index from which this iterator should start. Can't be used together with `row`.
	 * @param {Number} [options.endRow] A row index at which this iterator should end. Can't be used together with `row`.
	 * @param {Number} [options.column] A column index for which this iterator will output cells.
	 * Can't be used together with `startColumn` and `endColumn`.
	 * @param {Number} [options.startColumn=0] A column index from which this iterator should start. Can't be used together with `column`.
	 * @param {Number} [options.endColumn] A column index at which this iterator should end. Can't be used together with `column`.
	 * @param {Boolean} [options.includeAllSlots=false] Also return values for spanned cells.
	 */
	createTableWalker( table, options = {} ) {
		return new TableWalker( table, options );
	}

	/**
	 * Returns all model table cells that are fully selected (from the outside)
	 * within the provided model selection's ranges.
	 *
	 * To obtain the cells selected from the inside, use
	 * {@link #getTableCellsContainingSelection}.
	 *
	 * @param {module:engine/model/selection~Selection} selection
	 * @returns {Array.<module:engine/model/element~Element>}
	 */
	getSelectedTableCells( selection ) {
		const cells = [];

		for ( const range of this.sortRanges( selection.getRanges() ) ) {
			const element = range.getContainedElement();

			if ( element && element.is( 'element', 'tableCell' ) ) {
				cells.push( element );
			}
		}

		return cells;
	}

	/**
	 * Returns all model table cells that the provided model selection's ranges
	 * {@link module:engine/model/range~Range#start} inside.
	 *
	 * To obtain the cells selected from the outside, use
	 * {@link #getSelectedTableCells}.
	 *
	 * @param {module:engine/model/selection~Selection} selection
	 * @returns {Array.<module:engine/model/element~Element>}
	 */
	getTableCellsContainingSelection( selection ) {
		const cells = [];

		for ( const range of selection.getRanges() ) {
			const cellWithSelection = range.start.findAncestor( 'tableCell' );

			if ( cellWithSelection ) {
				cells.push( cellWithSelection );
			}
		}

		return cells;
	}

	/**
	 * Returns all model table cells that are either completely selected
	 * by selection ranges or host selection range
	 * {@link module:engine/model/range~Range#start start positions} inside them.
	 *
	 * Combines {@link #getTableCellsContainingSelection} and
	 * {@link #getSelectedTableCells}.
	 *
	 * @param {module:engine/model/selection~Selection} selection
	 * @returns {Array.<module:engine/model/element~Element>}
	 */
	getSelectionAffectedTableCells( selection ) {
		const selectedCells = this.getSelectedTableCells( selection );

		if ( selectedCells.length ) {
			return selectedCells;
		}

		return this.getTableCellsContainingSelection( selection );
	}

	/**
	 * Returns an object with the `first` and `last` row index contained in the given `tableCells`.
	 *
	 *		const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
	*
	*		const { first, last } = getRowIndexes( selectedTableCells );
	*
	*		console.log( `Selected rows: ${ first } to ${ last }` );
	*
	* @param {Array.<module:engine/model/element~Element>} tableCells
	* @returns {Object} Returns an object with the `first` and `last` table row indexes.
	*/
	getRowIndexes( tableCells ) {
		const indexes = tableCells.map( cell => cell.parent.index );

		return this._getFirstLastIndexesObject( indexes );
	}

	/**
	 * Returns an object with the `first` and `last` column index contained in the given `tableCells`.
	 *
	 *		const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
	*
	*		const { first, last } = getColumnIndexes( selectedTableCells );
	*
	*		console.log( `Selected columns: ${ first } to ${ last }` );
	*
	* @param {Array.<module:engine/model/element~Element>} tableCells
	* @returns {Object} Returns an object with the `first` and `last` table column indexes.
	*/
	getColumnIndexes( tableCells ) {
		const table = tableCells[ 0 ].findAncestor( 'table' );
		const tableMap = [ ...new TableWalker( table ) ];

		const indexes = tableMap
			.filter( entry => tableCells.includes( entry.cell ) )
			.map( entry => entry.column );

		return this._getFirstLastIndexesObject( indexes );
	}

	/**
	 * Checks if the selection contains cells that do not exceed rectangular selection.
	 *
	 * In a table below:
	 *
	 *		┌───┬───┬───┬───┐
	*		│ a │ b │ c │ d │
	*		├───┴───┼───┤   │
	*		│ e     │ f │   │
	*		│       ├───┼───┤
	*		│       │ g │ h │
	*		└───────┴───┴───┘
	*
	* Valid selections are these which create a solid rectangle (without gaps), such as:
	*   - a, b (two horizontal cells)
	*   - c, f (two vertical cells)
	*   - a, b, e (cell "e" spans over four cells)
	*   - c, d, f (cell d spans over a cell in the row below)
	*
	* While an invalid selection would be:
	*   - a, c (the unselected cell "b" creates a gap)
	*   - f, g, h (cell "d" spans over a cell from the row of "f" cell - thus creates a gap)
	*
	* @param {Array.<module:engine/model/element~Element>} selectedTableCells
	* @returns {Boolean}
	*/
	isSelectionRectangular( selectedTableCells ) {
		if ( selectedTableCells.length < 2 || !this._areCellInTheSameTableSection( selectedTableCells ) ) {
			return false;
		}

		// A valid selection is a fully occupied rectangle composed of table cells.
		// Below we will calculate the area of a selected table cells and the area of valid selection.
		// The area of a valid selection is defined by top-left and bottom-right cells.
		const rows = new Set();
		const columns = new Set();

		let areaOfSelectedCells = 0;

		for ( const tableCell of selectedTableCells ) {
			const { row, column } = this.getCellLocation( tableCell );
			const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
			const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );

			// Record row & column indexes of current cell.
			rows.add( row );
			columns.add( column );

			// For cells that spans over multiple rows add also the last row that this cell spans over.
			if ( rowspan > 1 ) {
				rows.add( row + rowspan - 1 );
			}

			// For cells that spans over multiple columns add also the last column that this cell spans over.
			if ( colspan > 1 ) {
				columns.add( column + colspan - 1 );
			}

			areaOfSelectedCells += ( rowspan * colspan );
		}

		// We can only merge table cells that are in adjacent rows...
		const areaOfValidSelection = getBiggestRectangleArea( rows, columns );

		return areaOfValidSelection == areaOfSelectedCells;
	}

	/**
	 * Returns array of sorted ranges.
	 *
	 * @param {Iterable.<module:engine/model/range~Range>} ranges
	 * @return {Array.<module:engine/model/range~Range>}
	 */
	sortRanges( ranges ) {
		return Array.from( ranges ).sort( compareRangeOrder );
	}

	/**
	 * Helper method to get an object with `first` and `last` indexes from an unsorted array of indexes.
	 *
	 * @private
	 * @param {Number[]} indexes
	 * @returns {Object}
	 */
	_getFirstLastIndexesObject( indexes ) {
		const allIndexesSorted = indexes.sort( ( indexA, indexB ) => indexA - indexB );

		const first = allIndexesSorted[ 0 ];
		const last = allIndexesSorted[ allIndexesSorted.length - 1 ];

		return { first, last };
	}

	/**
	 * Checks if the selection does not mix a header (column or row) with other cells.
	 *
	 * For instance, in the table below valid selections consist of cells with the same letter only.
	 * So, a-a (same heading row and column) or d-d (body cells) are valid while c-d or a-b are not.
	 *
	 * header columns
	 *		  ↓   ↓
	 *		┌───┬───┬───┬───┐
	 *		│ a │ a │ b │ b │  ← header row
	 *		├───┼───┼───┼───┤
	 *		│ c │ c │ d │ d │
	 *		├───┼───┼───┼───┤
	 *		│ c │ c │ d │ d │
	 *		└───┴───┴───┴───┘
	 *
	 * @private
	 * @param {Array.<module:engine/model/element~Element>} tableCells
	 * @returns {Boolean}
	 */
	_areCellInTheSameTableSection( tableCells ) {
		const table = tableCells[ 0 ].findAncestor( 'table' );

		const rowIndexes = this.getRowIndexes( tableCells );
		const headingRows = parseInt( table.getAttribute( 'headingRows' ) || 0 );

		// Calculating row indexes is a bit cheaper so if this check fails we can't merge.
		if ( !this._areIndexesInSameSection( rowIndexes, headingRows ) ) {
			return false;
		}

		const headingColumns = parseInt( table.getAttribute( 'headingColumns' ) || 0 );
		const columnIndexes = this.getColumnIndexes( tableCells );

		// Similarly cells must be in same column section.
		return this._areIndexesInSameSection( columnIndexes, headingColumns );
	}

	/**
	 * Unified check if table rows/columns indexes are in the same heading/body section.
	 *
	 * @private
	 * @param {Object} params
	 * @param {Number} params.first
	 * @param {Number} params.last
	 * @param {Number} headingSectionSize
	 */
	_areIndexesInSameSection( { first, last }, headingSectionSize ) {
		const firstCellIsInHeading = first < headingSectionSize;
		const lastCellIsInHeading = last < headingSectionSize;

		return firstCellIsInHeading === lastCellIsInHeading;
	}
}

// Creates empty rows at the given index in an existing table.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/element~Element} table
// @param {Number} insertAt The row index of row insertion.
// @param {Number} rows The number of rows to create.
// @param {Number} tableCellToInsert The number of cells to insert in each row.
function createEmptyRows( writer, table, insertAt, rows, tableCellToInsert, attributes = {} ) {
	for ( let i = 0; i < rows; i++ ) {
		const tableRow = writer.createElement( 'tableRow' );

		writer.insert( tableRow, table, insertAt );

		createCells( tableCellToInsert, writer, writer.createPositionAt( tableRow, 'end' ), attributes );
	}
}

// Creates cells at a given position.
//
// @param {Number} columns The number of columns to create
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/position~Position} insertPosition
function createCells( cells, writer, insertPosition, attributes = {} ) {
	for ( let i = 0; i < cells; i++ ) {
		createEmptyTableCell( writer, insertPosition, attributes );
	}
}

// Evenly distributes the span of a cell to a number of provided cells.
// The resulting spans will always be integer values.
//
// For instance breaking a span of 7 into 3 cells will return:
//
//		{ newCellsSpan: 2, updatedSpan: 3 }
//
// as two cells will have a span of 2 and the remainder will go the first cell so its span will change to 3.
//
// @param {Number} span The span value do break.
// @param {Number} numberOfCells The number of resulting spans.
// @returns {{newCellsSpan: Number, updatedSpan: Number}}
function breakSpanEvenly( span, numberOfCells ) {
	if ( span < numberOfCells ) {
		return { newCellsSpan: 1, updatedSpan: 1 };
	}

	const newCellsSpan = Math.floor( span / numberOfCells );
	const updatedSpan = ( span - newCellsSpan * numberOfCells ) + newCellsSpan;

	return { newCellsSpan, updatedSpan };
}

// Updates heading columns attribute if removing a row from head section.
function adjustHeadingColumns( table, removedColumnIndexes, writer ) {
	const headingColumns = table.getAttribute( 'headingColumns' ) || 0;

	if ( headingColumns && removedColumnIndexes.first < headingColumns ) {
		const headingsRemoved = Math.min( headingColumns - 1 /* Other numbers are 0-based */, removedColumnIndexes.last ) -
			removedColumnIndexes.first + 1;

		writer.setAttribute( 'headingColumns', headingColumns - headingsRemoved, table );
	}
}

// Calculates a new heading rows value for removing rows from heading section.
function updateHeadingRows( table, first, last, writer ) {
	const headingRows = table.getAttribute( 'headingRows' ) || 0;

	if ( first < headingRows ) {
		const newRows = last < headingRows ? headingRows - ( last - first + 1 ) : first;

		updateNumericAttribute( 'headingRows', newRows, table, writer, 0 );
	}
}

// Finds cells that will be:
// - trimmed - Cells that are "above" removed rows sections and overlap the removed section - their rowspan must be trimmed.
// - moved - Cells from removed rows section might stick out of. These cells are moved to the next row after a removed section.
//
// Sample table with overlapping & sticking out cells:
//
//      +----+----+----+----+----+
//      | 00 | 01 | 02 | 03 | 04 |
//      +----+    +    +    +    +
//      | 10 |    |    |    |    |
//      +----+----+    +    +    +
//      | 20 | 21 |    |    |    | <-- removed row
//      +    +    +----+    +    +
//      |    |    | 32 |    |    | <-- removed row
//      +----+    +    +----+    +
//      | 40 |    |    | 43 |    |
//      +----+----+----+----+----+
//
// In a table above:
// - cells to trim: '02', '03' & '04'.
// - cells to move: '21' & '32'.
function getCellsToMoveAndTrimOnRemoveRow( table, first, last ) {
	const cellsToMove = new Map();
	const cellsToTrim = [];

	for ( const { row, column, cellHeight, cell } of new TableWalker( table, { endRow: last } ) ) {
		const lastRowOfCell = row + cellHeight - 1;

		const isCellStickingOutFromRemovedRows = row >= first && row <= last && lastRowOfCell > last;

		if ( isCellStickingOutFromRemovedRows ) {
			const rowspanInRemovedSection = last - row + 1;
			const rowSpanToSet = cellHeight - rowspanInRemovedSection;

			cellsToMove.set( column, {
				cell,
				rowspan: rowSpanToSet
			} );
		}

		const isCellOverlappingRemovedRows = row < first && lastRowOfCell >= first;

		if ( isCellOverlappingRemovedRows ) {
			let rowspanAdjustment;

			// Cell fully covers removed section - trim it by removed rows count.
			if ( lastRowOfCell >= last ) {
				rowspanAdjustment = last - first + 1;
			}
			// Cell partially overlaps removed section - calculate cell's span that is in removed section.
			else {
				rowspanAdjustment = lastRowOfCell - first + 1;
			}

			cellsToTrim.push( {
				cell,
				rowspan: cellHeight - rowspanAdjustment
			} );
		}
	}
	return { cellsToMove, cellsToTrim };
}

function moveCellsToRow( table, targetRowIndex, cellsToMove, writer ) {
	const tableWalker = new TableWalker( table, {
		includeAllSlots: true,
		row: targetRowIndex
	} );

	const tableRowMap = [ ...tableWalker ];
	const row = table.getChild( targetRowIndex );

	let previousCell;

	for ( const { column, cell, isAnchor } of tableRowMap ) {
		if ( cellsToMove.has( column ) ) {
			const { cell: cellToMove, rowspan } = cellsToMove.get( column );

			const targetPosition = previousCell ?
				writer.createPositionAfter( previousCell ) :
				writer.createPositionAt( row, 0 );

			writer.move( writer.createRangeOn( cellToMove ), targetPosition );
			updateNumericAttribute( 'rowspan', rowspan, cellToMove, writer );

			previousCell = cellToMove;
		} else if ( isAnchor ) {
			// If cell is spanned then `cell` holds reference to overlapping cell. See ckeditor/ckeditor5#6502.
			previousCell = cell;
		}
	}
}

function compareRangeOrder( rangeA, rangeB ) {
	// Since table cell ranges are disjoint, it's enough to check their start positions.
	const posA = rangeA.start;
	const posB = rangeB.start;

	// Checking for equal position (returning 0) is not needed because this would be either:
	// a. Intersecting range (not allowed by model)
	// b. Collapsed range on the same position (allowed by model but should not happen).
	return posA.isBefore( posB ) ? -1 : 1;
}

// Calculates the area of a maximum rectangle that can span over the provided row & column indexes.
//
// @param {Array.<Number>} rows
// @param {Array.<Number>} columns
// @returns {Number}
function getBiggestRectangleArea( rows, columns ) {
	const rowsIndexes = Array.from( rows.values() );
	const columnIndexes = Array.from( columns.values() );

	const lastRow = Math.max( ...rowsIndexes );
	const firstRow = Math.min( ...rowsIndexes );
	const lastColumn = Math.max( ...columnIndexes );
	const firstColumn = Math.min( ...columnIndexes );

	return ( lastRow - firstRow + 1 ) * ( lastColumn - firstColumn + 1 );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/mergecellscommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/mergecellscommand
 */






/**
 * The merge cells command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'mergeTableCells'` editor command.
 *
 * For example, to merge selected table cells:
 *
 *		editor.execute( 'mergeTableCells' );
 *
 * @extends module:core/command~Command
 */
class MergeCellsCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const tableUtils = this.editor.plugins.get( TableUtils );

		const selectedTableCells = tableUtils.getSelectedTableCells( this.editor.model.document.selection );
		this.isEnabled = tableUtils.isSelectionRectangular( selectedTableCells, this.editor.plugins.get( TableUtils ) );
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 */
	execute() {
		const model = this.editor.model;
		const tableUtils = this.editor.plugins.get( TableUtils );

		model.change( writer => {
			const selectedTableCells = tableUtils.getSelectedTableCells( model.document.selection );

			// All cells will be merged into the first one.
			const firstTableCell = selectedTableCells.shift();

			// Update target cell dimensions.
			const { mergeWidth, mergeHeight } = getMergeDimensions( firstTableCell, selectedTableCells, tableUtils );
			updateNumericAttribute( 'colspan', mergeWidth, firstTableCell, writer );
			updateNumericAttribute( 'rowspan', mergeHeight, firstTableCell, writer );

			for ( const tableCell of selectedTableCells ) {
				mergecellscommand_mergeTableCells( tableCell, firstTableCell, writer );
			}

			const table = firstTableCell.findAncestor( 'table' );

			// Remove rows and columns that become empty (have no anchored cells).
			removeEmptyRowsColumns( table, tableUtils );

			writer.setSelection( firstTableCell, 'in' );
		} );
	}
}

// Merges two table cells. It will ensure that after merging cells with empty paragraphs the resulting table cell will only have one
// paragraph. If one of the merged table cells is empty, the merged table cell will have contents of the non-empty table cell.
// If both are empty, the merged table cell will have only one empty paragraph.
//
// @param {module:engine/model/element~Element} cellBeingMerged
// @param {module:engine/model/element~Element} targetCell
// @param {module:engine/model/writer~Writer} writer
function mergecellscommand_mergeTableCells( cellBeingMerged, targetCell, writer ) {
	if ( !mergecellscommand_isEmpty( cellBeingMerged ) ) {
		if ( mergecellscommand_isEmpty( targetCell ) ) {
			writer.remove( writer.createRangeIn( targetCell ) );
		}

		writer.move( writer.createRangeIn( cellBeingMerged ), writer.createPositionAt( targetCell, 'end' ) );
	}

	// Remove merged table cell.
	writer.remove( cellBeingMerged );
}

// Checks if the passed table cell contains an empty paragraph.
//
// @param {module:engine/model/element~Element} tableCell
// @returns {Boolean}
function mergecellscommand_isEmpty( tableCell ) {
	return tableCell.childCount == 1 && tableCell.getChild( 0 ).is( 'element', 'paragraph' ) && tableCell.getChild( 0 ).isEmpty;
}

function getMergeDimensions( firstTableCell, selectedTableCells, tableUtils ) {
	let maxWidthOffset = 0;
	let maxHeightOffset = 0;

	for ( const tableCell of selectedTableCells ) {
		const { row, column } = tableUtils.getCellLocation( tableCell );

		maxWidthOffset = getMaxOffset( tableCell, column, maxWidthOffset, 'colspan' );
		maxHeightOffset = getMaxOffset( tableCell, row, maxHeightOffset, 'rowspan' );
	}

	// Update table cell span attribute and merge set selection on a merged contents.
	const { row: firstCellRow, column: firstCellColumn } = tableUtils.getCellLocation( firstTableCell );

	const mergeWidth = maxWidthOffset - firstCellColumn;
	const mergeHeight = maxHeightOffset - firstCellRow;

	return { mergeWidth, mergeHeight };
}

function getMaxOffset( tableCell, start, currentMaxOffset, which ) {
	const dimensionValue = parseInt( tableCell.getAttribute( which ) || 1 );

	return Math.max( currentMaxOffset, start + dimensionValue );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/selectrowcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/selectrowcommand
 */



/**
 * The select row command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'selectTableRow'` editor command.
 *
 * To select the rows containing the selected cells, execute the command:
 *
 *		editor.execute( 'selectTableRow' );
 *
 * @extends module:core/command~Command
 */
class SelectRowCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		// It does not affect data so should be enabled in read-only mode.
		this.affectsData = false;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const selectedCells = tableUtils.getSelectionAffectedTableCells( this.editor.model.document.selection );

		this.isEnabled = selectedCells.length > 0;
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const model = this.editor.model;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const referenceCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const rowIndexes = tableUtils.getRowIndexes( referenceCells );

		const table = referenceCells[ 0 ].findAncestor( 'table' );
		const rangesToSelect = [];

		for ( let rowIndex = rowIndexes.first; rowIndex <= rowIndexes.last; rowIndex++ ) {
			for ( const cell of table.getChild( rowIndex ).getChildren() ) {
				rangesToSelect.push( model.createRangeOn( cell ) );
			}
		}

		model.change( writer => {
			writer.setSelection( rangesToSelect );
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/commands/selectcolumncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/commands/selectcolumncommand
 */





/**
 * The select column command.
 *
 * The command is registered by {@link module:table/tableediting~TableEditing} as the `'selectTableColumn'` editor command.
 *
 * To select the columns containing the selected cells, execute the command:
 *
 *		editor.execute( 'selectTableColumn' );
 *
 * @extends module:core/command~Command
 */
class SelectColumnCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		// It does not affect data so should be enabled in read-only mode.
		this.affectsData = false;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const selectedCells = tableUtils.getSelectionAffectedTableCells( this.editor.model.document.selection );

		this.isEnabled = selectedCells.length > 0;
	}

	/**
	 * @inheritDoc
	 */
	execute() {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const model = this.editor.model;
		const referenceCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const firstCell = referenceCells[ 0 ];
		const lastCell = referenceCells.pop();
		const table = firstCell.findAncestor( 'table' );

		const startLocation = tableUtils.getCellLocation( firstCell );
		const endLocation = tableUtils.getCellLocation( lastCell );

		const startColumn = Math.min( startLocation.column, endLocation.column );
		const endColumn = Math.max( startLocation.column, endLocation.column );

		const rangesToSelect = [];

		for ( const cellInfo of new TableWalker( table, { startColumn, endColumn } ) ) {
			rangesToSelect.push( model.createRangeOn( cellInfo.cell ) );
		}

		model.change( writer => {
			writer.setSelection( rangesToSelect );
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/table-layout-post-fixer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/table-layout-post-fixer
 */




/**
 * Injects a table layout post-fixer into the model.
 *
 * The role of the table layout post-fixer is to ensure that the table rows have the correct structure
 * after a {@link module:engine/model/model~Model#change `change()`} block was executed.
 *
 * The correct structure means that:
 *
 * * All table rows have the same size.
 * * None of the table cells extend vertically beyond their section (either header or body).
 * * A table cell has always at least one element as a child.
 *
 * If the table structure is not correct, the post-fixer will automatically correct it in two steps:
 *
 * 1. It will clip table cells that extend beyond their section.
 * 2. It will add empty table cells to the rows that are narrower than the widest table row.
 *
 * ## Clipping overlapping table cells
 *
 * Such situation may occur when pasting a table (or a part of a table) to the editor from external sources.
 *
 * For example, see the following table which has a cell (FOO) with the rowspan attribute (2):
 *
 *		<table headingRows="1">
 *			<tableRow>
 *				<tableCell rowspan="2"><paragraph>FOO</paragraph></tableCell>
 *				<tableCell colspan="2"><paragraph>BAR</paragraph></tableCell>
 *			</tableRow>
 *			<tableRow>
 *				<tableCell><paragraph>BAZ</paragraph></tableCell>
 *				<tableCell><paragraph>XYZ</paragraph></tableCell>
 *			</tableRow>
 *		</table>
 *
 * It will be rendered in the view as:
 *
 *		<table>
 *			<thead>
 *				<tr>
 *					<td rowspan="2">FOO</td>
 *					<td colspan="2">BAR</td>
 *				</tr>
 *			</thead>
 *			<tbody>
 *				<tr>
 *					<td>BAZ</td>
 *					<td>XYZ</td>
 *				</tr>
 *			</tbody>
 *		</table>
 *
 * In the above example the table will be rendered as a table with two rows: one in the header and second one in the body.
 * The table cell (FOO) cannot span over multiple rows as it would extend from the header to the body section.
 * The `rowspan` attribute must be changed to (1). The value (1) is the default value of the `rowspan` attribute
 * so the `rowspan` attribute will be removed from the model.
 *
 * The table cell with BAZ in the content will be in the first column of the table.
 *
 * ## Adding missing table cells
 *
 * The table post-fixer will insert empty table cells to equalize table row sizes (the number of columns).
 * The size of a table row is calculated by counting column spans of table cells, both horizontal (from the same row) and
 * vertical (from the rows above).
 *
 * In the above example, the table row in the body section of the table is narrower then the row from the header: it has two cells
 * with the default colspan (1). The header row has one cell with colspan (1) and the second with colspan (2).
 * The table cell (FOO) does not extend beyond the head section (and as such will be fixed in the first step of this post-fixer).
 * The post-fixer will add a missing table cell to the row in the body section of the table.
 *
 * The table from the above example will be fixed and rendered to the view as below:
 *
 *		<table>
 *			<thead>
 *				<tr>
 *					<td rowspan="2">FOO</td>
 *					<td colspan="2">BAR</td>
 *				</tr>
 *			</thead>
 *			<tbody>
 *				<tr>
 *					<td>BAZ</td>
 *					<td>XYZ</td>
 *				</tr>
 *			</tbody>
 *		</table>
 *
 * ## Collaboration and undo - Expectations vs post-fixer results
 *
 * The table post-fixer only ensures proper structure without a deeper analysis of the nature of the change. As such, it might lead
 * to a structure which was not intended by the user. In particular, it will also fix undo steps (in conjunction with collaboration)
 * in which the editor content might not return to the original state.
 *
 * This will usually happen when one or more users change the size of the table.
 *
 * As an example see the table below:
 *
 *		<table>
 *			<tbody>
 *				<tr>
 *					<td>11</td>
 *					<td>12</td>
 *				</tr>
 *				<tr>
 *					<td>21</td>
 *					<td>22</td>
 *				</tr>
 *			</tbody>
 *		</table>
 *
 * and the user actions:
 *
 * 1. Both users have a table with two rows and two columns.
 * 2. User A adds a column at the end of the table. This will insert empty table cells to two rows.
 * 3. User B adds a row at the end of the table. This will insert a row with two empty table cells.
 * 4. Both users will have a table as below:
 *
 *
 *		<table>
 *			<tbody>
 *				<tr>
 *					<td>11</td>
 *					<td>12</td>
 *					<td>(empty, inserted by A)</td>
 *				</tr>
 *				<tr>
 *					<td>21</td>
 *					<td>22</td>
 *					<td>(empty, inserted by A)</td>
 *				</tr>
 *				<tr>
 *					<td>(empty, inserted by B)</td>
 *					<td>(empty, inserted by B)</td>
 *				</tr>
 *			</tbody>
 *		</table>
 *
 * The last row is shorter then others so the table post-fixer will add an empty row to the last row:
 *
 *		<table>
 *			<tbody>
 *				<tr>
 *					<td>11</td>
 *					<td>12</td>
 *					<td>(empty, inserted by A)</td>
 *				</tr>
 *				<tr>
 *					<td>21</td>
 *					<td>22</td>
 *					<td>(empty, inserted by A)</td>
 *				</tr>
 *				<tr>
 *					<td>(empty, inserted by B)</td>
 *					<td>(empty, inserted by B)</td>
 *					<td>(empty, inserted by the post-fixer)</td>
 *				</tr>
 *			</tbody>
 *		</table>
 *
 * Unfortunately undo does not know the nature of the changes and depending on which user applies the post-fixer changes, undoing them
 * might lead to a broken table. If User B undoes inserting the column to the table, the undo engine will undo only the operations of
 * inserting empty cells to rows from the initial table state (row 1 and 2) but the cell in the post-fixed row will remain:
 *
 *		<table>
 *			<tbody>
 *				<tr>
 *					<td>11</td>
 *					<td>12</td>
 *				</tr>
 *				<tr>
 *					<td>21</td>
 *					<td>22</td>
 *				</tr>
 *				<tr>
 *					<td>(empty, inserted by B)</td>
 *					<td>(empty, inserted by B)</td>
 *					<td>(empty, inserted by a post-fixer)</td>
 *				</tr>
 *			</tbody>
 *		</table>
 *
 * After undo, the table post-fixer will detect that two rows are shorter than others and will fix the table to:
 *
 *		<table>
 *			<tbody>
 *				<tr>
 *					<td>11</td>
 *					<td>12</td>
 *					<td>(empty, inserted by a post-fixer after undo)</td>
 *				</tr>
 *				<tr>
 *					<td>21</td>
 *					<td>22</td>
 *					<td>(empty, inserted by a post-fixer after undo)</td>
 *				</tr>
 *				<tr>
 *					<td>(empty, inserted by B)</td>
 *					<td>(empty, inserted by B)</td>
 *					<td>(empty, inserted by a post-fixer)</td>
 *				</tr>
 *			</tbody>
 *		</table>
 * @param {module:engine/model/model~Model} model
 */
function injectTableLayoutPostFixer( model ) {
	model.document.registerPostFixer( writer => tableLayoutPostFixer( writer, model ) );
}

// The table layout post-fixer.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/model~Model} model
function tableLayoutPostFixer( writer, model ) {
	const changes = model.document.differ.getChanges();

	let wasFixed = false;

	// Do not analyze the same table more then once - may happen for multiple changes in the same table.
	const analyzedTables = new Set();

	for ( const entry of changes ) {
		let table;

		if ( entry.name == 'table' && entry.type == 'insert' ) {
			table = entry.position.nodeAfter;
		}

		// Fix table on adding/removing table cells and rows.
		if ( entry.name == 'tableRow' || entry.name == 'tableCell' ) {
			table = entry.position.findAncestor( 'table' );
		}

		// Fix table on any table's attribute change - including attributes of table cells.
		if ( isTableAttributeEntry( entry ) ) {
			table = entry.range.start.findAncestor( 'table' );
		}

		if ( table && !analyzedTables.has( table ) ) {
			// Step 1: correct rowspans of table cells if necessary.
			// The wasFixed flag should be true if any of tables in batch was fixed - might be more then one.
			wasFixed = fixTableCellsRowspan( table, writer ) || wasFixed;
			// Step 2: fix table rows sizes.
			wasFixed = fixTableRowsSizes( table, writer ) || wasFixed;

			analyzedTables.add( table );
		}
	}

	return wasFixed;
}

// Fixes the invalid value of the `rowspan` attribute because a table cell cannot vertically extend beyond the table section it belongs to.
//
// @param {module:engine/model/element~Element} table
// @param {module:engine/model/writer~Writer} writer
// @returns {Boolean} Returns `true` if the table was fixed.
function fixTableCellsRowspan( table, writer ) {
	let wasFixed = false;

	const cellsToTrim = findCellsToTrim( table );

	if ( cellsToTrim.length ) {
		// @if CK_DEBUG_TABLE // console.log( `Post-fixing table: trimming cells row-spans (${ cellsToTrim.length }).` );

		wasFixed = true;

		for ( const data of cellsToTrim ) {
			updateNumericAttribute( 'rowspan', data.rowspan, data.cell, writer, 1 );
		}
	}

	return wasFixed;
}

// Makes all table rows in a table the same size.
//
// @param {module:engine/model/element~Element} table
// @param {module:engine/model/writer~Writer} writer
// @returns {Boolean} Returns `true` if the table was fixed.
function fixTableRowsSizes( table, writer ) {
	let wasFixed = false;

	const childrenLengths = getChildrenLengths( table );
	const rowsToRemove = [];

	// Find empty rows.
	for ( const [ rowIndex, size ] of childrenLengths.entries() ) {
		// Ignore all non-row models.
		if ( !size && table.getChild( rowIndex ).is( 'element', 'tableRow' ) ) {
			rowsToRemove.push( rowIndex );
		}
	}

	// Remove empty rows.
	if ( rowsToRemove.length ) {
		// @if CK_DEBUG_TABLE // console.log( `Post-fixing table: remove empty rows (${ rowsToRemove.length }).` );

		wasFixed = true;

		for ( const rowIndex of rowsToRemove.reverse() ) {
			writer.remove( table.getChild( rowIndex ) );
			childrenLengths.splice( rowIndex, 1 );
		}
	}

	// Filter out everything that's not a table row.
	const rowsLengths = childrenLengths.filter( ( row, rowIndex ) => table.getChild( rowIndex ).is( 'element', 'tableRow' ) );

	// Verify if all the rows have the same number of columns.
	const tableSize = rowsLengths[ 0 ];
	const isValid = rowsLengths.every( length => length === tableSize );

	if ( !isValid ) {
		// @if CK_DEBUG_TABLE // console.log( 'Post-fixing table: adding missing cells.' );

		// Find the maximum number of columns.
		const maxColumns = rowsLengths.reduce( ( prev, current ) => current > prev ? current : prev, 0 );

		for ( const [ rowIndex, size ] of rowsLengths.entries() ) {
			const columnsToInsert = maxColumns - size;

			if ( columnsToInsert ) {
				for ( let i = 0; i < columnsToInsert; i++ ) {
					createEmptyTableCell( writer, writer.createPositionAt( table.getChild( rowIndex ), 'end' ) );
				}

				wasFixed = true;
			}
		}
	}

	return wasFixed;
}

// Searches for table cells that extend beyond the table section to which they belong to. It will return an array of objects
// that stores table cells to be trimmed and the correct value of the `rowspan` attribute to set.
//
// @param {module:engine/model/element~Element} table
// @returns {Array.<{{cell, rowspan}}>}
function findCellsToTrim( table ) {
	const headingRows = parseInt( table.getAttribute( 'headingRows' ) || 0 );
	const maxRows = Array.from( table.getChildren() )
		.reduce( ( count, row ) => row.is( 'element', 'tableRow' ) ? count + 1 : count, 0 );

	const cellsToTrim = [];

	for ( const { row, cell, cellHeight } of new TableWalker( table ) ) {
		// Skip cells that do not expand over its row.
		if ( cellHeight < 2 ) {
			continue;
		}

		const isInHeader = row < headingRows;

		// Row limit is either end of header section or whole table as table body is after the header.
		const rowLimit = isInHeader ? headingRows : maxRows;

		// If table cell expands over its limit reduce it height to proper value.
		if ( row + cellHeight > rowLimit ) {
			const newRowspan = rowLimit - row;

			cellsToTrim.push( { cell, rowspan: newRowspan } );
		}
	}

	return cellsToTrim;
}

// Returns an array with lengths of rows assigned to the corresponding row index.
//
// @param {module:engine/model/element~Element} table
// @returns {Array.<Number>}
function getChildrenLengths( table ) {
	// TableWalker will not provide items for the empty rows, we need to pre-fill this array.
	const lengths = new Array( table.childCount ).fill( 0 );

	for ( const { rowIndex } of new TableWalker( table, { includeAllSlots: true } ) ) {
		lengths[ rowIndex ]++;
	}

	return lengths;
}

// Checks if the differ entry for an attribute change is one of the table's attributes.
//
// @param entry
// @returns {Boolean}
function isTableAttributeEntry( entry ) {
	const isAttributeType = entry.type === 'attribute';
	const key = entry.attributeKey;

	return isAttributeType && ( key === 'headingRows' || key === 'colspan' || key === 'rowspan' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/table-cell-paragraph-post-fixer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/table-cell-paragraph-post-fixer
 */

/**
 * Injects a table cell post-fixer into the model which inserts a `paragraph` element into empty table cells.
 *
 * A table cell must contain at least one block element as a child. An empty table cell will have an empty `paragraph` as a child.
 *
 *		<table>
 *			<tableRow>
 *				<tableCell></tableCell>
 *			</tableRow>
 *		</table>
 *
 * Will be fixed to:
 *
 *		<table>
 *			<tableRow>
 *				<tableCell><paragraph></paragraph></tableCell>
 *			</tableRow>
 *		</table>
 *
 * @param {module:engine/model/model~Model} model
 */
function injectTableCellParagraphPostFixer( model ) {
	model.document.registerPostFixer( writer => tableCellContentsPostFixer( writer, model ) );
}

// The table cell contents post-fixer.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/model~Model} model
function tableCellContentsPostFixer( writer, model ) {
	const changes = model.document.differ.getChanges();

	let wasFixed = false;

	for ( const entry of changes ) {
		if ( entry.type == 'insert' && entry.name == 'table' ) {
			wasFixed = fixTable( entry.position.nodeAfter, writer ) || wasFixed;
		}

		if ( entry.type == 'insert' && entry.name == 'tableRow' ) {
			wasFixed = fixTableRow( entry.position.nodeAfter, writer ) || wasFixed;
		}

		if ( entry.type == 'insert' && entry.name == 'tableCell' ) {
			wasFixed = fixTableCellContent( entry.position.nodeAfter, writer ) || wasFixed;
		}

		if ( checkTableCellChange( entry ) ) {
			wasFixed = fixTableCellContent( entry.position.parent, writer ) || wasFixed;
		}
	}

	return wasFixed;
}

// Fixes all table cells in a table.
//
// @param {module:engine/model/element~Element} table
// @param {module:engine/model/writer~Writer} writer
function fixTable( table, writer ) {
	let wasFixed = false;

	for ( const row of table.getChildren() ) {
		if ( row.is( 'element', 'tableRow' ) ) {
			wasFixed = fixTableRow( row, writer ) || wasFixed;
		}
	}

	return wasFixed;
}

// Fixes all table cells in a table row.
//
// @param {module:engine/model/element~Element} tableRow
// @param {module:engine/model/writer~Writer} writer
function fixTableRow( tableRow, writer ) {
	let wasFixed = false;

	for ( const tableCell of tableRow.getChildren() ) {
		wasFixed = fixTableCellContent( tableCell, writer ) || wasFixed;
	}

	return wasFixed;
}

// Fixes all table cell content by:
// - Adding a paragraph to a table cell without any child.
// - Wrapping direct $text in a `<paragraph>`.
//
// @param {module:engine/model/element~Element} table
// @param {module:engine/model/writer~Writer} writer
// @returns {Boolean}
function fixTableCellContent( tableCell, writer ) {
	// Insert paragraph to an empty table cell.
	if ( tableCell.childCount == 0 ) {
		// @if CK_DEBUG_TABLE // console.log( 'Post-fixing table: insert paragraph in empty cell.' );

		writer.insertElement( 'paragraph', tableCell );

		return true;
	}

	// Check table cell children for directly placed text nodes.
	// Temporary solution. See https://github.com/ckeditor/ckeditor5/issues/1464.
	const textNodes = Array.from( tableCell.getChildren() ).filter( child => child.is( '$text' ) );

	// @if CK_DEBUG_TABLE // textNodes.length && console.log( 'Post-fixing table: wrap cell content with paragraph.' );

	for ( const child of textNodes ) {
		writer.wrap( writer.createRangeOn( child ), 'paragraph' );
	}

	// Return true when there were text nodes to fix.
	return !!textNodes.length;
}

// Checks if a differ change should fix the table cell. This happens on:
// - Removing content from the table cell (i.e. `tableCell` can be left empty).
// - Adding a text node directly into a table cell.
//
// @param {Object} differ change entry
// @returns {Boolean}
function checkTableCellChange( entry ) {
	if ( !entry.position || !entry.position.parent.is( 'element', 'tableCell' ) ) {
		return false;
	}

	return entry.type == 'insert' && entry.name == '$text' || entry.type == 'remove';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/table-headings-refresh-handler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/table-heading-rows-refresh-post-fixer
 */



/**
 * A table headings refresh handler which marks the table cells or rows in the differ to have it re-rendered
 * if the headings attribute changed.
 *
 * Table heading rows and heading columns are represented in the model by a `headingRows` and `headingColumns` attributes.
 *
 * When table headings attribute changes, all the cells/rows are marked to re-render to change between `<td>` and `<th>`.
 *
 * @param {module:engine/model/model~Model} model
 * @param {module:engine/controller/editingcontroller~EditingController} editing
 */
function tableHeadingsRefreshHandler( model, editing ) {
	const differ = model.document.differ;

	for ( const change of differ.getChanges() ) {
		let table;
		let isRowChange = false;

		if ( change.type == 'attribute' ) {
			const element = change.range.start.nodeAfter;

			if ( !element || !element.is( 'element', 'table' ) ) {
				continue;
			}

			if ( change.attributeKey != 'headingRows' && change.attributeKey != 'headingColumns' ) {
				continue;
			}

			table = element;
			isRowChange = change.attributeKey == 'headingRows';
		} else if ( change.name == 'tableRow' || change.name == 'tableCell' ) {
			table = change.position.findAncestor( 'table' );
			isRowChange = change.name == 'tableRow';
		}

		if ( !table ) {
			continue;
		}

		const headingRows = table.getAttribute( 'headingRows' ) || 0;
		const headingColumns = table.getAttribute( 'headingColumns' ) || 0;

		const tableWalker = new TableWalker( table );

		for ( const tableSlot of tableWalker ) {
			const isHeading = tableSlot.row < headingRows || tableSlot.column < headingColumns;
			const expectedElementName = isHeading ? 'th' : 'td';

			const viewElement = editing.mapper.toViewElement( tableSlot.cell );

			if ( viewElement && viewElement.is( 'element' ) && viewElement.name != expectedElementName ) {
				editing.reconvertItem( isRowChange ? tableSlot.cell.parent : tableSlot.cell );
			}
		}
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/table-cell-refresh-handler.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/table-cell-refresh-post-fixer
 */



/**
 * A table cell refresh handler which marks the table cell in the differ to have it re-rendered.
 *
 * Model `paragraph` inside a table cell can be rendered as `<span>` or `<p>`. It is rendered as `<span>` if this is the only block
 * element in that table cell and it does not have any attributes. It is rendered as `<p>` otherwise.
 *
 * When table cell content changes, for example a second `paragraph` element is added, we need to ensure that the first `paragraph` is
 * re-rendered so it changes from `<span>` to `<p>`. The easiest way to do it is to re-render the entire table cell.
 *
 * @param {module:engine/model/model~Model} model
 * @param {module:engine/controller/editingcontroller~EditingController} editing
 */
function tableCellRefreshHandler( model, editing ) {
	const differ = model.document.differ;

	// Stores cells to be refreshed, so the table cell will be refreshed once for multiple changes.
	const cellsToCheck = new Set();

	for ( const change of differ.getChanges() ) {
		const parent = change.type == 'attribute' ? change.range.start.parent : change.position.parent;

		if ( parent.is( 'element', 'tableCell' ) ) {
			cellsToCheck.add( parent );
		}
	}

	for ( const tableCell of cellsToCheck.values() ) {
		const paragraphsToRefresh = Array.from( tableCell.getChildren() ).filter( child => shouldRefresh( child, editing.mapper ) );

		for ( const paragraph of paragraphsToRefresh ) {
			editing.reconvertItem( paragraph );
		}
	}
}

// Check if given model element needs refreshing.
//
// @param {module:engine/model/element~Element} modelElement
// @param {module:engine/conversion/mapper~Mapper} mapper
// @returns {Boolean}
function shouldRefresh( child, mapper ) {
	if ( !child.is( 'element', 'paragraph' ) ) {
		return false;
	}

	const viewElement = mapper.toViewElement( child );

	if ( !viewElement ) {
		return false;
	}

	return isSingleParagraphWithoutAttributes( child ) !== viewElement.is( 'element', 'span' );
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/tableediting.css
var tableediting = __webpack_require__(4777);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/tableediting.css

            

var tableediting_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tableediting_options.insert = "head";
tableediting_options.singleton = true;

var tableediting_update = injectStylesIntoStyleTag_default()(tableediting/* default */.Z, tableediting_options);



/* harmony default export */ const theme_tableediting = (tableediting/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableediting
 */




























/**
 * The table editing feature.
 *
 * @extends module:core/plugin~Plugin
 */
class TableEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableUtils ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const model = editor.model;
		const schema = model.schema;
		const conversion = editor.conversion;
		const tableUtils = editor.plugins.get( TableUtils );

		schema.register( 'table', {
			inheritAllFrom: '$blockObject',
			allowAttributes: [ 'headingRows', 'headingColumns' ]
		} );

		schema.register( 'tableRow', {
			allowIn: 'table',
			isLimit: true
		} );

		schema.register( 'tableCell', {
			allowContentOf: '$container',
			allowIn: 'tableRow',
			allowAttributes: [ 'colspan', 'rowspan' ],
			isLimit: true,
			isSelectable: true
		} );

		// Figure conversion.
		conversion.for( 'upcast' ).add( upcastTableFigure() );

		// Table conversion.
		conversion.for( 'upcast' ).add( upcastTable() );

		conversion.for( 'editingDowncast' ).elementToStructure( {
			model: {
				name: 'table',
				attributes: [ 'headingRows' ]
			},
			view: downcastTable( tableUtils, { asWidget: true } )
		} );
		conversion.for( 'dataDowncast' ).elementToStructure( {
			model: {
				name: 'table',
				attributes: [ 'headingRows' ]
			},
			view: downcastTable( tableUtils )
		} );

		// Table row conversion.
		conversion.for( 'upcast' ).elementToElement( { model: 'tableRow', view: 'tr' } );
		conversion.for( 'upcast' ).add( skipEmptyTableRow() );

		conversion.for( 'downcast' ).elementToElement( {
			model: 'tableRow',
			view: downcastRow()
		} );

		// Table cell conversion.
		conversion.for( 'upcast' ).elementToElement( { model: 'tableCell', view: 'td' } );
		conversion.for( 'upcast' ).elementToElement( { model: 'tableCell', view: 'th' } );
		conversion.for( 'upcast' ).add( ensureParagraphInTableCell( 'td' ) );
		conversion.for( 'upcast' ).add( ensureParagraphInTableCell( 'th' ) );

		conversion.for( 'editingDowncast' ).elementToElement( {
			model: 'tableCell',
			view: downcastCell( { asWidget: true } )
		} );
		conversion.for( 'dataDowncast' ).elementToElement( {
			model: 'tableCell',
			view: downcastCell()
		} );

		// Duplicates code - needed to properly refresh paragraph inside a table cell.
		conversion.for( 'editingDowncast' ).elementToElement( {
			model: 'paragraph',
			view: convertParagraphInTableCell( { asWidget: true } ),
			converterPriority: 'high'
		} );
		conversion.for( 'dataDowncast' ).elementToElement( {
			model: 'paragraph',
			view: convertParagraphInTableCell(),
			converterPriority: 'high'
		} );

		// Table attributes conversion.
		conversion.for( 'downcast' ).attributeToAttribute( { model: 'colspan', view: 'colspan' } );
		conversion.for( 'upcast' ).attributeToAttribute( {
			model: { key: 'colspan', value: upcastCellSpan( 'colspan' ) },
			view: 'colspan'
		} );

		conversion.for( 'downcast' ).attributeToAttribute( { model: 'rowspan', view: 'rowspan' } );
		conversion.for( 'upcast' ).attributeToAttribute( {
			model: { key: 'rowspan', value: upcastCellSpan( 'rowspan' ) },
			view: 'rowspan'
		} );

		// Manually adjust model position mappings in a special case, when a table cell contains a paragraph, which is bound
		// to its parent (to the table cell). This custom model-to-view position mapping is necessary in data pipeline only,
		// because only during this conversion a paragraph can be bound to its parent.
		editor.data.mapper.on( 'modelToViewPosition', mapTableCellModelPositionToView() );

		// Define the config.
		editor.config.define( 'table.defaultHeadings.rows', 0 );
		editor.config.define( 'table.defaultHeadings.columns', 0 );

		// Define all the commands.
		editor.commands.add( 'insertTable', new InsertTableCommand( editor ) );
		editor.commands.add( 'insertTableRowAbove', new InsertRowCommand( editor, { order: 'above' } ) );
		editor.commands.add( 'insertTableRowBelow', new InsertRowCommand( editor, { order: 'below' } ) );
		editor.commands.add( 'insertTableColumnLeft', new InsertColumnCommand( editor, { order: 'left' } ) );
		editor.commands.add( 'insertTableColumnRight', new InsertColumnCommand( editor, { order: 'right' } ) );

		editor.commands.add( 'removeTableRow', new RemoveRowCommand( editor ) );
		editor.commands.add( 'removeTableColumn', new RemoveColumnCommand( editor ) );

		editor.commands.add( 'splitTableCellVertically', new SplitCellCommand( editor, { direction: 'vertically' } ) );
		editor.commands.add( 'splitTableCellHorizontally', new SplitCellCommand( editor, { direction: 'horizontally' } ) );

		editor.commands.add( 'mergeTableCells', new MergeCellsCommand( editor ) );

		editor.commands.add( 'mergeTableCellRight', new MergeCellCommand( editor, { direction: 'right' } ) );
		editor.commands.add( 'mergeTableCellLeft', new MergeCellCommand( editor, { direction: 'left' } ) );
		editor.commands.add( 'mergeTableCellDown', new MergeCellCommand( editor, { direction: 'down' } ) );
		editor.commands.add( 'mergeTableCellUp', new MergeCellCommand( editor, { direction: 'up' } ) );

		editor.commands.add( 'setTableColumnHeader', new SetHeaderColumnCommand( editor ) );
		editor.commands.add( 'setTableRowHeader', new SetHeaderRowCommand( editor ) );

		editor.commands.add( 'selectTableRow', new SelectRowCommand( editor ) );
		editor.commands.add( 'selectTableColumn', new SelectColumnCommand( editor ) );

		injectTableLayoutPostFixer( model );
		injectTableCellParagraphPostFixer( model );

		this.listenTo( model.document, 'change:data', () => {
			tableHeadingsRefreshHandler( model, editor.editing );
			tableCellRefreshHandler( model, editor.editing );
		} );
	}
}

// Creates a mapper callback to adjust model position mappings in a table cell containing a paragraph, which is bound to its parent
// (to the table cell). Only positions after this paragraph have to be adjusted, because after binding this paragraph to the table cell,
// elements located after this paragraph would point either to a non-existent offset inside `tableCell` (if paragraph is empty), or after
// the first character of the paragraph's text. See https://github.com/ckeditor/ckeditor5/issues/10116.
//
// <tableCell><paragraph></paragraph>^</tableCell> -> <td>^&nbsp;</td>
//
// <tableCell><paragraph>foobar</paragraph>^</tableCell> -> <td>foobar^</td>
//
// @returns {Function}
function mapTableCellModelPositionToView() {
	return ( evt, data ) => {
		const modelParent = data.modelPosition.parent;
		const modelNodeBefore = data.modelPosition.nodeBefore;

		if ( !modelParent.is( 'element', 'tableCell' ) ) {
			return;
		}

		if ( !modelNodeBefore || !modelNodeBefore.is( 'element', 'paragraph' ) ) {
			return;
		}

		const viewNodeBefore = data.mapper.toViewElement( modelNodeBefore );
		const viewParent = data.mapper.toViewElement( modelParent );

		if ( viewNodeBefore === viewParent ) {
			// Since the paragraph has already been bound to its parent, update the current position in the model with paragraph's
			// max offset, so it points to the place which should normally (in all other cases) be the end position of this paragraph.
			data.viewPosition = data.mapper.findPositionIn( viewParent, modelNodeBefore.maxOffset );
		}
	};
}

// Returns fixed colspan and rowspan attrbutes values.
//
// @private
// @param {String} type colspan or rowspan.
// @returns {Function} conversion value function.
function upcastCellSpan( type ) {
	return cell => {
		const span = parseInt( cell.getAttribute( type ) );

		if ( Number.isNaN( span ) || span <= 0 ) {
			return null;
		}

		return span;
	};
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/inserttable.css
var inserttable = __webpack_require__(8085);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/inserttable.css

            

var inserttable_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

inserttable_options.insert = "head";
inserttable_options.singleton = true;

var inserttable_update = injectStylesIntoStyleTag_default()(inserttable/* default */.Z, inserttable_options);



/* harmony default export */ const theme_inserttable = (inserttable/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/ui/inserttableview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/ui/inserttableview
 */







/**
 * The table size view.
 *
 * It renders a 10x10 grid to choose the inserted table size.
 *
 * @extends module:ui/view~View
 * @implements module:ui/dropdown/dropdownpanelfocusable~DropdownPanelFocusable
 */
class InsertTableView extends src_view_View {
	/**
	 * @inheritDoc
	 */
	constructor( locale ) {
		super( locale );

		const bind = this.bindTemplate;

		/**
		 * A collection of table size box items.
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.items = this._createGridCollection();

		/**
		 * Listen to `keydown` events fired in this view's main element.
		 *
		 * @readonly
		 * @member {module:utils/keystrokeHandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * Tracks information about the DOM focus in the grid.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * The currently selected number of rows of the new table.
		 *
		 * @observable
		 * @member {Number} #rows
		 */
		this.set( 'rows', 0 );

		/**
		 * The currently selected number of columns of the new table.
		 *
		 * @observable
		 * @member {Number} #columns
		 */
		this.set( 'columns', 0 );

		/**
		 * The label text displayed under the boxes.
		 *
		 * @observable
		 * @member {String} #label
		 */
		this.bind( 'label' )
			.to( this, 'columns', this, 'rows', ( columns, rows ) => `${ rows } × ${ columns }` );

		this.setTemplate( {
			tag: 'div',
			attributes: {
				class: [ 'ck' ]
			},

			children: [
				{
					tag: 'div',
					attributes: {
						class: [ 'ck-insert-table-dropdown__grid' ]
					},
					on: {
						'mouseover@.ck-insert-table-dropdown-grid-box': bind.to( 'boxover' )
					},
					children: this.items
				},
				{
					tag: 'div',
					attributes: {
						class: [
							'ck',
							'ck-insert-table-dropdown__label'
						],
						'aria-hidden': true
					},
					children: [
						{
							text: bind.to( 'label' )
						}
					]
				}
			],

			on: {
				mousedown: bind.to( evt => {
					evt.preventDefault();
				} ),

				click: bind.to( () => {
					this.fire( 'execute' );
				} )
			}
		} );

		// #rows and #columns are set via changes to #focusTracker on mouse over.
		this.on( 'boxover', ( evt, domEvt ) => {
			const { row, column } = domEvt.target.dataset;
			this.items.get( ( parseInt( row, 10 ) - 1 ) * 10 + ( parseInt( column, 10 ) - 1 ) ).focus();
		} );

		// This allows the #rows and #columns to be updated when:
		// * the user navigates the grid using the keyboard,
		// * the user moves the mouse over grid items.
		this.focusTracker.on( 'change:focusedElement', ( evt, name, focusedElement ) => {
			if ( !focusedElement ) {
				return;
			}

			const { row, column } = focusedElement.dataset;

			// As row & column indexes are zero-based transform it to number of selected rows & columns.
			this.set( {
				rows: parseInt( row ),
				columns: parseInt( column )
			} );
		} );

		this.on( 'change:columns', () => this._highlightGridBoxes() );
		this.on( 'change:rows', () => this._highlightGridBoxes() );
	}

	render() {
		super.render();

		addKeyboardHandlingForGrid( {
			keystrokeHandler: this.keystrokes,
			focusTracker: this.focusTracker,
			gridItems: this.items,
			numberOfColumns: 10
		} );

		for ( const item of this.items ) {
			this.focusTracker.add( item.element );
		}

		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	focus() {
		this.items.get( 0 ).focus();
	}

	/**
	 * @inheritDoc
	 */
	focusLast() {
		this.items.get( 0 ).focus();
	}

	/**
	 * Highlights grid boxes depending on rows and columns selected.
	 *
	 * @private
	 */
	_highlightGridBoxes() {
		const rows = this.rows;
		const columns = this.columns;

		this.items.map( ( boxView, index ) => {
			// Translate box index to the row & column index.
			const itemRow = Math.floor( index / 10 );
			const itemColumn = index % 10;

			// Grid box is highlighted when its row & column index belongs to selected number of rows & columns.
			const isOn = itemRow < rows && itemColumn < columns;

			boxView.set( 'isOn', isOn );
		} );
	}

	/**
	 * Creates a new Button for the grid.
	 *
	 * @private
	 * @param {module:utils/locale~Locale} locale The locale instance.
	 * @param {Number} row Row number.
	 * @param {Number} column Column number.
	 * @param {String} label The grid button label.
	 * @returns {module:ui/button/buttonview~ButtonView}
	 */
	_createGridButton( locale, row, column, label ) {
		const button = new buttonview_ButtonView( locale );

		button.set( {
			label,
			class: 'ck-insert-table-dropdown-grid-box'
		} );

		button.extendTemplate( {
			attributes: {
				'data-row': row,
				'data-column': column
			}
		} );

		return button;
	}

	/**
	 * @private
	 * @returns {module:ui/viewcollection~ViewCollection} A view collection containing boxes to be placed in a table grid.
	 */
	_createGridCollection() {
		const boxes = [];

		// Add grid boxes to table selection view.
		for ( let index = 0; index < 100; index++ ) {
			const row = Math.floor( index / 10 );
			const column = index % 10;
			const label = `${ row + 1 } × ${ column + 1 }`;

			boxes.push( this._createGridButton( this.locale, row + 1, column + 1, label ) );
		}

		return this.createCollection( boxes );
	}

	/**
	 * Fired when the mouse hover over one of the {@link #items child grid boxes}.
	 *
	 * @event boxover
	 */
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/icons/table.svg
/* harmony default export */ const table = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3 6v3h4V6H3zm0 4v3h4v-3H3zm0 4v3h4v-3H3zm5 3h4v-3H8v3zm5 0h4v-3h-4v3zm4-4v-3h-4v3h4zm0-4V6h-4v3h4zm1.5 8a1.5 1.5 0 0 1-1.5 1.5H3A1.5 1.5 0 0 1 1.5 17V4c.222-.863 1.068-1.5 2-1.5h13c.932 0 1.778.637 2 1.5v13zM12 13v-3H8v3h4zm0-4V6H8v3h4z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/icons/table-column.svg
/* harmony default export */ const table_column = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z\" opacity=\".6\"/><path d=\"M18 7v1H2V7h16zm0 5v1H2v-1h16z\" opacity=\".6\"/><path d=\"M14 1v18a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1zm-2 1H8v4h4V2zm0 6H8v4h4V8zm0 6H8v4h4v-4z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/icons/table-row.svg
/* harmony default export */ const table_row = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z\" opacity=\".6\"/><path d=\"M7 2h1v16H7V2zm5 0h1v16h-1V2z\" opacity=\".6\"/><path d=\"M1 6h18a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm1 2v4h4V8H2zm6 0v4h4V8H8zm6 0v4h4V8h-4z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/icons/table-merge-cell.svg
/* harmony default export */ const table_merge_cell = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z\" opacity=\".6\"/><path d=\"M7 2h1v16H7V2zm5 0h1v7h-1V2zm6 5v1H2V7h16zM8 12v1H2v-1h6z\" opacity=\".6\"/><path d=\"M7 7h12a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1zm1 2v9h10V9H8z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableui
 */












/**
 * The table UI plugin. It introduces:
 *
 * * The `'insertTable'` dropdown,
 * * The `'tableColumn'` dropdown,
 * * The `'tableRow'` dropdown,
 * * The `'mergeTableCells'` split button.
 *
 * The `'tableColumn'`, `'tableRow'` and `'mergeTableCells'` dropdowns work best with {@link module:table/tabletoolbar~TableToolbar}.
 *
 * @extends module:core/plugin~Plugin
 */
class TableUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = this.editor.t;
		const contentLanguageDirection = editor.locale.contentLanguageDirection;
		const isContentLtr = contentLanguageDirection === 'ltr';

		editor.ui.componentFactory.add( 'insertTable', locale => {
			const command = editor.commands.get( 'insertTable' );
			const dropdownView = createDropdown( locale );

			dropdownView.bind( 'isEnabled' ).to( command );

			// Decorate dropdown's button.
			dropdownView.buttonView.set( {
				icon: table,
				label: t( 'Insert table' ),
				tooltip: true
			} );

			let insertTableView;

			dropdownView.on( 'change:isOpen', () => {
				if ( insertTableView ) {
					return;
				}

				// Prepare custom view for dropdown's panel.
				insertTableView = new InsertTableView( locale );
				dropdownView.panelView.children.add( insertTableView );

				insertTableView.delegate( 'execute' ).to( dropdownView );

				dropdownView.on( 'execute', () => {
					editor.execute( 'insertTable', { rows: insertTableView.rows, columns: insertTableView.columns } );
					editor.editing.view.focus();
				} );
			} );

			return dropdownView;
		} );

		editor.ui.componentFactory.add( 'tableColumn', locale => {
			const options = [
				{
					type: 'switchbutton',
					model: {
						commandName: 'setTableColumnHeader',
						label: t( 'Header column' ),
						bindIsOn: true
					}
				},
				{ type: 'separator' },
				{
					type: 'button',
					model: {
						commandName: isContentLtr ? 'insertTableColumnLeft' : 'insertTableColumnRight',
						label: t( 'Insert column left' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: isContentLtr ? 'insertTableColumnRight' : 'insertTableColumnLeft',
						label: t( 'Insert column right' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: 'removeTableColumn',
						label: t( 'Delete column' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: 'selectTableColumn',
						label: t( 'Select column' )
					}
				}
			];

			return this._prepareDropdown( t( 'Column' ), table_column, options, locale );
		} );

		editor.ui.componentFactory.add( 'tableRow', locale => {
			const options = [
				{
					type: 'switchbutton',
					model: {
						commandName: 'setTableRowHeader',
						label: t( 'Header row' ),
						bindIsOn: true
					}
				},
				{ type: 'separator' },
				{
					type: 'button',
					model: {
						commandName: 'insertTableRowAbove',
						label: t( 'Insert row above' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: 'insertTableRowBelow',
						label: t( 'Insert row below' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: 'removeTableRow',
						label: t( 'Delete row' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: 'selectTableRow',
						label: t( 'Select row' )
					}
				}
			];

			return this._prepareDropdown( t( 'Row' ), table_row, options, locale );
		} );

		editor.ui.componentFactory.add( 'mergeTableCells', locale => {
			const options = [
				{
					type: 'button',
					model: {
						commandName: 'mergeTableCellUp',
						label: t( 'Merge cell up' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: isContentLtr ? 'mergeTableCellRight' : 'mergeTableCellLeft',
						label: t( 'Merge cell right' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: 'mergeTableCellDown',
						label: t( 'Merge cell down' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: isContentLtr ? 'mergeTableCellLeft' : 'mergeTableCellRight',
						label: t( 'Merge cell left' )
					}
				},
				{ type: 'separator' },
				{
					type: 'button',
					model: {
						commandName: 'splitTableCellVertically',
						label: t( 'Split cell vertically' )
					}
				},
				{
					type: 'button',
					model: {
						commandName: 'splitTableCellHorizontally',
						label: t( 'Split cell horizontally' )
					}
				}
			];

			return this._prepareMergeSplitButtonDropdown( t( 'Merge cells' ), table_merge_cell, options, locale );
		} );
	}

	/**
	 * Creates a dropdown view from a set of options.
	 *
	 * @private
	 * @param {String} label The dropdown button label.
	 * @param {String} icon An icon for the dropdown button.
	 * @param {Array.<module:ui/dropdown/utils~ListDropdownItemDefinition>} options The list of options for the dropdown.
	 * @param {module:utils/locale~Locale} locale
	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
	 */
	_prepareDropdown( label, icon, options, locale ) {
		const editor = this.editor;
		const dropdownView = createDropdown( locale );
		const commands = this._fillDropdownWithListOptions( dropdownView, options );

		// Decorate dropdown's button.
		dropdownView.buttonView.set( {
			label,
			icon,
			tooltip: true
		} );

		// Make dropdown button disabled when all options are disabled.
		dropdownView.bind( 'isEnabled' ).toMany( commands, 'isEnabled', ( ...areEnabled ) => {
			return areEnabled.some( isEnabled => isEnabled );
		} );

		this.listenTo( dropdownView, 'execute', evt => {
			editor.execute( evt.source.commandName );

			// Toggling a switch button view should not move the focus to the editable.
			if ( !( evt.source instanceof SwitchButtonView ) ) {
				editor.editing.view.focus();
			}
		} );

		return dropdownView;
	}

	/**
	 * Creates a dropdown view with a {@link module:ui/dropdown/button/splitbuttonview~SplitButtonView} for
	 * merge (and split)–related commands.
	 *
	 * @private
	 * @param {String} label The dropdown button label.
	 * @param {String} icon An icon for the dropdown button.
	 * @param {Array.<module:ui/dropdown/utils~ListDropdownItemDefinition>} options The list of options for the dropdown.
	 * @param {module:utils/locale~Locale} locale
	 * @returns {module:ui/dropdown/dropdownview~DropdownView}
	 */
	_prepareMergeSplitButtonDropdown( label, icon, options, locale ) {
		const editor = this.editor;
		const dropdownView = createDropdown( locale, SplitButtonView );
		const mergeCommandName = 'mergeTableCells';

		// Main command.
		const mergeCommand = editor.commands.get( mergeCommandName );

		// Subcommands in the dropdown.
		const commands = this._fillDropdownWithListOptions( dropdownView, options );

		dropdownView.buttonView.set( {
			label,
			icon,
			tooltip: true,
			isEnabled: true
		} );

		// Make dropdown button disabled when all options are disabled together with the main command.
		dropdownView.bind( 'isEnabled' ).toMany( [ mergeCommand, ...commands ], 'isEnabled', ( ...areEnabled ) => {
			return areEnabled.some( isEnabled => isEnabled );
		} );

		// Merge selected table cells when the main part of the split button is clicked.
		this.listenTo( dropdownView.buttonView, 'execute', () => {
			editor.execute( mergeCommandName );
			editor.editing.view.focus();
		} );

		// Execute commands for events coming from the list in the dropdown panel.
		this.listenTo( dropdownView, 'execute', evt => {
			editor.execute( evt.source.commandName );
			editor.editing.view.focus();
		} );

		return dropdownView;
	}

	/**
	 * Injects a {@link module:ui/list/listview~ListView} into the passed dropdown with buttons
	 * which execute editor commands as configured in passed options.
	 *
	 * @private
	 * @param {module:ui/dropdown/dropdownview~DropdownView} dropdownView
	 * @param {Array.<module:ui/dropdown/utils~ListDropdownItemDefinition>} options The list of options for the dropdown.
	 * @returns {Array.<module:core/command~Command>} Commands the list options are interacting with.
	 */
	_fillDropdownWithListOptions( dropdownView, options ) {
		const editor = this.editor;
		const commands = [];
		const itemDefinitions = new Collection();

		for ( const option of options ) {
			addListOption( option, editor, commands, itemDefinitions );
		}

		addListToDropdown( dropdownView, itemDefinitions, editor.ui.componentFactory );

		return commands;
	}
}

// Adds an option to a list view.
//
// @param {module:table/tableui~DropdownOption} option A configuration option.
// @param {module:core/editor/editor~Editor} editor
// @param {Array.<module:core/command~Command>} commands The list of commands to update.
// @param {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>} itemDefinitions
// A collection of dropdown items to update with the given option.
function addListOption( option, editor, commands, itemDefinitions ) {
	const model = option.model = new model_Model( option.model );
	const { commandName, bindIsOn } = option.model;

	if ( option.type === 'button' || option.type === 'switchbutton' ) {
		const command = editor.commands.get( commandName );

		commands.push( command );

		model.set( { commandName } );

		model.bind( 'isEnabled' ).to( command );

		if ( bindIsOn ) {
			model.bind( 'isOn' ).to( command, 'value' );
		}
	}

	model.set( {
		withText: true
	} );

	itemDefinitions.add( option );
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/tableselection.css
var tableselection = __webpack_require__(5593);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/tableselection.css

            

var tableselection_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tableselection_options.insert = "head";
tableselection_options.singleton = true;

var tableselection_update = injectStylesIntoStyleTag_default()(tableselection/* default */.Z, tableselection_options);



/* harmony default export */ const theme_tableselection = (tableselection/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableselection.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableselection
 */











/**
 * This plugin enables the advanced table cells, rows and columns selection.
 * It is loaded automatically by the {@link module:table/table~Table} plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableSelection extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableSelection';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableUtils, TableUtils ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const model = editor.model;
		const view = editor.editing.view;

		this.listenTo( model, 'deleteContent', ( evt, args ) => this._handleDeleteContent( evt, args ), { priority: 'high' } );
		this.listenTo( view.document, 'insertText', ( evt, data ) => this._handleInsertTextEvent( evt, data ), { priority: 'high' } );

		this._defineSelectionConverter();
		this._enablePluginDisabling(); // sic!
	}

	/**
	 * Returns the currently selected table cells or `null` if it is not a table cells selection.
	 *
	 * @returns {Array.<module:engine/model/element~Element>|null}
	 */
	getSelectedTableCells() {
		const tableUtils = this.editor.plugins.get( TableUtils );
		const selection = this.editor.model.document.selection;

		const selectedCells = tableUtils.getSelectedTableCells( selection );

		if ( selectedCells.length == 0 ) {
			return null;
		}

		// This should never happen, but let's know if it ever happens.
		// @if CK_DEBUG //	/* istanbul ignore next */
		// @if CK_DEBUG //	if ( selectedCells.length != selection.rangeCount ) {
		// @if CK_DEBUG //		console.warn( 'Mixed selection warning. The selection contains table cells and some other ranges.' );
		// @if CK_DEBUG //	}

		return selectedCells;
	}

	/**
	 * Returns the selected table fragment as a document fragment.
	 *
	 * @returns {module:engine/model/documentfragment~DocumentFragment|null}
	 */
	getSelectionAsFragment() {
		const tableUtils = this.editor.plugins.get( TableUtils );
		const selectedCells = this.getSelectedTableCells();

		if ( !selectedCells ) {
			return null;
		}

		return this.editor.model.change( writer => {
			const documentFragment = writer.createDocumentFragment();

			const { first: firstColumn, last: lastColumn } = tableUtils.getColumnIndexes( selectedCells );
			const { first: firstRow, last: lastRow } = tableUtils.getRowIndexes( selectedCells );

			const sourceTable = selectedCells[ 0 ].findAncestor( 'table' );

			let adjustedLastRow = lastRow;
			let adjustedLastColumn = lastColumn;

			// If the selection is rectangular there could be a case of all cells in the last row/column spanned over
			// next row/column so the real lastRow/lastColumn should be updated.
			if ( tableUtils.isSelectionRectangular( selectedCells ) ) {
				const dimensions = {
					firstColumn,
					lastColumn,
					firstRow,
					lastRow
				};

				adjustedLastRow = adjustLastRowIndex( sourceTable, dimensions );
				adjustedLastColumn = adjustLastColumnIndex( sourceTable, dimensions );
			}

			const cropDimensions = {
				startRow: firstRow,
				startColumn: firstColumn,
				endRow: adjustedLastRow,
				endColumn: adjustedLastColumn
			};

			const table = cropTableToDimensions( sourceTable, cropDimensions, writer );

			writer.insert( table, documentFragment, 0 );

			return documentFragment;
		} );
	}

	/**
	 * Sets the model selection based on given anchor and target cells (can be the same cell).
	 * Takes care of setting the backward flag.
	 *
	 *		const modelRoot = editor.model.document.getRoot();
	 *		const firstCell = modelRoot.getNodeByPath( [ 0, 0, 0 ] );
	 *		const lastCell = modelRoot.getNodeByPath( [ 0, 0, 1 ] );
	 *
	 *		const tableSelection = editor.plugins.get( 'TableSelection' );
	 *		tableSelection.setCellSelection( firstCell, lastCell );
	 *
	 * @param {module:engine/model/element~Element} anchorCell
	 * @param {module:engine/model/element~Element} targetCell
	 */
	setCellSelection( anchorCell, targetCell ) {
		const cellsToSelect = this._getCellsToSelect( anchorCell, targetCell );

		this.editor.model.change( writer => {
			writer.setSelection(
				cellsToSelect.cells.map( cell => writer.createRangeOn( cell ) ),
				{ backward: cellsToSelect.backward }
			);
		} );
	}

	/**
	 * Returns the focus cell from the current selection.
	 *
	 * @returns {module:engine/model/element~Element}
	 */
	getFocusCell() {
		const selection = this.editor.model.document.selection;
		const focusCellRange = [ ...selection.getRanges() ].pop();
		const element = focusCellRange.getContainedElement();

		if ( element && element.is( 'element', 'tableCell' ) ) {
			return element;
		}

		return null;
	}

	/**
	 * Returns the anchor cell from the current selection.
	 *
	 * @returns {module:engine/model/element~Element} anchorCell
	 */
	getAnchorCell() {
		const selection = this.editor.model.document.selection;
		const anchorCellRange = first_first( selection.getRanges() );
		const element = anchorCellRange.getContainedElement();

		if ( element && element.is( 'element', 'tableCell' ) ) {
			return element;
		}

		return null;
	}

	/**
	 * Defines a selection converter which marks the selected cells with a specific class.
	 *
	 * The real DOM selection is put in the last cell. Since the order of ranges is dependent on whether the
	 * selection is backward or not, the last cell will usually be close to the "focus" end of the selection
	 * (a selection has anchor and focus).
	 *
	 * The real DOM selection is then hidden with CSS.
	 *
	 * @private
	 */
	_defineSelectionConverter() {
		const editor = this.editor;
		const highlighted = new Set();

		editor.conversion.for( 'editingDowncast' ).add( dispatcher => dispatcher.on( 'selection', ( evt, data, conversionApi ) => {
			const viewWriter = conversionApi.writer;

			clearHighlightedTableCells( viewWriter );

			const selectedCells = this.getSelectedTableCells();

			if ( !selectedCells ) {
				return;
			}

			for ( const tableCell of selectedCells ) {
				const viewElement = conversionApi.mapper.toViewElement( tableCell );

				viewWriter.addClass( 'ck-editor__editable_selected', viewElement );
				highlighted.add( viewElement );
			}

			const lastViewCell = conversionApi.mapper.toViewElement( selectedCells[ selectedCells.length - 1 ] );
			viewWriter.setSelection( lastViewCell, 0 );
		}, { priority: 'lowest' } ) );

		function clearHighlightedTableCells( writer ) {
			for ( const previouslyHighlighted of highlighted ) {
				writer.removeClass( 'ck-editor__editable_selected', previouslyHighlighted );
			}

			highlighted.clear();
		}
	}

	/**
	 * Creates a listener that reacts to changes in {@link #isEnabled} and, if the plugin was disabled,
	 * it collapses the multi-cell selection to a regular selection placed inside a table cell.
	 *
	 * This listener helps features that disable the table selection plugin bring the selection
	 * to a clear state they can work with (for instance, because they don't support multiple cell selection).
	 */
	_enablePluginDisabling() {
		const editor = this.editor;

		this.on( 'change:isEnabled', () => {
			if ( !this.isEnabled ) {
				const selectedCells = this.getSelectedTableCells();

				if ( !selectedCells ) {
					return;
				}

				editor.model.change( writer => {
					const position = writer.createPositionAt( selectedCells[ 0 ], 0 );
					const range = editor.model.schema.getNearestSelectionRange( position );

					writer.setSelection( range );
				} );
			}
		} );
	}

	/**
	 * Overrides the default `model.deleteContent()` behavior over a selected table fragment.
	 *
	 * @private
	 * @param {module:utils/eventinfo~EventInfo} event
	 * @param {Array.<*>} args Delete content method arguments.
	 */
	_handleDeleteContent( event, args ) {
		const tableUtils = this.editor.plugins.get( TableUtils );
		const [ selection, options ] = args;
		const model = this.editor.model;
		const isBackward = !options || options.direction == 'backward';
		const selectedTableCells = tableUtils.getSelectedTableCells( selection );

		if ( !selectedTableCells.length ) {
			return;
		}

		event.stop();

		model.change( writer => {
			const tableCellToSelect = selectedTableCells[ isBackward ? selectedTableCells.length - 1 : 0 ];

			model.change( writer => {
				for ( const tableCell of selectedTableCells ) {
					model.deleteContent( writer.createSelection( tableCell, 'in' ) );
				}
			} );

			const rangeToSelect = model.schema.getNearestSelectionRange( writer.createPositionAt( tableCellToSelect, 0 ) );

			// Note: we ignore the case where rangeToSelect may be null because deleteContent() will always (unless someone broke it)
			// create an empty paragraph to accommodate the selection.

			if ( selection.is( 'documentSelection' ) ) {
				writer.setSelection( rangeToSelect );
			} else {
				selection.setTo( rangeToSelect );
			}
		} );
	}

	/**
	 * This handler makes it possible to remove the content of all selected cells by starting to type.
	 * If you take a look at {@link #_defineSelectionConverter} you will find out that despite the multi-cell selection being set
	 * in the model, the view selection is collapsed in the last cell (because most browsers are unable to render multi-cell selections;
	 * yes, it's a hack).
	 *
	 * When multiple cells are selected in the model and the user starts to type, the
	 * {@link module:engine/view/document~Document#event:insertText} event carries information provided by the
	 * beforeinput DOM  event, that in turn only knows about this collapsed DOM selection in the last cell.
	 *
	 * As a result, the selected cells have no chance to be cleaned up. To fix this, this listener intercepts
	 * the event and injects the custom view selection in the data that translates correctly to the actual state
	 * of the multi-cell selection in the model.
	 *
	 * @private
	 * @param {module:utils/eventinfo~EventInfo} event
	 * @param {module:engine/view/observer/domeventdata~DomEventData} data Insert text event data.
	 */
	_handleInsertTextEvent( evt, data ) {
		const editor = this.editor;
		const model = editor.model;
		const modelSelection = model.document.selection;
		const selectedCells = this.getSelectedTableCells( modelSelection );

		if ( !selectedCells ) {
			return;
		}

		const view = editor.editing.view;
		const mapper = editor.editing.mapper;
		const viewRanges = selectedCells.map( tableCell => view.createRangeOn( mapper.toViewElement( tableCell ) ) );

		data.selection = view.createSelection( viewRanges );
	}

	/**
	 * Returns an array of table cells that should be selected based on the
	 * given anchor cell and target (focus) cell.
	 *
	 * The cells are returned in a reverse direction if the selection is backward.
	 *
	 * @private
	 * @param {module:engine/model/element~Element} anchorCell
	 * @param {module:engine/model/element~Element} targetCell
	 * @returns {Array.<module:engine/model/element~Element>}
	 */
	_getCellsToSelect( anchorCell, targetCell ) {
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const startLocation = tableUtils.getCellLocation( anchorCell );
		const endLocation = tableUtils.getCellLocation( targetCell );

		const startRow = Math.min( startLocation.row, endLocation.row );
		const endRow = Math.max( startLocation.row, endLocation.row );

		const startColumn = Math.min( startLocation.column, endLocation.column );
		const endColumn = Math.max( startLocation.column, endLocation.column );

		// 2-dimensional array of the selected cells to ease flipping the order of cells for backward selections.
		const selectionMap = new Array( endRow - startRow + 1 ).fill( null ).map( () => [] );

		const walkerOptions = {
			startRow,
			endRow,
			startColumn,
			endColumn
		};

		for ( const { row, cell } of new TableWalker( anchorCell.findAncestor( 'table' ), walkerOptions ) ) {
			selectionMap[ row - startRow ].push( cell );
		}

		const flipVertically = endLocation.row < startLocation.row;
		const flipHorizontally = endLocation.column < startLocation.column;

		if ( flipVertically ) {
			selectionMap.reverse();
		}

		if ( flipHorizontally ) {
			selectionMap.forEach( row => row.reverse() );
		}

		return {
			cells: selectionMap.flat(),
			backward: flipVertically || flipHorizontally
		};
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableclipboard.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableclipboard
 */








/**
 * This plugin adds support for copying/cutting/pasting fragments of tables.
 * It is loaded automatically by the {@link module:table/table~Table} plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableClipboard extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableClipboard';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableSelection, TableUtils ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const viewDocument = editor.editing.view.document;

		this.listenTo( viewDocument, 'copy', ( evt, data ) => this._onCopyCut( evt, data ) );
		this.listenTo( viewDocument, 'cut', ( evt, data ) => this._onCopyCut( evt, data ) );
		this.listenTo( editor.model, 'insertContent', ( evt, args ) => this._onInsertContent( evt, ...args ), { priority: 'high' } );

		this.decorate( '_replaceTableSlotCell' );
	}

	/**
	 * Copies table content to a clipboard on "copy" & "cut" events.
	 *
	 * @private
	 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the handled event.
	 * @param {Object} data Clipboard event data.
	 */
	_onCopyCut( evt, data ) {
		const tableSelection = this.editor.plugins.get( TableSelection );

		if ( !tableSelection.getSelectedTableCells() ) {
			return;
		}

		if ( evt.name == 'cut' && this.editor.isReadOnly ) {
			return;
		}

		data.preventDefault();
		evt.stop();

		const dataController = this.editor.data;
		const viewDocument = this.editor.editing.view.document;

		const content = dataController.toView( tableSelection.getSelectionAsFragment() );

		viewDocument.fire( 'clipboardOutput', {
			dataTransfer: data.dataTransfer,
			content,
			method: evt.name
		} );
	}

	/**
	 * Overrides default {@link module:engine/model/model~Model#insertContent `model.insertContent()`} method to handle pasting table inside
	 * selected table fragment.
	 *
	 * Depending on selected table fragment:
	 * - If a selected table fragment is smaller than paste table it will crop pasted table to match dimensions.
	 * - If dimensions are equal it will replace selected table fragment with a pasted table contents.
	 *
	 * @private
	 * @param evt
	 * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
	 * @param {module:engine/model/selection~Selectable} [selectable=model.document.selection]
	 * The selection into which the content should be inserted. If not provided the current model document selection will be used.
	 */
	_onInsertContent( evt, content, selectable ) {
		if ( selectable && !selectable.is( 'documentSelection' ) ) {
			return;
		}

		const model = this.editor.model;
		const tableUtils = this.editor.plugins.get( TableUtils );

		// We might need to crop table before inserting so reference might change.
		let pastedTable = getTableIfOnlyTableInContent( content, model );

		if ( !pastedTable ) {
			return;
		}

		const selectedTableCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );

		if ( !selectedTableCells.length ) {
			removeEmptyRowsColumns( pastedTable, tableUtils );

			return;
		}

		// Override default model.insertContent() handling at this point.
		evt.stop();

		model.change( writer => {
			const pastedDimensions = {
				width: tableUtils.getColumns( pastedTable ),
				height: tableUtils.getRows( pastedTable )
			};

			// Prepare the table for pasting.
			const selection = prepareTableForPasting( selectedTableCells, pastedDimensions, writer, tableUtils );

			// Beyond this point we operate on a fixed content table with rectangular selection and proper last row/column values.

			const selectionHeight = selection.lastRow - selection.firstRow + 1;
			const selectionWidth = selection.lastColumn - selection.firstColumn + 1;

			// Crop pasted table if:
			// - Pasted table dimensions exceeds selection area.
			// - Pasted table has broken layout (ie some cells sticks out by the table dimensions established by the first and last row).
			//
			// Note: The table dimensions are established by the width of the first row and the total number of rows.
			// It is possible to programmatically create a table that has rows which would have cells anchored beyond first row width but
			// such table will not be created by other editing solutions.
			const cropDimensions = {
				startRow: 0,
				startColumn: 0,
				endRow: Math.min( selectionHeight, pastedDimensions.height ) - 1,
				endColumn: Math.min( selectionWidth, pastedDimensions.width ) - 1
			};

			pastedTable = cropTableToDimensions( pastedTable, cropDimensions, writer );

			// Content table to which we insert a pasted table.
			const selectedTable = selectedTableCells[ 0 ].findAncestor( 'table' );

			const cellsToSelect = this._replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selection, writer );

			if ( this.editor.plugins.get( 'TableSelection' ).isEnabled ) {
				// Selection ranges must be sorted because the first and last selection ranges are considered
				// as anchor/focus cell ranges for multi-cell selection.
				const selectionRanges = tableUtils.sortRanges( cellsToSelect.map( cell => writer.createRangeOn( cell ) ) );

				writer.setSelection( selectionRanges );
			} else {
				// Set selection inside first cell if multi-cell selection is disabled.
				writer.setSelection( cellsToSelect[ 0 ], 0 );
			}
		} );
	}

	/**
	 * Replaces the part of selectedTable with pastedTable.
	 *
	 * @private
	 * @param {module:engine/model/element~Element} pastedTable
	 * @param {Object} pastedDimensions
	 * @param {Number} pastedDimensions.height
	 * @param {Number} pastedDimensions.width
	 * @param {module:engine/model/element~Element} selectedTable
	 * @param {Object} selection
	 * @param {Number} selection.firstColumn
	 * @param {Number} selection.firstRow
	 * @param {Number} selection.lastColumn
	 * @param {Number} selection.lastRow
	 * @param {module:engine/model/writer~Writer} writer
	 * @returns {Array.<module:engine/model/element~Element>}
	 */
	_replaceSelectedCellsWithPasted( pastedTable, pastedDimensions, selectedTable, selection, writer ) {
		const { width: pastedWidth, height: pastedHeight } = pastedDimensions;

		// Holds two-dimensional array that is addressed by [ row ][ column ] that stores cells anchored at given location.
		const pastedTableLocationMap = createLocationMap( pastedTable, pastedWidth, pastedHeight );

		const selectedTableMap = [ ...new TableWalker( selectedTable, {
			startRow: selection.firstRow,
			endRow: selection.lastRow,
			startColumn: selection.firstColumn,
			endColumn: selection.lastColumn,
			includeAllSlots: true
		} ) ];

		// Selection must be set to pasted cells (some might be removed or new created).
		const cellsToSelect = [];

		// Store next cell insert position.
		let insertPosition;

		// Content table replace cells algorithm iterates over a selected table fragment and:
		//
		// - Removes existing table cells at current slot (location).
		// - Inserts cell from a pasted table for a matched slots.
		//
		// This ensures proper table geometry after the paste
		for ( const tableSlot of selectedTableMap ) {
			const { row, column } = tableSlot;

			// Save the insert position for current row start.
			if ( column === selection.firstColumn ) {
				insertPosition = tableSlot.getPositionBefore();
			}

			// Map current table slot location to an pasted table slot location.
			const pastedRow = row - selection.firstRow;
			const pastedColumn = column - selection.firstColumn;
			const pastedCell = pastedTableLocationMap[ pastedRow % pastedHeight ][ pastedColumn % pastedWidth ];

			// Clone cell to insert (to duplicate its attributes and children).
			// Cloning is required to support repeating pasted table content when inserting to a bigger selection.
			const cellToInsert = pastedCell ? writer.cloneElement( pastedCell ) : null;

			// Replace the cell from the current slot with new table cell.
			const newTableCell = this._replaceTableSlotCell( tableSlot, cellToInsert, insertPosition, writer );

			// The cell was only removed.
			if ( !newTableCell ) {
				continue;
			}

			// Trim the cell if it's row/col-spans would exceed selection area.
			trimTableCellIfNeeded( newTableCell, row, column, selection.lastRow, selection.lastColumn, writer );

			cellsToSelect.push( newTableCell );

			insertPosition = writer.createPositionAfter( newTableCell );
		}

		// If there are any headings, all the cells that overlap from heading must be splitted.
		const headingRows = parseInt( selectedTable.getAttribute( 'headingRows' ) || 0 );
		const headingColumns = parseInt( selectedTable.getAttribute( 'headingColumns' ) || 0 );

		const areHeadingRowsIntersectingSelection = selection.firstRow < headingRows && headingRows <= selection.lastRow;
		const areHeadingColumnsIntersectingSelection = selection.firstColumn < headingColumns && headingColumns <= selection.lastColumn;

		if ( areHeadingRowsIntersectingSelection ) {
			const columnsLimit = { first: selection.firstColumn, last: selection.lastColumn };
			const newCells = doHorizontalSplit( selectedTable, headingRows, columnsLimit, writer, selection.firstRow );

			cellsToSelect.push( ...newCells );
		}

		if ( areHeadingColumnsIntersectingSelection ) {
			const rowsLimit = { first: selection.firstRow, last: selection.lastRow };
			const newCells = doVerticalSplit( selectedTable, headingColumns, rowsLimit, writer );

			cellsToSelect.push( ...newCells );
		}

		return cellsToSelect;
	}

	/**
	 * Replaces a single table slot.
	 *
	 * @private
	 * @param {module:table/tablewalker~TableSlot} tableSlot
	 * @param {module:engine/model/element~Element} cellToInsert
	 * @param {module:engine/model/position~Position} insertPosition
	 * @param {module:engine/model/writer~Writer} writer
	 * @returns {module:engine/model/element~Element|null} Inserted table cell or null if slot should remain empty.
	 */
	_replaceTableSlotCell( tableSlot, cellToInsert, insertPosition, writer ) {
		const { cell, isAnchor } = tableSlot;

		// If the slot is occupied by a cell in a selected table - remove it.
		// The slot of this cell will be either:
		// - Replaced by a pasted table cell.
		// - Spanned by a previously pasted table cell.
		if ( isAnchor ) {
			writer.remove( cell );
		}

		// There is no cell to insert (might be spanned by other cell in a pasted table) - advance to the next content table slot.
		if ( !cellToInsert ) {
			return null;
		}

		writer.insert( cellToInsert, insertPosition );

		return cellToInsert;
	}

	/**
	 * Extracts the table for pasting into a table.
	 *
	 * @protected
	 * @param {module:engine/model/documentfragment~DocumentFragment|module:engine/model/item~Item} content The content to insert.
	 * @param {module:engine/model/model~Model} model The editor model.
	 * @returns {module:engine/model/element~Element|null}
	 */
	getTableIfOnlyTableInContent( content, model ) {
		return getTableIfOnlyTableInContent( content, model );
	}
}

function getTableIfOnlyTableInContent( content, model ) {
	if ( !content.is( 'documentFragment' ) && !content.is( 'element' ) ) {
		return null;
	}

	// Table passed directly.
	if ( content.is( 'element', 'table' ) ) {
		return content;
	}

	// We do not support mixed content when pasting table into table.
	// See: https://github.com/ckeditor/ckeditor5/issues/6817.
	if ( content.childCount == 1 && content.getChild( 0 ).is( 'element', 'table' ) ) {
		return content.getChild( 0 );
	}

	// If there are only whitespaces around a table then use that table for pasting.

	const contentRange = model.createRangeIn( content );

	for ( const element of contentRange.getItems() ) {
		if ( element.is( 'element', 'table' ) ) {
			// Stop checking if there is some content before table.
			const rangeBefore = model.createRange( contentRange.start, model.createPositionBefore( element ) );

			if ( model.hasContent( rangeBefore, { ignoreWhitespaces: true } ) ) {
				return null;
			}

			// Stop checking if there is some content after table.
			const rangeAfter = model.createRange( model.createPositionAfter( element ), contentRange.end );

			if ( model.hasContent( rangeAfter, { ignoreWhitespaces: true } ) ) {
				return null;
			}

			// There wasn't any content neither before nor after.
			return element;
		}
	}

	return null;
}

// Prepares a table for pasting and returns adjusted selection dimensions.
//
// @param {Array.<module:engine/model/element~Element>} selectedTableCells
// @param {Object} pastedDimensions
// @param {Number} pastedDimensions.height
// @param {Number} pastedDimensions.width
// @param {module:engine/model/writer~Writer} writer
// @param {module:table/tableutils~TableUtils} tableUtils
// @returns {Object} selection
// @returns {Number} selection.firstColumn
// @returns {Number} selection.firstRow
// @returns {Number} selection.lastColumn
// @returns {Number} selection.lastRow
function prepareTableForPasting( selectedTableCells, pastedDimensions, writer, tableUtils ) {
	const selectedTable = selectedTableCells[ 0 ].findAncestor( 'table' );

	const columnIndexes = tableUtils.getColumnIndexes( selectedTableCells );
	const rowIndexes = tableUtils.getRowIndexes( selectedTableCells );

	const selection = {
		firstColumn: columnIndexes.first,
		lastColumn: columnIndexes.last,
		firstRow: rowIndexes.first,
		lastRow: rowIndexes.last
	};

	// Single cell selected - expand selection to pasted table dimensions.
	const shouldExpandSelection = selectedTableCells.length === 1;

	if ( shouldExpandSelection ) {
		selection.lastRow += pastedDimensions.height - 1;
		selection.lastColumn += pastedDimensions.width - 1;

		expandTableSize( selectedTable, selection.lastRow + 1, selection.lastColumn + 1, tableUtils );
	}

	// In case of expanding selection we do not reset the selection so in this case we will always try to fix selection
	// like in the case of a non-rectangular area. This might be fixed by re-setting selected cells array but this shortcut is safe.
	if ( shouldExpandSelection || !tableUtils.isSelectionRectangular( selectedTableCells ) ) {
		// For a non-rectangular selection (ie in which some cells sticks out from a virtual selection rectangle) we need to create
		// a table layout that has a rectangular selection. This will split cells so the selection become rectangular.
		// Beyond this point we will operate on fixed content table.
		splitCellsToRectangularSelection( selectedTable, selection, writer );
	}
	// However a selected table fragment might be invalid if examined alone. Ie such table fragment:
	//
	//    +---+---+---+---+
	//  0 | a | b | c | d |
	//    +   +   +---+---+
	//  1 |   | e | f | g |
	//    +   +---+   +---+
	//  2 |   | h |   | i | <- last row, each cell has rowspan = 2,
	//    +   +   +   +   +    so we need to return 3, not 2
	//  3 |   |   |   |   |
	//    +---+---+---+---+
	//
	// is invalid as the cells "h" and "i" have rowspans.
	// This case needs only adjusting the selection dimension as the rest of the algorithm operates on empty slots also.
	else {
		selection.lastRow = adjustLastRowIndex( selectedTable, selection );
		selection.lastColumn = adjustLastColumnIndex( selectedTable, selection );
	}

	return selection;
}

// Expand table (in place) to expected size.
function expandTableSize( table, expectedHeight, expectedWidth, tableUtils ) {
	const tableWidth = tableUtils.getColumns( table );
	const tableHeight = tableUtils.getRows( table );

	if ( expectedWidth > tableWidth ) {
		tableUtils.insertColumns( table, {
			at: tableWidth,
			columns: expectedWidth - tableWidth
		} );
	}

	if ( expectedHeight > tableHeight ) {
		tableUtils.insertRows( table, {
			at: tableHeight,
			rows: expectedHeight - tableHeight
		} );
	}
}

// Returns two-dimensional array that is addressed by [ row ][ column ] that stores cells anchored at given location.
//
// At given row & column location it might be one of:
//
// * cell - cell from pasted table anchored at this location.
// * null - if no cell is anchored at this location.
//
// For instance, from a table below:
//
//		+----+----+----+----+
//		| 00 | 01 | 02 | 03 |
//		+    +----+----+----+
//		|    | 11      | 13 |
//		+----+         +----+
//		| 20 |         | 23 |
//		+----+----+----+----+
//
// The method will return an array (numbers represents cell element):
//
//	const map = [
//		[ '00', '01', '02', '03' ],
//		[ null, '11', null, '13' ],
//		[ '20', null, null, '23' ]
//	]
//
// This allows for a quick access to table at give row & column. For instance to access table cell "13" from pasted table call:
//
//		const cell = map[ 1 ][ 3 ]
//
function createLocationMap( table, width, height ) {
	// Create height x width (row x column) two-dimensional table to store cells.
	const map = new Array( height ).fill( null )
		.map( () => new Array( width ).fill( null ) );

	for ( const { column, row, cell } of new TableWalker( table ) ) {
		map[ row ][ column ] = cell;
	}

	return map;
}

// Make selected cells rectangular by splitting the cells that stand out from a rectangular selection.
//
// In the table below a selection is shown with "::" and slots with anchor cells are named.
//
// +----+----+----+----+----+                    +----+----+----+----+----+
// | 00 | 01 | 02 | 03      |                    | 00 | 01 | 02 | 03      |
// +    +----+    +----+----+                    |    ::::::::::::::::----+
// |    | 11 |    | 13 | 14 |                    |    ::11 |    | 13:: 14 |    <- first row
// +----+----+    +    +----+                    +----::---|    |   ::----+
// | 20 | 21 |    |    | 24 |   select cells:    | 20 ::21 |    |   :: 24 |
// +----+----+    +----+----+     11 -> 33       +----::---|    |---::----+
// | 30      |    | 33 | 34 |                    | 30 ::   |    | 33:: 34 |    <- last row
// +         +    +----+    +                    |    ::::::::::::::::    +
// |         |    | 43 |    |                    |         |    | 43 |    |
// +----+----+----+----+----+                    +----+----+----+----+----+
//                                                      ^          ^
//                                                     first & last columns
//
// Will update table to:
//
//                       +----+----+----+----+----+
//                       | 00 | 01 | 02 | 03      |
//                       +    +----+----+----+----+
//                       |    | 11 |    | 13 | 14 |
//                       +----+----+    +    +----+
//                       | 20 | 21 |    |    | 24 |
//                       +----+----+    +----+----+
//                       | 30 |    |    | 33 | 34 |
//                       +    +----+----+----+    +
//                       |    |    |    | 43 |    |
//                       +----+----+----+----+----+
//
// In th example above:
// - Cell "02" which have `rowspan = 4` must be trimmed at first and at after last row.
// - Cell "03" which have `rowspan = 2` and `colspan = 2` must be trimmed at first column and after last row.
// - Cells "00", "03" & "30" which cannot be cut by this algorithm as they are outside the trimmed area.
// - Cell "13" cannot be cut as it is inside the trimmed area.
function splitCellsToRectangularSelection( table, dimensions, writer ) {
	const { firstRow, lastRow, firstColumn, lastColumn } = dimensions;

	const rowIndexes = { first: firstRow, last: lastRow };
	const columnIndexes = { first: firstColumn, last: lastColumn };

	// 1. Split cells vertically in two steps as first step might create cells that needs to split again.
	doVerticalSplit( table, firstColumn, rowIndexes, writer );
	doVerticalSplit( table, lastColumn + 1, rowIndexes, writer );

	// 2. Split cells horizontally in two steps as first step might create cells that needs to split again.
	doHorizontalSplit( table, firstRow, columnIndexes, writer );
	doHorizontalSplit( table, lastRow + 1, columnIndexes, writer, firstRow );
}

function doHorizontalSplit( table, splitRow, limitColumns, writer, startRow = 0 ) {
	// If selection starts at first row then no split is needed.
	if ( splitRow < 1 ) {
		return;
	}

	const overlappingCells = getVerticallyOverlappingCells( table, splitRow, startRow );

	// Filter out cells that are not touching insides of the rectangular selection.
	const cellsToSplit = overlappingCells.filter( ( { column, cellWidth } ) => isAffectedBySelection( column, cellWidth, limitColumns ) );

	return cellsToSplit.map( ( { cell } ) => splitHorizontally( cell, splitRow, writer ) );
}

function doVerticalSplit( table, splitColumn, limitRows, writer ) {
	// If selection starts at first column then no split is needed.
	if ( splitColumn < 1 ) {
		return;
	}

	const overlappingCells = getHorizontallyOverlappingCells( table, splitColumn );

	// Filter out cells that are not touching insides of the rectangular selection.
	const cellsToSplit = overlappingCells.filter( ( { row, cellHeight } ) => isAffectedBySelection( row, cellHeight, limitRows ) );

	return cellsToSplit.map( ( { cell, column } ) => splitVertically( cell, column, splitColumn, writer ) );
}

// Checks if cell at given row (column) is affected by a rectangular selection defined by first/last column (row).
//
// The same check is used for row as for column.
function isAffectedBySelection( index, span, limit ) {
	const endIndex = index + span - 1;
	const { first, last } = limit;

	const isInsideSelection = index >= first && index <= last;
	const overlapsSelectionFromOutside = index < first && endIndex >= first;

	return isInsideSelection || overlapsSelectionFromOutside;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablekeyboard.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablekeyboard
 */








/**
 * This plugin enables keyboard navigation for tables.
 * It is loaded automatically by the {@link module:table/table~Table} plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableKeyboard extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableKeyboard';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableSelection, TableUtils ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const view = this.editor.editing.view;
		const viewDocument = view.document;

		this.listenTo( viewDocument, 'arrowKey', ( ...args ) => this._onArrowKey( ...args ), { context: 'table' } );
		this.listenTo( viewDocument, 'tab', ( ...args ) => this._handleTabOnSelectedTable( ...args ), { context: 'figure' } );
		this.listenTo( viewDocument, 'tab', ( ...args ) => this._handleTab( ...args ), { context: [ 'th', 'td' ] } );
	}

	/**
	 * Handles {@link module:engine/view/document~Document#event:tab tab} events for the <kbd>Tab</kbd> key executed
	 * when the table widget is selected.
	 *
	 * @private
	 * @param {module:engine/view/observer/bubblingeventinfo~BubblingEventInfo} bubblingEventInfo
	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
	 */
	_handleTabOnSelectedTable( bubblingEventInfo, domEventData ) {
		const editor = this.editor;
		const selection = editor.model.document.selection;
		const selectedElement = selection.getSelectedElement();

		if ( !selectedElement || !selectedElement.is( 'element', 'table' ) ) {
			return;
		}

		domEventData.preventDefault();
		domEventData.stopPropagation();
		bubblingEventInfo.stop();

		editor.model.change( writer => {
			writer.setSelection( writer.createRangeIn( selectedElement.getChild( 0 ).getChild( 0 ) ) );
		} );
	}

	/**
	 * Handles {@link module:engine/view/document~Document#event:tab tab} events for the <kbd>Tab</kbd> key executed
	 * inside table cells.
	 *
	 * @private
	 * @param {module:engine/view/observer/bubblingeventinfo~BubblingEventInfo} bubblingEventInfo
	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
	 */
	_handleTab( bubblingEventInfo, domEventData ) {
		const editor = this.editor;
		const tableUtils = this.editor.plugins.get( TableUtils );

		const selection = editor.model.document.selection;
		const isForward = !domEventData.shiftKey;

		let tableCell = tableUtils.getTableCellsContainingSelection( selection )[ 0 ];

		if ( !tableCell ) {
			tableCell = this.editor.plugins.get( 'TableSelection' ).getFocusCell();
		}

		if ( !tableCell ) {
			return;
		}

		domEventData.preventDefault();
		domEventData.stopPropagation();
		bubblingEventInfo.stop();

		const tableRow = tableCell.parent;
		const table = tableRow.parent;

		const currentRowIndex = table.getChildIndex( tableRow );
		const currentCellIndex = tableRow.getChildIndex( tableCell );

		const isFirstCellInRow = currentCellIndex === 0;

		if ( !isForward && isFirstCellInRow && currentRowIndex === 0 ) {
			// Set the selection over the whole table if the selection was in the first table cell.
			editor.model.change( writer => {
				writer.setSelection( writer.createRangeOn( table ) );
			} );

			return;
		}

		const isLastCellInRow = currentCellIndex === tableRow.childCount - 1;
		const isLastRow = currentRowIndex === tableUtils.getRows( table ) - 1;

		if ( isForward && isLastRow && isLastCellInRow ) {
			editor.execute( 'insertTableRowBelow' );

			// Check if the command actually added a row. If `insertTableRowBelow` execution didn't add a row (because it was disabled
			// or it got overwritten) set the selection over the whole table to mirror the first cell case.
			if ( currentRowIndex === tableUtils.getRows( table ) - 1 ) {
				editor.model.change( writer => {
					writer.setSelection( writer.createRangeOn( table ) );
				} );

				return;
			}
		}

		let cellToFocus;

		// Move to the first cell in the next row.
		if ( isForward && isLastCellInRow ) {
			const nextRow = table.getChild( currentRowIndex + 1 );

			cellToFocus = nextRow.getChild( 0 );
		}
		// Move to the last cell in the previous row.
		else if ( !isForward && isFirstCellInRow ) {
			const previousRow = table.getChild( currentRowIndex - 1 );

			cellToFocus = previousRow.getChild( previousRow.childCount - 1 );
		}
		// Move to the next/previous cell.
		else {
			cellToFocus = tableRow.getChild( currentCellIndex + ( isForward ? 1 : -1 ) );
		}

		editor.model.change( writer => {
			writer.setSelection( writer.createRangeIn( cellToFocus ) );
		} );
	}

	/**
	 * Handles {@link module:engine/view/document~Document#event:keydown keydown} events.
	 *
	 * @private
	 * @param {module:utils/eventinfo~EventInfo} eventInfo
	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
	 */
	_onArrowKey( eventInfo, domEventData ) {
		const editor = this.editor;
		const keyCode = domEventData.keyCode;

		const direction = getLocalizedArrowKeyCodeDirection( keyCode, editor.locale.contentLanguageDirection );
		const wasHandled = this._handleArrowKeys( direction, domEventData.shiftKey );

		if ( wasHandled ) {
			domEventData.preventDefault();
			domEventData.stopPropagation();
			eventInfo.stop();
		}
	}

	/**
	 * Handles arrow keys to move the selection around the table.
	 *
	 * @private
	 * @param {'left'|'up'|'right'|'down'} direction The direction of the arrow key.
	 * @param {Boolean} expandSelection If the current selection should be expanded.
	 * @returns {Boolean} Returns `true` if key was handled.
	 */
	_handleArrowKeys( direction, expandSelection ) {
		const tableUtils = this.editor.plugins.get( TableUtils );
		const model = this.editor.model;
		const selection = model.document.selection;
		const isForward = [ 'right', 'down' ].includes( direction );

		// In case one or more table cells are selected (from outside),
		// move the selection to a cell adjacent to the selected table fragment.
		const selectedCells = tableUtils.getSelectedTableCells( selection );

		if ( selectedCells.length ) {
			let focusCell;

			if ( expandSelection ) {
				focusCell = this.editor.plugins.get( 'TableSelection' ).getFocusCell();
			} else {
				focusCell = isForward ? selectedCells[ selectedCells.length - 1 ] : selectedCells[ 0 ];
			}

			this._navigateFromCellInDirection( focusCell, direction, expandSelection );

			return true;
		}

		// Abort if we're not in a table cell.
		const tableCell = selection.focus.findAncestor( 'tableCell' );

		/* istanbul ignore if: paranoid check */
		if ( !tableCell ) {
			return false;
		}

		// When the selection is not collapsed.
		if ( !selection.isCollapsed ) {
			if ( expandSelection ) {
				// Navigation is in the opposite direction than the selection direction so this is shrinking of the selection.
				// Selection for sure will not approach cell edge.
				//
				// With a special case when all cell content is selected - then selection should expand to the other cell.
				// Note: When the entire cell gets selected using CTRL+A, the selection is always forward.
				if ( selection.isBackward == isForward && !selection.containsEntireContent( tableCell ) ) {
					return false;
				}
			} else {
				const selectedElement = selection.getSelectedElement();

				// It will collapse for non-object selected so it's not going to move to other cell.
				if ( !selectedElement || !model.schema.isObject( selectedElement ) ) {
					return false;
				}
			}
		}

		// Let's check if the selection is at the beginning/end of the cell.
		if ( this._isSelectionAtCellEdge( selection, tableCell, isForward ) ) {
			this._navigateFromCellInDirection( tableCell, direction, expandSelection );

			return true;
		}

		return false;
	}

	/**
	 * Returns `true` if the selection is at the boundary of a table cell according to the navigation direction.
	 *
	 * @private
	 * @param {module:engine/model/selection~Selection} selection The current selection.
	 * @param {module:engine/model/element~Element} tableCell The current table cell element.
	 * @param {Boolean} isForward The expected navigation direction.
	 * @returns {Boolean}
	 */
	_isSelectionAtCellEdge( selection, tableCell, isForward ) {
		const model = this.editor.model;
		const schema = this.editor.model.schema;

		const focus = isForward ? selection.getLastPosition() : selection.getFirstPosition();

		// If the current limit element is not table cell we are for sure not at the cell edge.
		// Also `modifySelection` will not let us out of it.
		if ( !schema.getLimitElement( focus ).is( 'element', 'tableCell' ) ) {
			const boundaryPosition = model.createPositionAt( tableCell, isForward ? 'end' : 0 );

			return boundaryPosition.isTouching( focus );
		}

		const probe = model.createSelection( focus );

		model.modifySelection( probe, { direction: isForward ? 'forward' : 'backward' } );

		// If there was no change in the focus position, then it's not possible to move the selection there.
		return focus.isEqual( probe.focus );
	}

	/**
	 * Moves the selection from the given table cell in the specified direction.
	 *
	 * @protected
	 * @param {module:engine/model/element~Element} focusCell The table cell that is current multi-cell selection focus.
	 * @param {'left'|'up'|'right'|'down'} direction Direction in which selection should move.
	 * @param {Boolean} [expandSelection=false] If the current selection should be expanded.
	 */
	_navigateFromCellInDirection( focusCell, direction, expandSelection = false ) {
		const model = this.editor.model;

		const table = focusCell.findAncestor( 'table' );
		const tableMap = [ ...new TableWalker( table, { includeAllSlots: true } ) ];
		const { row: lastRow, column: lastColumn } = tableMap[ tableMap.length - 1 ];

		const currentCellInfo = tableMap.find( ( { cell } ) => cell == focusCell );
		let { row, column } = currentCellInfo;

		switch ( direction ) {
			case 'left':
				column--;
				break;

			case 'up':
				row--;
				break;

			case 'right':
				column += currentCellInfo.cellWidth;
				break;

			case 'down':
				row += currentCellInfo.cellHeight;
				break;
		}

		const isOutsideVertically = row < 0 || row > lastRow;
		const isBeforeFirstCell = column < 0 && row <= 0;
		const isAfterLastCell = column > lastColumn && row >= lastRow;

		// Note that if the table cell at the end of a row is row-spanned then isAfterLastCell will never be true.
		// However, we don't know if user was navigating on the last row or not, so let's stay in the table.

		if ( isOutsideVertically || isBeforeFirstCell || isAfterLastCell ) {
			model.change( writer => {
				writer.setSelection( writer.createRangeOn( table ) );
			} );

			return;
		}

		if ( column < 0 ) {
			column = expandSelection ? 0 : lastColumn;
			row--;
		} else if ( column > lastColumn ) {
			column = expandSelection ? lastColumn : 0;
			row++;
		}

		const cellToSelect = tableMap.find( cellInfo => cellInfo.row == row && cellInfo.column == column ).cell;
		const isForward = [ 'right', 'down' ].includes( direction );
		const tableSelection = this.editor.plugins.get( 'TableSelection' );

		if ( expandSelection && tableSelection.isEnabled ) {
			const anchorCell = tableSelection.getAnchorCell() || focusCell;

			tableSelection.setCellSelection( anchorCell, cellToSelect );
		} else {
			const positionToSelect = model.createPositionAt( cellToSelect, isForward ? 0 : 'end' );

			model.change( writer => {
				writer.setSelection( positionToSelect );
			} );
		}
	}
}


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablemouse/mouseeventsobserver.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableselection/mouseeventsobserver
 */



/**
 * The mouse selection event observer.
 *
 * It registers listeners for the following DOM events:
 *
 * - `'mousemove'`
 * - `'mouseup'`
 * - `'mouseleave'`
 *
 * Note that this observer is disabled by default. To enable this observer, it needs to be added to
 * {@link module:engine/view/view~View} using the {@link module:engine/view/view~View#addObserver} method.
 *
 * The observer is registered by the {@link module:table/tableselection~TableSelection} plugin.
 *
 * @extends module:engine/view/observer/domeventobserver~DomEventObserver
 */
class MouseEventsObserver extends DomEventObserver {
	/**
	 * @inheritDoc
	 */
	constructor( view ) {
		super( view );

		this.domEventType = [ 'mousemove', 'mouseleave' ];
	}

	/**
	 * @inheritDoc
	 */
	onDomEvent( domEvent ) {
		this.fire( domEvent.type, domEvent );
	}
}

/**
 * Fired when the mouse is moved over one of the editables.
 *
 * Introduced by {@link module:table/tableselection/mouseeventsobserver~MouseEventsObserver}.
 *
 * Note that this event is not available by default. To make it available,
 * {@link module:table/tableselection/mouseeventsobserver~MouseEventsObserver} needs to be added
 * to {@link module:engine/view/view~View} using the {@link module:engine/view/view~View#addObserver} method.
 *
 * @see module:table/tableselection/mouseeventsobserver~MouseEventsObserver
 * @event module:engine/view/document~Document#event:mousemove
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */

/**
 * Fired when the mouse is moved out of one of the editables.
 *
 * Introduced by {@link module:table/tableselection/mouseeventsobserver~MouseEventsObserver}.
 *
 * Note that this event is not available by default. To make it available,
 * {@link module:table/tableselection/mouseeventsobserver~MouseEventsObserver} needs to be added
 * to {@link module:engine/view/view~View} using the {@link module:engine/view/view~View#addObserver} method.
 *
 * @see module:table/tableselection/mouseeventsobserver~MouseEventsObserver
 * @event module:engine/view/document~Document#event:mouseleave
 * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablemouse.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablemouse
 */







/**
 * This plugin enables a table cells' selection with the mouse.
 * It is loaded automatically by the {@link module:table/table~Table} plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableMouse extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableMouse';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableSelection, TableUtils ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Currently the MouseObserver only handles `mousedown` and `mouseup` events.
		// TODO move to the engine?
		editor.editing.view.addObserver( MouseEventsObserver );

		this._enableShiftClickSelection();
		this._enableMouseDragSelection();
	}

	/**
	 * Enables making cells selection by <kbd>Shift</kbd>+click. Creates a selection from the cell which previously held
	 * the selection to the cell which was clicked. It can be the same cell, in which case it selects a single cell.
	 *
	 * @private
	 */
	_enableShiftClickSelection() {
		const editor = this.editor;
		const tableUtils = editor.plugins.get( TableUtils );
		let blockSelectionChange = false;

		const tableSelection = editor.plugins.get( TableSelection );

		this.listenTo( editor.editing.view.document, 'mousedown', ( evt, domEventData ) => {
			const selection = editor.model.document.selection;

			if ( !this.isEnabled || !tableSelection.isEnabled ) {
				return;
			}

			if ( !domEventData.domEvent.shiftKey ) {
				return;
			}

			const anchorCell = tableSelection.getAnchorCell() || tableUtils.getTableCellsContainingSelection( selection )[ 0 ];

			if ( !anchorCell ) {
				return;
			}

			const targetCell = this._getModelTableCellFromDomEvent( domEventData );

			if ( targetCell && haveSameTableParent( anchorCell, targetCell ) ) {
				blockSelectionChange = true;
				tableSelection.setCellSelection( anchorCell, targetCell );

				domEventData.preventDefault();
			}
		} );

		this.listenTo( editor.editing.view.document, 'mouseup', () => {
			blockSelectionChange = false;
		} );

		// We need to ignore a `selectionChange` event that is fired after we render our new table cells selection.
		// When downcasting table cells selection to the view, we put the view selection in the last selected cell
		// in a place that may not be natively a "correct" location. This is – we put it directly in the `<td>` element.
		// All browsers fire the native `selectionchange` event.
		// However, all browsers except Safari return the selection in the exact place where we put it
		// (even though it's visually normalized). Safari returns `<td><p>^foo` that makes our selection observer
		// fire our `selectionChange` event (because the view selection that we set in the first step differs from the DOM selection).
		// Since `selectionChange` is fired, we automatically update the model selection that moves it that paragraph.
		// This breaks our dear cells selection.
		//
		// Theoretically this issue concerns only Safari that is the only browser that do normalize the selection.
		// However, to avoid code branching and to have a good coverage for this event blocker, I enabled it for all browsers.
		//
		// Note: I'm keeping the `blockSelectionChange` state separately for shift+click and mouse drag (exact same logic)
		// so I don't have to try to analyze whether they don't overlap in some weird cases. Probably they don't.
		// But I have other things to do, like writing this comment.
		this.listenTo( editor.editing.view.document, 'selectionChange', evt => {
			if ( blockSelectionChange ) {
				// @if CK_DEBUG // console.log( 'Blocked selectionChange to avoid breaking table cells selection.' );

				evt.stop();
			}
		}, { priority: 'highest' } );
	}

	/**
	 * Enables making cells selection by dragging.
	 *
	 * The selection is made only on mousemove. Mouse tracking is started on mousedown.
	 * However, the cells selection is enabled only after the mouse cursor left the anchor cell.
	 * Thanks to that normal text selection within one cell works just fine. However, you can still select
	 * just one cell by leaving the anchor cell and moving back to it.
	 *
	 * @private
	 */
	_enableMouseDragSelection() {
		const editor = this.editor;
		let anchorCell, targetCell;
		let beganCellSelection = false;
		let blockSelectionChange = false;

		const tableSelection = editor.plugins.get( TableSelection );

		this.listenTo( editor.editing.view.document, 'mousedown', ( evt, domEventData ) => {
			if ( !this.isEnabled || !tableSelection.isEnabled ) {
				return;
			}

			// Make sure to not conflict with the shift+click listener and any other possible handler.
			if ( domEventData.domEvent.shiftKey || domEventData.domEvent.ctrlKey || domEventData.domEvent.altKey ) {
				return;
			}

			anchorCell = this._getModelTableCellFromDomEvent( domEventData );
		} );

		this.listenTo( editor.editing.view.document, 'mousemove', ( evt, domEventData ) => {
			if ( !domEventData.domEvent.buttons ) {
				return;
			}

			if ( !anchorCell ) {
				return;
			}

			const newTargetCell = this._getModelTableCellFromDomEvent( domEventData );

			if ( newTargetCell && haveSameTableParent( anchorCell, newTargetCell ) ) {
				targetCell = newTargetCell;

				// Switch to the cell selection mode after the mouse cursor left the anchor cell.
				// Switch off only on mouseup (makes selecting a single cell possible).
				if ( !beganCellSelection && targetCell != anchorCell ) {
					beganCellSelection = true;
				}
			}

			// Yep, not making a cell selection yet. See method docs.
			if ( !beganCellSelection ) {
				return;
			}

			blockSelectionChange = true;
			tableSelection.setCellSelection( anchorCell, targetCell );

			domEventData.preventDefault();
		} );

		this.listenTo( editor.editing.view.document, 'mouseup', () => {
			beganCellSelection = false;
			blockSelectionChange = false;
			anchorCell = null;
			targetCell = null;
		} );

		// See the explanation in `_enableShiftClickSelection()`.
		this.listenTo( editor.editing.view.document, 'selectionChange', evt => {
			if ( blockSelectionChange ) {
				// @if CK_DEBUG // console.log( 'Blocked selectionChange to avoid breaking table cells selection.' );

				evt.stop();
			}
		}, { priority: 'highest' } );
	}

	/**
	 * Returns the model table cell element based on the target element of the passed DOM event.
	 *
	 * @private
	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData
	 * @returns {module:engine/model/element~Element|undefined} Returns the table cell or `undefined`.
	 */
	_getModelTableCellFromDomEvent( domEventData ) {
		// Note: Work with positions (not element mapping) because the target element can be an attribute or other non-mapped element.
		const viewTargetElement = domEventData.target;
		const viewPosition = this.editor.editing.view.createPositionAt( viewTargetElement, 0 );
		const modelPosition = this.editor.editing.mapper.toModelPosition( viewPosition );
		const modelElement = modelPosition.parent;

		return modelElement.findAncestor( 'tableCell', { includeSelf: true } );
	}
}

function haveSameTableParent( cellA, cellB ) {
	return cellA.parent.parent == cellB.parent.parent;
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/table.css
var theme_table = __webpack_require__(4104);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/table.css

            

var table_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

table_options.insert = "head";
table_options.singleton = true;

var table_update = injectStylesIntoStyleTag_default()(theme_table/* default */.Z, table_options);



/* harmony default export */ const ckeditor5_table_theme_table = (theme_table/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/table.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/table
 */













/**
 * The table plugin.
 *
 * For a detailed overview, check the {@glink features/table Table feature documentation}.
 *
 * This is a "glue" plugin that loads the following table features:
 *
 * * {@link module:table/tableediting~TableEditing editing feature},
 * * {@link module:table/tableselection~TableSelection selection feature},
 * * {@link module:table/tablekeyboard~TableKeyboard keyboard navigation feature},
 * * {@link module:table/tablemouse~TableMouse mouse selection feature},
 * * {@link module:table/tableclipboard~TableClipboard clipboard feature},
 * * {@link module:table/tableui~TableUI UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class Table extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableEditing, TableUI, TableSelection, TableMouse, TableKeyboard, TableClipboard, Widget ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Table';
	}
}

/**
 * The configuration of the table feature. Used by the table feature in the `@ckeditor/ckeditor5-table` package.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 * 				table: ... // Table feature options.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface TableConfig
 */

/**
 * The configuration of the {@link module:table/table~Table} feature.
 *
 * Read more in {@link module:table/table~TableConfig}.
 *
 * @member {module:table/table~TableConfig} module:core/editor/editorconfig~EditorConfig#table
 */

/**
 * Number of rows and columns to render by default as table heading when inserting new tables.
 *
 * You can configure it like this:
 *
 *		const tableConfig = {
 *			defaultHeadings: {
 *				rows: 1,
 *				columns: 1
 *			}
 *		};
 *
 * Both rows and columns properties are optional defaulting to 0 (no heading).
 *
 * @member {Object} module:table/table~TableConfig#defaultHeadings
 */

/**
 * An array of color definitions (either strings or objects).
 *
 *		const colors = [
 *			{
 *				color: 'hsl(0, 0%, 60%)',
 *				label: 'Grey'
 *			},
 *			'hsl(0, 0%, 80%)',
 *			{
 *				color: 'hsl(0, 0%, 90%)',
 *				label: 'Light grey'
 *			},
 *			{
 *				color: 'hsl(0, 0%, 100%)',
 *				label: 'White',
 *				hasBorder: true
 *			},
 *			'#FF0000'
 *		]
 *
 * Usually used as a configuration parameter, for instance in
 * {@link module:table/table~TableConfig#tableProperties `config.table.tableProperties`}
 * or {@link module:table/table~TableConfig#tableCellProperties `config.table.tableCellProperties`}.
 *
 * @typedef {Array.<String|Object>} module:table/table~TableColorConfig
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/converters/table-caption-post-fixer.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/converters/table-caption-post-fixer
 */

/**
 * Injects a table caption post-fixer into the model.
 *
 * The role of the table caption post-fixer is to ensure that the table with caption have the correct structure
 * after a {@link module:engine/model/model~Model#change `change()`} block was executed.
 *
 * The correct structure means that:
 *
 * * If there are many caption model element, they are merged into one model.
 * * A final, merged caption model is placed at the end of the table.
 *
 * @param {module:engine/model/model~Model} model
 */
function injectTableCaptionPostFixer( model ) {
	model.document.registerPostFixer( writer => tableCaptionPostFixer( writer, model ) );
}

// The table caption post-fixer.
//
// @param {module:engine/model/writer~Writer} writer
// @param {module:engine/model/model~Model} model
function tableCaptionPostFixer( writer, model ) {
	const changes = model.document.differ.getChanges();
	let wasFixed = false;

	for ( const entry of changes ) {
		if ( entry.type != 'insert' ) {
			continue;
		}

		const positionParent = entry.position.parent;

		if ( positionParent.is( 'element', 'table' ) || entry.name == 'table' ) {
			const table = entry.name == 'table' ? entry.position.nodeAfter : entry.position.parent;
			const captionsToMerge = Array.from( table.getChildren() ).filter( child => child.is( 'element', 'caption' ) );
			const firstCaption = captionsToMerge.shift();

			if ( !firstCaption ) {
				continue;
			}

			// Move all the contents of the captions to the first one.
			for ( const caption of captionsToMerge ) {
				writer.move( writer.createRangeIn( caption ), firstCaption, 'end' );
				writer.remove( caption );
			}

			// Make sure the final caption is at the end of the table.
			if ( firstCaption.nextSibling ) {
				writer.move( writer.createRangeOn( firstCaption ), table, 'end' );
				wasFixed = true;
			}

			// Do we merged captions and/or moved the single caption to the end of the table?
			wasFixed = !!captionsToMerge.length || wasFixed;
		}
	}

	return wasFixed;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecaption/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecaption/utils
 */

/**
 * Checks if the provided model element is a `table`.
 *
 * @param {module:engine/model/element~Element} modelElement Element to check if it is a table.
 * @returns {Boolean}
 */
function isTable( modelElement ) {
	return !!modelElement && modelElement.is( 'element', 'table' );
}

/**
 * Returns the caption model element from a given table element. Returns `null` if no caption is found.
 *
 * @param {module:engine/model/element~Element} tableModelElement Table element in which we will try to find a caption element.
 * @returns {module:engine/model/element~Element|null}
 */
function getCaptionFromTableModelElement( tableModelElement ) {
	for ( const node of tableModelElement.getChildren() ) {
		if ( node.is( 'element', 'caption' ) ) {
			return node;
		}
	}

	return null;
}

/**
 * Returns the caption model element for a model selection. Returns `null` if the selection has no caption element ancestor.
 *
 * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
 * The selection checked for caption presence.
 * @returns {module:engine/model/element~Element|null}
 */
function getCaptionFromModelSelection( selection ) {
	const tableElement = getSelectionAffectedTable( selection );

	if ( !tableElement ) {
		return null;
	}

	return getCaptionFromTableModelElement( tableElement );
}

/**
 * {@link module:engine/view/matcher~Matcher} pattern. Checks if a given element is a caption.
 *
 * There are two possible forms of the valid caption:
 *  - A `<figcaption>` element inside a `<figure class="table">` element.
 *  - A `<caption>` inside a <table>.
 *
 * @param {module:engine/view/element~Element} element
 * @returns {Object|null} Returns the object accepted by {@link module:engine/view/matcher~Matcher} or `null` if the element
 * cannot be matched.
 */
function matchTableCaptionViewElement( element ) {
	const parent = element.parent;

	if ( element.name == 'figcaption' && parent && parent.name == 'figure' && parent.hasClass( 'table' ) ) {
		return { name: true };
	}

	if ( element.name == 'caption' && parent && parent.name == 'table' ) {
		return { name: true };
	}

	return null;
}

/**
 * Depending on the position of the selection we either return the table under cursor or look for the table higher in the hierarchy.
 *
 * @param {module:engine/model/position~Position} position
 * @returns {module:engine/model/element~Element}
 */
function getSelectionAffectedTable( selection ) {
	const selectedElement = selection.getSelectedElement();

	// Is the command triggered from the `tableToolbar`?
	if ( selectedElement && selectedElement.is( 'element', 'table' ) ) {
		return selectedElement;
	}

	return selection.getFirstPosition().findAncestor( 'table' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecaption/toggletablecaptioncommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
* @module table/tablecaption/toggletablecaptioncommand
*/





/**
 * The toggle table caption command.
 *
 * This command is registered by {@link module:table/tablecaption/tablecaptionediting~TableCaptionEditing} as the
 * `'toggleTableCaption'` editor command.
 *
 * Executing this command:
 *
 * * either adds or removes the table caption of a selected table (depending on whether the caption is present or not),
 * * removes the table caption if the selection is anchored in one.
 *
 *		// Toggle the presence of the caption.
 *		editor.execute( 'toggleTableCaption' );
 *
 * **Note**: You can move the selection to the caption right away as it shows up upon executing this command by using
 * the `focusCaptionOnShow` option:
 *
 *		editor.execute( 'toggleTableCaption', { focusCaptionOnShow: true } );
 *
 * @extends module:core/command~Command
 */
class ToggleTableCaptionCommand extends command_Command {
	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const tableElement = getSelectionAffectedTable( editor.model.document.selection );

		this.isEnabled = !!tableElement;

		if ( !this.isEnabled ) {
			this.value = false;
		} else {
			this.value = !!getCaptionFromTableModelElement( tableElement );
		}
	}

	/**
	 * Executes the command.
	 *
	 *		editor.execute( 'toggleTableCaption' );
	 *
	 * @param {Object} [options] Options for the executed command.
	 * @param {String} [options.focusCaptionOnShow] When true and the caption shows up, the selection will be moved into it straight away.
	 * @fires execute
	 */
	execute( options = {} ) {
		const { focusCaptionOnShow } = options;

		this.editor.model.change( writer => {
			if ( this.value ) {
				this._hideTableCaption( writer );
			} else {
				this._showTableCaption( writer, focusCaptionOnShow );
			}
		} );
	}

	/**
	 * Shows the table caption. Also:
	 *
	 * * it attempts to restore the caption content from the `TableCaptionEditing` caption registry,
	 * * it moves the selection to the caption right away, it the `focusCaptionOnShow` option was set.
	 *
	 * @private
	 * @param {module:engine/model/writer~Writer} writer
	 * @param {Boolean} focusCaptionOnShow Default focus behavior when showing the caption.
	 */
	_showTableCaption( writer, focusCaptionOnShow ) {
		const model = this.editor.model;
		const tableElement = getSelectionAffectedTable( model.document.selection );
		const tableCaptionEditing = this.editor.plugins.get( 'TableCaptionEditing' );
		const savedCaptionElement = tableCaptionEditing._getSavedCaption( tableElement );

		// Try restoring the caption from the TableCaptionEditing plugin storage.
		const newCaptionElement = savedCaptionElement || writer.createElement( 'caption' );

		model.insertContent( newCaptionElement, tableElement, 'end' );

		if ( focusCaptionOnShow ) {
			writer.setSelection( newCaptionElement, 'in' );
		}
	}

	/**
	 * Hides the caption of a selected table (or an table caption the selection is anchored to).
	 *
	 * The content of the caption is stored in the `TableCaptionEditing` caption registry to make this
	 * a reversible action.
	 *
	 * @private
	 * @param {module:engine/model/writer~Writer} writer
	 */
	_hideTableCaption( writer ) {
		const model = this.editor.model;
		const tableElement = getSelectionAffectedTable( model.document.selection );
		const tableCaptionEditing = this.editor.plugins.get( 'TableCaptionEditing' );
		const captionElement = getCaptionFromTableModelElement( tableElement );

		// Store the caption content so it can be restored quickly if the user changes their mind.
		tableCaptionEditing._saveCaption( tableElement, captionElement );

		model.deleteContent( writer.createSelection( captionElement, 'on' ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecaption/tablecaptionediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecaption/tablecaptionediting
 */









/**
 * The table caption editing plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableCaptionEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableCaptionEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * A map that keeps saved JSONified table captions and table model elements they are
		 * associated with.
		 *
		 * To learn more about this system, see {@link #_saveCaption}.
		 *
		 * @member {WeakMap.<module:engine/model/element~Element,Object>}
		 */
		this._savedCaptionsMap = new WeakMap();
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;
		const view = editor.editing.view;
		const t = editor.t;

		if ( !schema.isRegistered( 'caption' ) ) {
			schema.register( 'caption', {
				allowIn: 'table',
				allowContentOf: '$block',
				isLimit: true
			} );
		} else {
			schema.extend( 'caption', {
				allowIn: 'table'
			} );
		}

		editor.commands.add( 'toggleTableCaption', new ToggleTableCaptionCommand( this.editor ) );

		// View -> model converter for the data pipeline.
		editor.conversion.for( 'upcast' ).elementToElement( {
			view: matchTableCaptionViewElement,
			model: 'caption'
		} );

		// Model -> view converter for the data pipeline.
		editor.conversion.for( 'dataDowncast' ).elementToElement( {
			model: 'caption',
			view: ( modelElement, { writer } ) => {
				if ( !isTable( modelElement.parent ) ) {
					return null;
				}

				return writer.createContainerElement( 'figcaption' );
			}
		} );

		// Model -> view converter for the editing pipeline.
		editor.conversion.for( 'editingDowncast' ).elementToElement( {
			model: 'caption',
			view: ( modelElement, { writer } ) => {
				if ( !isTable( modelElement.parent ) ) {
					return null;
				}

				const figcaptionElement = writer.createEditableElement( 'figcaption' );
				writer.setCustomProperty( 'tableCaption', true, figcaptionElement );

				enablePlaceholder( {
					view,
					element: figcaptionElement,
					text: t( 'Enter table caption' ),
					keepOnFocus: true
				} );

				return toWidgetEditable( figcaptionElement, writer );
			}
		} );

		injectTableCaptionPostFixer( editor.model );
	}

	/**
	 * Returns the saved {@link module:engine/model/element~Element#toJSON JSONified} caption
	 * of a table model element.
	 *
	 * See {@link #_saveCaption}.
	 *
	 * @protected
	 * @param {module:engine/model/element~Element} tableModelElement The model element the
	 * caption should be returned for.
	 * @returns {module:engine/model/element~Element|null} The model caption element or `null` if there is none.
	 */
	_getSavedCaption( tableModelElement ) {
		const jsonObject = this._savedCaptionsMap.get( tableModelElement );

		return jsonObject ? element_Element.fromJSON( jsonObject ) : null;
	}

	/**
	 * Saves a {@link module:engine/model/element~Element#toJSON JSONified} caption for
	 * a table element to allow restoring it in the future.
	 *
	 * A caption is saved every time it gets hidden. The
	 * user should be able to restore it on demand.
	 *
	 * **Note**: The caption cannot be stored in the table model element attribute because,
	 * for instance, when the model state propagates to collaborators, the attribute would get
	 * lost (mainly because it does not convert to anything when the caption is hidden) and
	 * the states of collaborators' models would de-synchronize causing numerous issues.
	 *
	 * See {@link #_getSavedCaption}.
	 *
	 * @protected
	 * @param {module:engine/model/element~Element} tableModelElement The model element the
	 * caption is saved for.
	 * @param {module:engine/model/element~Element} caption The caption model element to be saved.
	 */
	_saveCaption( tableModelElement, caption ) {
		this._savedCaptionsMap.set( tableModelElement, caption.toJSON() );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecaption/tablecaptionui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
* @module table/tablecaption/tablecaptionui
*/






/**
  * The table caption UI plugin. It introduces the `'toggleTableCaption'` UI button.
  *
  * @extends module:core/plugin~Plugin
  */
class TableCaptionUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableCaptionUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const editingView = editor.editing.view;
		const t = editor.t;

		editor.ui.componentFactory.add( 'toggleTableCaption', locale => {
			const command = editor.commands.get( 'toggleTableCaption' );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				icon: icons.caption,
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );
			view.bind( 'label' ).to( command, 'value', value => value ? t( 'Toggle caption off' ) : t( 'Toggle caption on' ) );

			this.listenTo( view, 'execute', () => {
				editor.execute( 'toggleTableCaption', { focusCaptionOnShow: true } );

				// Scroll to the selection and highlight the caption if the caption showed up.
				if ( command.value ) {
					const modelCaptionElement = getCaptionFromModelSelection( editor.model.document.selection );
					const figcaptionElement = editor.editing.mapper.toViewElement( modelCaptionElement );

					if ( !figcaptionElement ) {
						return;
					}

					editingView.scrollToTheSelection();
					editingView.change( writer => {
						writer.addClass( 'table__caption_highlighted', figcaptionElement );
					} );
				}

				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/tablecaption.css
var tablecaption = __webpack_require__(9888);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/tablecaption.css

            

var tablecaption_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tablecaption_options.insert = "head";
tablecaption_options.singleton = true;

var tablecaption_update = injectStylesIntoStyleTag_default()(tablecaption/* default */.Z, tablecaption_options);



/* harmony default export */ const theme_tablecaption = (tablecaption/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecaption.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecaption
 */







/**
 * The table caption plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableCaption extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableCaption';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableCaptionEditing, TableCaptionUI ];
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/colorinput.css
var colorinput = __webpack_require__(4082);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/colorinput.css

            

var colorinput_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

colorinput_options.insert = "head";
colorinput_options.singleton = true;

var colorinput_update = injectStylesIntoStyleTag_default()(colorinput/* default */.Z, colorinput_options);



/* harmony default export */ const theme_colorinput = (colorinput/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/ui/colorinputview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/ui/colorinputview
 */







/**
 * The color input view class. It allows the user to type in a color (hex, rgb, etc.)
 * or choose it from the configurable color palette with a preview.
 *
 * @private
 * @extends module:ui/view~View
 */
class ColorInputView extends src_view_View {
	/**
	 * Creates an instance of the color input view.
	 *
	 * @param {module:utils/locale~Locale} locale The locale instance.
	 * @param {Object} options The input options.
	 * @param {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>} options.colorDefinitions The colors to be displayed
	 * in the palette inside the input's dropdown.
	 * @param {Number} options.columns The number of columns in which the colors will be displayed.
	 * @param {String} [options.defaultColorValue] If specified, the color input view will replace the "Remove color" button with
	 * the "Restore default" button. Instead of clearing the input field, the default color value will be set.
	 */
	constructor( locale, options ) {
		super( locale );

		/**
		 * The value of the input.
		 *
		 * @observable
		 * @member {String} #value
		 * @default ''
		 */
		this.set( 'value', '' );

		/**
		 * Controls whether the input view is in read-only mode.
		 *
		 * @observable
		 * @member {Boolean} #isReadOnly
		 * @default false
		 */
		this.set( 'isReadOnly', false );

		/**
		 * An observable flag set to `true` when the input is focused by the user.
		 * `false` otherwise.
		 *
		 * @readonly
		 * @observable
		 * @member {Boolean} #isFocused
		 * @default false
		 */
		this.set( 'isFocused', false );

		/**
		 * An observable flag set to `true` when the input contains no text.
		 *
		 * @readonly
		 * @observable
		 * @member {Boolean} #isEmpty
		 * @default true
		 */
		this.set( 'isEmpty', true );

		/**
		 * A cached reference to the options passed to the constructor.
		 *
		 * @member {Object}
		 */
		this.options = options;

		/**
		 * Tracks information about the DOM focus in the view.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * A collection of views that can be focused in the view.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * An instance of the dropdown allowing to select a color from a grid.
		 *
		 * @member {module:ui/dropdown/dropdown~DropdownView}
		 */
		this.dropdownView = this._createDropdownView();

		/**
		 * An instance of the input allowing the user to type a color value.
		 *
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.inputView = this._createInputTextView();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * The flag that indicates whether the user is still typing.
		 * If set to true, it means that the text input field ({@link #inputView}) still has the focus.
		 * So, we should interrupt the user by replacing the input's value.
		 *
		 * @protected
		 * @member {Boolean}
		 */
		this._stillTyping = false;

		/**
		 * Helps cycling over focusable items in the view.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate items backwards using the <kbd>Shift</kbd> + <kbd>Tab</kbd> keystroke.
				focusPrevious: 'shift + tab',

				// Navigate items forwards using the <kbd>Tab</kbd> key.
				focusNext: 'tab'
			}
		} );

		this.setTemplate( {
			tag: 'div',
			attributes: {
				class: [
					'ck',
					'ck-input-color'
				]
			},
			children: [
				this.dropdownView,
				this.inputView
			]
		} );

		this.on( 'change:value', ( evt, name, inputValue ) => this._setInputValue( inputValue ) );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		// Start listening for the keystrokes coming from the dropdown panel view.
		this.keystrokes.listenTo( this.dropdownView.panelView.element );
	}

	/**
	 * Focuses the input.
	 */
	focus() {
		this.inputView.focus();
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Creates and configures the {@link #dropdownView}.
	 *
	 * @private
	 */
	_createDropdownView() {
		const locale = this.locale;
		const t = locale.t;
		const bind = this.bindTemplate;
		const colorGrid = this._createColorGrid( locale );
		const dropdown = createDropdown( locale );
		const colorPreview = new src_view_View();
		const removeColorButton = this._createRemoveColorButton();

		colorPreview.setTemplate( {
			tag: 'span',
			attributes: {
				class: [
					'ck',
					'ck-input-color__button__preview'
				],
				style: {
					backgroundColor: bind.to( 'value' )
				}
			},
			children: [ {
				tag: 'span',
				attributes: {
					class: [
						'ck',
						'ck-input-color__button__preview__no-color-indicator',
						bind.if( 'value', 'ck-hidden', value => value != '' )
					]
				}
			} ]
		} );

		dropdown.buttonView.extendTemplate( {
			attributes: {
				class: 'ck-input-color__button'
			}
		} );

		dropdown.buttonView.children.add( colorPreview );
		dropdown.buttonView.label = t( 'Color picker' );
		dropdown.buttonView.tooltip = true;

		dropdown.panelPosition = locale.uiLanguageDirection === 'rtl' ? 'se' : 'sw';
		dropdown.panelView.children.add( removeColorButton );
		dropdown.panelView.children.add( colorGrid );
		dropdown.bind( 'isEnabled' ).to( this, 'isReadOnly', value => !value );

		this._focusables.add( removeColorButton );
		this._focusables.add( colorGrid );

		this.focusTracker.add( removeColorButton.element );
		this.focusTracker.add( colorGrid.element );

		return dropdown;
	}

	/**
	 * Creates and configures an instance of {@link module:ui/inputtext/inputtextview~InputTextView}.
	 *
	 * @private
	 * @returns {module:ui/inputtext/inputtextview~InputTextView} A configured instance to be set as {@link #inputView}.
	 */
	_createInputTextView() {
		const locale = this.locale;
		const inputView = new InputTextView( locale );

		inputView.extendTemplate( {
			on: {
				blur: inputView.bindTemplate.to( 'blur' )
			}
		} );

		inputView.value = this.value;
		inputView.bind( 'isReadOnly', 'hasError' ).to( this );
		this.bind( 'isFocused', 'isEmpty' ).to( inputView );

		inputView.on( 'input', () => {
			const inputValue = inputView.element.value;
			// Check if the value matches one of our defined colors' label.
			const mappedColor = this.options.colorDefinitions.find( def => inputValue === def.label );

			this._stillTyping = true;
			this.value = mappedColor && mappedColor.color || inputValue;
		} );

		inputView.on( 'blur', () => {
			this._stillTyping = false;
			this._setInputValue( inputView.element.value );
		} );

		inputView.delegate( 'input' ).to( this );

		return inputView;
	}

	/**
	 * Creates and configures the button that clears the color.
	 *
	 * @private
	 */
	_createRemoveColorButton() {
		const locale = this.locale;
		const t = locale.t;
		const removeColorButton = new buttonview_ButtonView( locale );
		const defaultColor = this.options.defaultColorValue || '';
		const removeColorButtonLabel = defaultColor ? t( 'Restore default' ) : t( 'Remove color' );

		removeColorButton.class = 'ck-input-color__remove-color';
		removeColorButton.withText = true;
		removeColorButton.icon = icons.eraser;
		removeColorButton.label = removeColorButtonLabel;
		removeColorButton.on( 'execute', () => {
			this.value = defaultColor;
			this.dropdownView.isOpen = false;
			this.fire( 'input' );
		} );

		return removeColorButton;
	}

	/**
	 * Creates and configures the color grid inside the {@link #dropdownView}.
	 *
	 * @private
	 */
	_createColorGrid( locale ) {
		const colorGrid = new ColorGridView( locale, {
			colorDefinitions: this.options.colorDefinitions,
			columns: this.options.columns
		} );

		colorGrid.on( 'execute', ( evtData, data ) => {
			this.value = data.value;
			this.dropdownView.isOpen = false;
			this.fire( 'input' );
		} );
		colorGrid.bind( 'selectedColor' ).to( this, 'value' );

		return colorGrid;
	}

	/**
	 * Sets {@link #inputView}'s value property to the color value or color label,
	 * if there is one and the user is not typing.
	 *
	 * Handles cases like:
	 *
	 * * Someone picks the color in the grid.
	 * * The color is set from the plugin level.
	 *
	 * @private
	 * @param {String} inputValue Color value to be set.
	 */
	_setInputValue( inputValue ) {
		if ( !this._stillTyping ) {
			const normalizedInputValue = normalizeColor( inputValue );
			// Check if the value matches one of our defined colors.
			const mappedColor = this.options.colorDefinitions.find( def => normalizedInputValue === normalizeColor( def.color ) );

			if ( mappedColor ) {
				this.inputView.value = mappedColor.label;
			} else {
				this.inputView.value = inputValue || '';
			}
		}
	}
}

// Normalizes color value, by stripping extensive whitespace.
// For example., transforms:
// * `   rgb(  25 50    0 )` to `rgb(25 50 0)`,
// * "\t  rgb(  25 ,  50,0 )		" to `rgb(25 50 0)`.
//
// @param {String} colorString The value to be normalized.
// @returns {String}
function normalizeColor( colorString ) {
	return colorString
		// Remove any whitespace right after `(` or `,`.
		.replace( /([(,])\s+/g, '$1' )
		// Remove any whitespace at the beginning or right before the end, `)`, `,`, or another whitespace.
		.replace( /^\s+|\s+(?=[),\s]|$)/g, '' )
		// Then, replace `,` or whitespace with a single space.
		.replace( /,|\s/g, ' ' );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/utils/ui/table-properties.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/utils/ui/table-properties
 */







const table_properties_isEmpty = val => val === '';

/**
 * Returns an object containing pairs of CSS border style values and their localized UI
 * labels. Used by {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView}
 * and {@link module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView}.
 *
 * @param {module:utils/locale~Locale#t} t The "t" function provided by the editor
 * that is used to localize strings.
 * @returns {Object.<String,String>}
 */
function getBorderStyleLabels( t ) {
	return {
		none: t( 'None' ),
		solid: t( 'Solid' ),
		dotted: t( 'Dotted' ),
		dashed: t( 'Dashed' ),
		double: t( 'Double' ),
		groove: t( 'Groove' ),
		ridge: t( 'Ridge' ),
		inset: t( 'Inset' ),
		outset: t( 'Outset' )
	};
}

/**
 * Returns a localized error string that can be displayed next to color (background, border)
 * fields that have an invalid value.
 *
 * @param {module:utils/locale~Locale#t} t The "t" function provided by the editor
 * that is used to localize strings.
 * @returns {String}
 */
function getLocalizedColorErrorText( t ) {
	return t( 'The color is invalid. Try "#FF0000" or "rgb(255,0,0)" or "red".' );
}

/**
 * Returns a localized error string that can be displayed next to length (padding, border width)
 * fields that have an invalid value.
 *
 * @param {module:utils/locale~Locale#t} t The "t" function provided by the editor
 * that is used to localize strings.
 * @returns {String}
 */
function getLocalizedLengthErrorText( t ) {
	return t( 'The value is invalid. Try "10px" or "2em" or simply "2".' );
}

/**
 * Returns `true` when the passed value is an empty string or a valid CSS color expression.
 * Otherwise, `false` is returned.
 *
 * See {@link module:engine/view/styles/utils~isColor}.
 *
 * @param {String} value
 * @returns {Boolean}
 */
function colorFieldValidator( value ) {
	value = value.trim();

	return table_properties_isEmpty( value ) || isColor( value );
}

/**
 * Returns `true` when the passed value is an empty string, a number without a unit or a valid CSS length expression.
 * Otherwise, `false` is returned.
 *
 * See {@link module:engine/view/styles/utils~isLength}.
 * See {@link module:engine/view/styles/utils~isPercentage}.
 *
 * @param {String} value
 * @returns {Boolean}
 */
function lengthFieldValidator( value ) {
	value = value.trim();

	return table_properties_isEmpty( value ) || isNumberString( value ) || utils_isLength( value ) || isPercentage( value );
}

/**
 * Returns `true` when the passed value is an empty string, a number without a unit or a valid CSS length expression.
 * Otherwise, `false` is returned.
 *
 * See {@link module:engine/view/styles/utils~isLength}.
 *
 * @param {String} value
 * @returns {Boolean}
 */
function lineWidthFieldValidator( value ) {
	value = value.trim();

	return table_properties_isEmpty( value ) || isNumberString( value ) || utils_isLength( value );
}

/**
 * Generates item definitions for a UI dropdown that allows changing the border style of a table or a table cell.
 *
 * @param {module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView|
 * module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView} view
 * @param {String} defaultStyle The default border.
 * @returns {Iterable.<module:ui/dropdown/utils~ListDropdownItemDefinition>}
 */
function getBorderStyleDefinitions( view, defaultStyle ) {
	const itemDefinitions = new Collection();
	const styleLabels = getBorderStyleLabels( view.t );

	for ( const style in styleLabels ) {
		const definition = {
			type: 'button',
			model: new model_Model( {
				_borderStyleValue: style,
				label: styleLabels[ style ],
				withText: true
			} )
		};

		if ( style === 'none' ) {
			definition.model.bind( 'isOn' ).to( view, 'borderStyle', value => {
				if ( defaultStyle === 'none' ) {
					return !value;
				}

				return value === style;
			} );
		} else {
			definition.model.bind( 'isOn' ).to( view, 'borderStyle', value => {
				return value === style;
			} );
		}

		itemDefinitions.add( definition );
	}

	return itemDefinitions;
}

/**
 * A helper that fills a toolbar with buttons that:
 *
 * * have some labels,
 * * have some icons,
 * * set a certain UI view property value upon execution.
 *
 * @param {Object} options
 * @param {module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView|
 * module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView} options.view
 * @param {Array.<String>} options.icons
 * @param {module:ui/toolbar/toolbarview~ToolbarView} options.toolbar
 * @param {Object.<String,String>} labels
 * @param {String} propertyName
 * @param {Function} nameToValue A function that maps a button name to a value. By default names are the same as values.
 */
function fillToolbar( options ) {
	const { view, icons, toolbar, labels, propertyName, nameToValue, defaultValue } = options;
	for ( const name in labels ) {
		const button = new buttonview_ButtonView( view.locale );

		button.set( {
			label: labels[ name ],
			icon: icons[ name ],
			tooltip: labels[ name ]
		} );

		// If specified the `nameToValue()` callback, map the value based on the option's name.
		const buttonValue = nameToValue ? nameToValue( name ) : name;

		button.bind( 'isOn' ).to( view, propertyName, value => {
			// `value` comes from `view[ propertyName ]`.
			let valueToCompare = value;

			// If it's empty, and the `defaultValue` is specified, use it instead.
			if ( value === '' && defaultValue ) {
				valueToCompare = defaultValue;
			}

			return buttonValue === valueToCompare;
		} );

		button.on( 'execute', () => {
			view[ propertyName ] = buttonValue;
		} );

		toolbar.items.add( button );
	}
}

/**
 * A default color palette used by various user interfaces related to tables, for instance,
 * by {@link module:table/tablecellproperties/tablecellpropertiesui~TableCellPropertiesUI} or
 * {@link module:table/tableproperties/tablepropertiesui~TablePropertiesUI}.
 *
 * The color palette follows the {@link module:table/table~TableColorConfig table color configuration format}
 * and contains the following color definitions:
 *
 *		const defaultColors = [
 *			{
 *				color: 'hsl(0, 0%, 0%)',
 *				label: 'Black'
 *			},
 *			{
 *				color: 'hsl(0, 0%, 30%)',
 *				label: 'Dim grey'
 *			},
 *			{
 *				color: 'hsl(0, 0%, 60%)',
 *				label: 'Grey'
 *			},
 *			{
 *				color: 'hsl(0, 0%, 90%)',
 *				label: 'Light grey'
 *			},
 *			{
 *				color: 'hsl(0, 0%, 100%)',
 *				label: 'White',
 *				hasBorder: true
 *			},
 *			{
 *				color: 'hsl(0, 75%, 60%)',
 *				label: 'Red'
 *			},
 *			{
 *				color: 'hsl(30, 75%, 60%)',
 *				label: 'Orange'
 *			},
 *			{
 *				color: 'hsl(60, 75%, 60%)',
 *				label: 'Yellow'
 *			},
 *			{
 *				color: 'hsl(90, 75%, 60%)',
 *				label: 'Light green'
 *			},
 *			{
 *				color: 'hsl(120, 75%, 60%)',
 *				label: 'Green'
 *			},
 *			{
 *				color: 'hsl(150, 75%, 60%)',
 *				label: 'Aquamarine'
 *			},
 *			{
 *				color: 'hsl(180, 75%, 60%)',
 *				label: 'Turquoise'
 *			},
 *			{
 *				color: 'hsl(210, 75%, 60%)',
 *				label: 'Light blue'
 *			},
 *			{
 *				color: 'hsl(240, 75%, 60%)',
 *				label: 'Blue'
 *			},
 *			{
 *				color: 'hsl(270, 75%, 60%)',
 *				label: 'Purple'
 *			}
 *		];
 */
const defaultColors = [
	{
		color: 'hsl(0, 0%, 0%)',
		label: 'Black'
	},
	{
		color: 'hsl(0, 0%, 30%)',
		label: 'Dim grey'
	},
	{
		color: 'hsl(0, 0%, 60%)',
		label: 'Grey'
	},
	{
		color: 'hsl(0, 0%, 90%)',
		label: 'Light grey'
	},
	{
		color: 'hsl(0, 0%, 100%)',
		label: 'White',
		hasBorder: true
	},
	{
		color: 'hsl(0, 75%, 60%)',
		label: 'Red'
	},
	{
		color: 'hsl(30, 75%, 60%)',
		label: 'Orange'
	},
	{
		color: 'hsl(60, 75%, 60%)',
		label: 'Yellow'
	},
	{
		color: 'hsl(90, 75%, 60%)',
		label: 'Light green'
	},
	{
		color: 'hsl(120, 75%, 60%)',
		label: 'Green'
	},
	{
		color: 'hsl(150, 75%, 60%)',
		label: 'Aquamarine'
	},
	{
		color: 'hsl(180, 75%, 60%)',
		label: 'Turquoise'
	},
	{
		color: 'hsl(210, 75%, 60%)',
		label: 'Light blue'
	},
	{
		color: 'hsl(240, 75%, 60%)',
		label: 'Blue'
	},
	{
		color: 'hsl(270, 75%, 60%)',
		label: 'Purple'
	}
];

/**
 * Returns a creator for a color input with a label.
 *
 * For given options, it returns a function that creates an instance of a
 * {@link module:table/ui/colorinputview~ColorInputView color input} logically related to
 * a {@link module:ui/labeledfield/labeledfieldview~LabeledFieldView labeled view} in the DOM.
 *
 * The helper does the following:
 *
 * * It sets the color input `id` and `ariaDescribedById` attributes.
 * * It binds the color input `isReadOnly` to the labeled view.
 * * It binds the color input `hasError` to the labeled view.
 * * It enables a logic that cleans up the error when the user starts typing in the color input.
 *
 * Usage:
 *
 *		const colorInputCreator = getLabeledColorInputCreator( {
 *			colorConfig: [ ... ],
 *			columns: 3,
 *		} );
 *
 *		const labeledInputView = new LabeledFieldView( locale, colorInputCreator );
 *		console.log( labeledInputView.view ); // A color input instance.
 *
 * @private
 * @param options Color input options.
 * @param {module:table/table~TableColorConfig} options.colorConfig The configuration of the color palette
 * displayed in the input's dropdown.
 * @param {Number} options.columns The configuration of the number of columns the color palette consists of
 * in the input's dropdown.
 * @param {String} [options.defaultColorValue] If specified, the color input view will replace the "Remove color" button with
 * the "Restore default" button. Instead of clearing the input field, the default color value will be set.
 * @returns {Function}
 */
function getLabeledColorInputCreator( options ) {
	return ( labeledFieldView, viewUid, statusUid ) => {
		const colorInputView = new ColorInputView( labeledFieldView.locale, {
			colorDefinitions: colorConfigToColorGridDefinitions( options.colorConfig ),
			columns: options.columns,
			defaultColorValue: options.defaultColorValue
		} );

		colorInputView.inputView.set( {
			id: viewUid,
			ariaDescribedById: statusUid
		} );

		colorInputView.bind( 'isReadOnly' ).to( labeledFieldView, 'isEnabled', value => !value );
		colorInputView.bind( 'hasError' ).to( labeledFieldView, 'errorText', value => !!value );

		colorInputView.on( 'input', () => {
			// UX: Make the error text disappear and disable the error indicator as the user
			// starts fixing the errors.
			labeledFieldView.errorText = null;
		} );

		labeledFieldView.bind( 'isEmpty', 'isFocused' ).to( colorInputView );

		return colorInputView;
	};
}

// A simple helper method to detect number strings.
// I allows full number notation, so omitting 0 is not allowed:
function isNumberString( value ) {
	const parsedValue = parseFloat( value );

	return !Number.isNaN( parsedValue ) && value === String( parsedValue );
}

// @param {Array.<Object>} colorConfig
// @returns {Array.<module:ui/colorgrid/colorgrid~ColorDefinition>}
function colorConfigToColorGridDefinitions( colorConfig ) {
	return colorConfig.map( item => ( {
		color: item.model,
		label: item.label,
		options: {
			hasBorder: item.hasBorder
		}
	} ) );
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/formrow.css
var formrow = __webpack_require__(9865);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/formrow.css

            

var formrow_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

formrow_options.insert = "head";
formrow_options.singleton = true;

var formrow_update = injectStylesIntoStyleTag_default()(formrow/* default */.Z, formrow_options);



/* harmony default export */ const theme_formrow = (formrow/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/ui/formrowview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/ui/formrowview
 */





/**
 * The class representing a single row in a complex form,
 * used by {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView}.
 *
 * **Note**: For now this class is private. When more use cases arrive (beyond ckeditor5-table),
 * it will become a component in ckeditor5-ui.
 *
 * @private
 * @extends module:ui/view~View
 */
class FormRowView extends src_view_View {
	/**
	 * Creates an instance of the form row class.
	 *
	 * @param {module:utils/locale~Locale} locale The locale instance.
	 * @param {Object} options
	 * @param {Array.<module:ui/view~View>} options.children
	 * @param {String} [options.class]
	 * @param {module:ui/view~View} [options.labelView] When passed, the row gets the `group` and `aria-labelledby`
	 * DOM attributes and gets described by the label.
	 */
	constructor( locale, options = {} ) {
		super( locale );

		const bind = this.bindTemplate;

		/**
		 * An additional CSS class added to the {@link #element}.
		 *
		 * @observable
		 * @member {String} #class
		 */
		this.set( 'class', options.class || null );

		/**
		 * A collection of row items (buttons, dropdowns, etc.).
		 *
		 * @readonly
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this.children = this.createCollection();

		if ( options.children ) {
			options.children.forEach( child => this.children.add( child ) );
		}

		/**
		 * The role property reflected by the `role` DOM attribute of the {@link #element}.
		 *
		 * **Note**: Used only when a `labelView` is passed to constructor `options`.
		 *
		 * @private
		 * @observable
		 * @member {String} #role
		 */
		this.set( '_role', null );

		/**
		 * The ARIA property reflected by the `aria-labelledby` DOM attribute of the {@link #element}.
		 *
		 * **Note**: Used only when a `labelView` is passed to constructor `options`.
		 *
		 * @private
		 * @observable
		 * @member {String} #ariaLabelledBy
		 */
		this.set( '_ariaLabelledBy', null );

		if ( options.labelView ) {
			this.set( {
				_role: 'group',
				_ariaLabelledBy: options.labelView.id
			} );
		}

		this.setTemplate( {
			tag: 'div',
			attributes: {
				class: [
					'ck',
					'ck-form__row',
					bind.to( 'class' )
				],
				role: bind.to( '_role' ),
				'aria-labelledby': bind.to( '_ariaLabelledBy' )
			},
			children: this.children
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/form.css
var theme_form = __webpack_require__(4880);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/form.css

            

var form_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

form_options.insert = "head";
form_options.singleton = true;

var form_update = injectStylesIntoStyleTag_default()(theme_form/* default */.Z, form_options);



/* harmony default export */ const ckeditor5_table_theme_form = (theme_form/* default.locals */.Z.locals || {});
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/tableform.css
var tableform = __webpack_require__(198);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/tableform.css

            

var tableform_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tableform_options.insert = "head";
tableform_options.singleton = true;

var tableform_update = injectStylesIntoStyleTag_default()(tableform/* default */.Z, tableform_options);



/* harmony default export */ const theme_tableform = (tableform/* default.locals */.Z.locals || {});
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/tablecellproperties.css
var tablecellproperties = __webpack_require__(5737);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/tablecellproperties.css

            

var tablecellproperties_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tablecellproperties_options.insert = "head";
tablecellproperties_options.singleton = true;

var tablecellproperties_update = injectStylesIntoStyleTag_default()(tablecellproperties/* default */.Z, tablecellproperties_options);



/* harmony default export */ const theme_tablecellproperties = (tablecellproperties/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/ui/tablecellpropertiesview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/ui/tablecellpropertiesview
 */












const ALIGNMENT_ICONS = {
	left: icons.alignLeft,
	center: icons.alignCenter,
	right: icons.alignRight,
	justify: icons.alignJustify,
	top: icons.alignTop,
	middle: icons.alignMiddle,
	bottom: icons.alignBottom
};

/**
 * The class representing a table cell properties form, allowing users to customize
 * certain style aspects of a table cell, for instance, border, padding, text alignment, etc..
 *
 * @extends module:ui/view~View
 */
class TableCellPropertiesView extends src_view_View {
	/**
	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
	 * @param {Object} options Additional configuration of the view.
	 * @param {module:table/table~TableColorConfig} options.borderColors A configuration of the border
	 * color palette used by the
	 * {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView#borderColorInput}.
	 * @param {module:table/table~TableColorConfig} options.backgroundColors A configuration of the background
	 * color palette used by the
	 * {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView#backgroundInput}.
	 * @param {module:table/tablecellproperties~TableCellPropertiesOptions} options.defaultTableCellProperties The default
	 * table cell properties.
	 */
	constructor( locale, options ) {
		super( locale );

		this.set( {
			/**
			 * The value of the cell border style.
			 *
			 * @observable
			 * @default ''
			 * @member #borderStyle
			 */
			borderStyle: '',

			/**
			 * The value of the cell border width style.
			 *
			 * @observable
			 * @default ''
			 * @member #borderWidth
			 */
			borderWidth: '',

			/**
			 * The value of the cell border color style.
			 *
			 * @observable
			 * @default ''
			 * @member #borderColor
			 */
			borderColor: '',

			/**
			 * The value of the cell padding style.
			 *
			 * @observable
			 * @default ''
			 * @member #padding
			 */
			padding: '',

			/**
			 * The value of the cell background color style.
			 *
			 * @observable
			 * @default ''
			 * @member #backgroundColor
			 */
			backgroundColor: '',

			/**
			 * The value of the table cell width style.
			 *
			 * @observable
			 * @default ''
			 * @member #width
			 */
			width: '',

			/**
			 * The value of the table cell height style.
			 *
			 * @observable
			 * @default ''
			 * @member #height
			 */
			height: '',

			/**
			 * The value of the horizontal text alignment style.
			 *
			 * @observable
			 * @default ''
			 * @member #horizontalAlignment
			 */
			horizontalAlignment: '',

			/**
			 * The value of the vertical text alignment style.
			 *
			 * @observable
			 * @default ''
			 * @member #verticalAlignment
			 */
			verticalAlignment: ''
		} );

		/**
		 * Options passed to the view. See {@link #constructor} to learn more.
		 *
		 * @member {Object}
		 */
		this.options = options;

		const { borderStyleDropdown, borderWidthInput, borderColorInput, borderRowLabel } = this._createBorderFields();
		const { backgroundRowLabel, backgroundInput } = this._createBackgroundFields();
		const { widthInput, operatorLabel, heightInput, dimensionsLabel } = this._createDimensionFields();
		const { horizontalAlignmentToolbar, verticalAlignmentToolbar, alignmentLabel } = this._createAlignmentFields();

		/**
		 * Tracks information about the DOM focus in the form.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * A collection of child views in the form.
		 *
		 * @readonly
		 * @type {module:ui/viewcollection~ViewCollection}
		 */
		this.children = this.createCollection();

		/**
		 * A dropdown that allows selecting the style of the table cell border.
		 *
		 * @readonly
		 * @member {module:ui/dropdown/dropdownview~DropdownView}
		 */
		this.borderStyleDropdown = borderStyleDropdown;

		/**
		 * An input that allows specifying the width of the table cell border.
		 *
		 * @readonly
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.borderWidthInput = borderWidthInput;

		/**
		 * An input that allows specifying the color of the table cell border.
		 *
		 * @readonly
		 * @member {module:table/ui/colorinputview~ColorInputView}
		 */
		this.borderColorInput = borderColorInput;

		/**
		 * An input that allows specifying the table cell background color.
		 *
		 * @readonly
		 * @member {module:table/ui/colorinputview~ColorInputView}
		 */
		this.backgroundInput = backgroundInput;

		/**
		 * An input that allows specifying the table cell padding.
		 *
		 * @readonly
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.paddingInput = this._createPaddingField();

		/**
		 * An input that allows specifying the table cell width.
		 *
		 * @readonly
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.widthInput = widthInput;

		/**
		 * An input that allows specifying the table cell height.
		 *
		 * @readonly
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.heightInput = heightInput;

		/**
		 * A toolbar with buttons that allow changing the horizontal text alignment in a table cell.
		 *
		 * @readonly
		 * @member {module:ui/toolbar/toolbarview~ToolbarView}
		 */
		this.horizontalAlignmentToolbar = horizontalAlignmentToolbar;

		/**
		 * A toolbar with buttons that allow changing the vertical text alignment in a table cell.
		 *
		 * @readonly
		 * @member {module:ui/toolbar/toolbarview~ToolbarView}
		 */
		this.verticalAlignmentToolbar = verticalAlignmentToolbar;

		// Defer creating to make sure other fields are present and the Save button can
		// bind its #isEnabled to their error messages so there's no way to save unless all
		// fields are valid.
		const { saveButtonView, cancelButtonView } = this._createActionButtons();

		/**
		 * The "Save" button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.saveButtonView = saveButtonView;

		/**
		 * The "Cancel" button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.cancelButtonView = cancelButtonView;

		/**
		 * A collection of views that can be focused in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate form fields backwards using the Shift + Tab keystroke.
				focusPrevious: 'shift + tab',

				// Navigate form fields forwards using the Tab key.
				focusNext: 'tab'
			}
		} );

		// Form header.
		this.children.add( new FormHeaderView( locale, {
			label: this.t( 'Cell properties' )
		} ) );

		// Border row.
		this.children.add( new FormRowView( locale, {
			labelView: borderRowLabel,
			children: [
				borderRowLabel,
				borderStyleDropdown,
				borderColorInput,
				borderWidthInput
			],
			class: 'ck-table-form__border-row'
		} ) );

		// Background.
		this.children.add( new FormRowView( locale, {
			labelView: backgroundRowLabel,
			children: [
				backgroundRowLabel,
				backgroundInput
			],
			class: 'ck-table-form__background-row'
		} ) );

		// Dimensions row and padding.
		this.children.add( new FormRowView( locale, {
			children: [
				// Dimensions row.
				new FormRowView( locale, {
					labelView: dimensionsLabel,
					children: [
						dimensionsLabel,
						widthInput,
						operatorLabel,
						heightInput
					],
					class: 'ck-table-form__dimensions-row'
				} ),
				// Padding row.
				new FormRowView( locale, {
					children: [
						this.paddingInput
					],
					class: 'ck-table-cell-properties-form__padding-row'
				} )
			]
		} ) );

		// Text alignment row.
		this.children.add( new FormRowView( locale, {
			labelView: alignmentLabel,
			children: [
				alignmentLabel,
				horizontalAlignmentToolbar,
				verticalAlignmentToolbar
			],
			class: 'ck-table-cell-properties-form__alignment-row'
		} ) );

		// Action row.
		this.children.add( new FormRowView( locale, {
			children: [
				this.saveButtonView,
				this.cancelButtonView
			],
			class: 'ck-table-form__action-row'
		} ) );

		this.setTemplate( {
			tag: 'form',
			attributes: {
				class: [
					'ck',
					'ck-form',
					'ck-table-form',
					'ck-table-cell-properties-form'
				],
				// https://github.com/ckeditor/ckeditor5-link/issues/90
				tabindex: '-1'
			},
			children: this.children
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		// Enable the "submit" event for this view. It can be triggered by the #saveButtonView
		// which is of the "submit" DOM "type".
		submitHandler( {
			view: this
		} );

		[
			this.borderStyleDropdown,
			this.borderColorInput,
			this.borderColorInput.fieldView.dropdownView.buttonView,
			this.borderWidthInput,
			this.backgroundInput,
			this.backgroundInput.fieldView.dropdownView.buttonView,
			this.widthInput,
			this.heightInput,
			this.paddingInput,
			this.horizontalAlignmentToolbar,
			this.verticalAlignmentToolbar,
			this.saveButtonView,
			this.cancelButtonView
		].forEach( view => {
			// Register the view as focusable.
			this._focusables.add( view );

			// Register the view in the focus tracker.
			this.focusTracker.add( view.element );
		} );

		// Mainly for closing using "Esc" and navigation using "Tab".
		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Focuses the fist focusable field in the form.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #borderStyleDropdown},
	 * * {@link #borderWidthInput},
	 * * {@link #borderColorInput}.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createBorderFields() {
		const defaultTableCellProperties = this.options.defaultTableCellProperties;
		const defaultBorder = {
			style: defaultTableCellProperties.borderStyle,
			width: defaultTableCellProperties.borderWidth,
			color: defaultTableCellProperties.borderColor
		};

		const colorInputCreator = getLabeledColorInputCreator( {
			colorConfig: this.options.borderColors,
			columns: 5,
			defaultColorValue: defaultBorder.color
		} );
		const locale = this.locale;
		const t = this.t;

		// -- Group label ---------------------------------------------

		const borderRowLabel = new LabelView( locale );
		borderRowLabel.text = t( 'Border' );

		// -- Style ---------------------------------------------------

		const styleLabels = getBorderStyleLabels( t );
		const borderStyleDropdown = new LabeledFieldView( locale, createLabeledDropdown );
		borderStyleDropdown.set( {
			label: t( 'Style' ),
			class: 'ck-table-form__border-style'
		} );

		borderStyleDropdown.fieldView.buttonView.set( {
			isOn: false,
			withText: true,
			tooltip: t( 'Style' )
		} );

		borderStyleDropdown.fieldView.buttonView.bind( 'label' ).to( this, 'borderStyle', value => {
			return styleLabels[ value ? value : 'none' ];
		} );

		borderStyleDropdown.fieldView.on( 'execute', evt => {
			this.borderStyle = evt.source._borderStyleValue;
		} );

		borderStyleDropdown.bind( 'isEmpty' ).to( this, 'borderStyle', value => !value );

		addListToDropdown( borderStyleDropdown.fieldView, getBorderStyleDefinitions( this, defaultBorder.style ) );

		// -- Width ---------------------------------------------------

		const borderWidthInput = new LabeledFieldView( locale, createLabeledInputText );

		borderWidthInput.set( {
			label: t( 'Width' ),
			class: 'ck-table-form__border-width'
		} );

		borderWidthInput.fieldView.bind( 'value' ).to( this, 'borderWidth' );
		borderWidthInput.bind( 'isEnabled' ).to( this, 'borderStyle', isBorderStyleSet );
		borderWidthInput.fieldView.on( 'input', () => {
			this.borderWidth = borderWidthInput.fieldView.element.value;
		} );

		// -- Color ---------------------------------------------------

		const borderColorInput = new LabeledFieldView( locale, colorInputCreator );

		borderColorInput.set( {
			label: t( 'Color' ),
			class: 'ck-table-form__border-color'
		} );

		borderColorInput.fieldView.bind( 'value' ).to( this, 'borderColor' );
		borderColorInput.bind( 'isEnabled' ).to( this, 'borderStyle', isBorderStyleSet );

		borderColorInput.fieldView.on( 'input', () => {
			this.borderColor = borderColorInput.fieldView.value;
		} );

		// Reset the border color and width fields depending on the `border-style` value.
		this.on( 'change:borderStyle', ( evt, name, newValue, oldValue ) => {
			// When removing the border (`border-style:none`), clear the remaining `border-*` properties.
			// See: https://github.com/ckeditor/ckeditor5/issues/6227.
			if ( !isBorderStyleSet( newValue ) ) {
				this.borderColor = '';
				this.borderWidth = '';
			}

			// When setting the `border-style` from `none`, set the default `border-color` and `border-width` properties.
			if ( !isBorderStyleSet( oldValue ) ) {
				this.borderColor = defaultBorder.color;
				this.borderWidth = defaultBorder.width;
			}
		} );

		return {
			borderRowLabel,
			borderStyleDropdown,
			borderColorInput,
			borderWidthInput
		};
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #backgroundInput}.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createBackgroundFields() {
		const locale = this.locale;
		const t = this.t;

		// -- Group label ---------------------------------------------

		const backgroundRowLabel = new LabelView( locale );
		backgroundRowLabel.text = t( 'Background' );

		// -- Background color input -----------------------------------

		const colorInputCreator = getLabeledColorInputCreator( {
			colorConfig: this.options.backgroundColors,
			columns: 5,
			defaultColorValue: this.options.defaultTableCellProperties.backgroundColor
		} );

		const backgroundInput = new LabeledFieldView( locale, colorInputCreator );

		backgroundInput.set( {
			label: t( 'Color' ),
			class: 'ck-table-cell-properties-form__background'
		} );

		backgroundInput.fieldView.bind( 'value' ).to( this, 'backgroundColor' );
		backgroundInput.fieldView.on( 'input', () => {
			this.backgroundColor = backgroundInput.fieldView.value;
		} );

		return {
			backgroundRowLabel,
			backgroundInput
		};
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #widthInput}.
	 * * {@link #heightInput}.
	 *
	 * @private
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
	 */
	_createDimensionFields() {
		const locale = this.locale;
		const t = this.t;

		// -- Label ---------------------------------------------------

		const dimensionsLabel = new LabelView( locale );
		dimensionsLabel.text = t( 'Dimensions' );

		// -- Width ---------------------------------------------------

		const widthInput = new LabeledFieldView( locale, createLabeledInputText );

		widthInput.set( {
			label: t( 'Width' ),
			class: 'ck-table-form__dimensions-row__width'
		} );

		widthInput.fieldView.bind( 'value' ).to( this, 'width' );
		widthInput.fieldView.on( 'input', () => {
			this.width = widthInput.fieldView.element.value;
		} );

		// -- Operator ---------------------------------------------------

		const operatorLabel = new src_view_View( locale );
		operatorLabel.setTemplate( {
			tag: 'span',
			attributes: {
				class: [
					'ck-table-form__dimension-operator'
				]
			},
			children: [
				{ text: '×' }
			]
		} );

		// -- Height ---------------------------------------------------

		const heightInput = new LabeledFieldView( locale, createLabeledInputText );

		heightInput.set( {
			label: t( 'Height' ),
			class: 'ck-table-form__dimensions-row__height'
		} );

		heightInput.fieldView.bind( 'value' ).to( this, 'height' );
		heightInput.fieldView.on( 'input', () => {
			this.height = heightInput.fieldView.element.value;
		} );

		return {
			dimensionsLabel,
			widthInput,
			operatorLabel,
			heightInput
		};
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #paddingInput}.
	 *
	 * @private
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
	 */
	_createPaddingField() {
		const locale = this.locale;
		const t = this.t;

		const paddingInput = new LabeledFieldView( locale, createLabeledInputText );

		paddingInput.set( {
			label: t( 'Padding' ),
			class: 'ck-table-cell-properties-form__padding'
		} );

		paddingInput.fieldView.bind( 'value' ).to( this, 'padding' );
		paddingInput.fieldView.on( 'input', () => {
			this.padding = paddingInput.fieldView.element.value;
		} );

		return paddingInput;
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #horizontalAlignmentToolbar},
	 * * {@link #verticalAlignmentToolbar}.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createAlignmentFields() {
		const locale = this.locale;
		const t = this.t;

		const alignmentLabel = new LabelView( locale );

		alignmentLabel.text = t( 'Table cell text alignment' );

		// -- Horizontal ---------------------------------------------------

		const horizontalAlignmentToolbar = new toolbarview_ToolbarView( locale );
		const isContentRTL = this.locale.contentLanguageDirection === 'rtl';

		horizontalAlignmentToolbar.set( {
			isCompact: true,
			ariaLabel: t( 'Horizontal text alignment toolbar' )
		} );

		fillToolbar( {
			view: this,
			icons: ALIGNMENT_ICONS,
			toolbar: horizontalAlignmentToolbar,
			labels: this._horizontalAlignmentLabels,
			propertyName: 'horizontalAlignment',
			nameToValue: name => {
				// For the RTL content, we want to swap the buttons "align to the left" and "align to the right".
				if ( isContentRTL ) {
					if ( name === 'left' ) {
						return 'right';
					} else if ( name === 'right' ) {
						return 'left';
					}
				}

				return name;
			},
			defaultValue: this.options.defaultTableCellProperties.horizontalAlignment
		} );

		// -- Vertical -----------------------------------------------------

		const verticalAlignmentToolbar = new toolbarview_ToolbarView( locale );

		verticalAlignmentToolbar.set( {
			isCompact: true,
			ariaLabel: t( 'Vertical text alignment toolbar' )
		} );

		fillToolbar( {
			view: this,
			icons: ALIGNMENT_ICONS,
			toolbar: verticalAlignmentToolbar,
			labels: this._verticalAlignmentLabels,
			propertyName: 'verticalAlignment',
			defaultValue: this.options.defaultTableCellProperties.verticalAlignment
		} );

		return {
			horizontalAlignmentToolbar,
			verticalAlignmentToolbar,
			alignmentLabel
		};
	}

	/**
	 * Creates the following form controls:
	 *
	 * * {@link #saveButtonView},
	 * * {@link #cancelButtonView}.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createActionButtons() {
		const locale = this.locale;
		const t = this.t;
		const saveButtonView = new buttonview_ButtonView( locale );
		const cancelButtonView = new buttonview_ButtonView( locale );
		const fieldsThatShouldValidateToSave = [
			this.borderWidthInput,
			this.borderColorInput,
			this.backgroundInput,
			this.paddingInput
		];

		saveButtonView.set( {
			label: t( 'Save' ),
			icon: icons.check,
			class: 'ck-button-save',
			type: 'submit',
			withText: true
		} );

		saveButtonView.bind( 'isEnabled' ).toMany( fieldsThatShouldValidateToSave, 'errorText', ( ...errorTexts ) => {
			return errorTexts.every( errorText => !errorText );
		} );

		cancelButtonView.set( {
			label: t( 'Cancel' ),
			icon: icons.cancel,
			class: 'ck-button-cancel',
			withText: true
		} );

		cancelButtonView.delegate( 'execute' ).to( this, 'cancel' );

		return {
			saveButtonView, cancelButtonView
		};
	}

	/**
	 * Provides localized labels for {@link #horizontalAlignmentToolbar} buttons.
	 *
	 * @private
	 * @type {Object.<String,String>}
	 */
	get _horizontalAlignmentLabels() {
		const locale = this.locale;
		const t = this.t;

		const left = t( 'Align cell text to the left' );
		const center = t( 'Align cell text to the center' );
		const right = t( 'Align cell text to the right' );
		const justify = t( 'Justify cell text' );

		// Returns object with a proper order of labels.
		if ( locale.uiLanguageDirection === 'rtl' ) {
			return { right, center, left, justify };
		} else {
			return { left, center, right, justify };
		}
	}

	/**
	 * Provides localized labels for {@link #verticalAlignmentToolbar} buttons.
	 *
	 * @private
	 * @type {Object.<String,String>}
	 */
	get _verticalAlignmentLabels() {
		const t = this.t;

		return {
			top: t( 'Align cell text to the top' ),
			middle: t( 'Align cell text to the middle' ),
			bottom: t( 'Align cell text to the bottom' )
		};
	}
}

function isBorderStyleSet( value ) {
	return value !== 'none';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/utils/ui/widget.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/utils/ui/widget
 */



/**
 * Returns a table widget editing view element if one is selected.
 *
 * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
 * @returns {module:engine/view/element~Element|null}
 */
function getSelectedTableWidget( selection ) {
	const viewElement = selection.getSelectedElement();

	if ( viewElement && isTableWidget( viewElement ) ) {
		return viewElement;
	}

	return null;
}

/**
 * Returns a table widget editing view element if one is among the selection's ancestors.
 *
 * @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
 * @returns {module:engine/view/element~Element|null}
 */
function getTableWidgetAncestor( selection ) {
	const selectionPosition = selection.getFirstPosition();

	if ( !selectionPosition ) {
		return null;
	}

	let parent = selectionPosition.parent;

	while ( parent ) {
		if ( parent.is( 'element' ) && isTableWidget( parent ) ) {
			return parent;
		}

		parent = parent.parent;
	}

	return null;
}

// Checks if a given view element is a table widget.
//
// @param {module:engine/view/element~Element} viewElement
// @returns {Boolean}
function isTableWidget( viewElement ) {
	return !!viewElement.getCustomProperty( 'table' ) && isWidget( viewElement );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/utils/ui/contextualballoon.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/utils/ui/contextualballoon
 */






const DEFAULT_BALLOON_POSITIONS = balloonpanelview_BalloonPanelView.defaultPositions;

const BALLOON_POSITIONS = [
	DEFAULT_BALLOON_POSITIONS.northArrowSouth,
	DEFAULT_BALLOON_POSITIONS.northArrowSouthWest,
	DEFAULT_BALLOON_POSITIONS.northArrowSouthEast,
	DEFAULT_BALLOON_POSITIONS.southArrowNorth,
	DEFAULT_BALLOON_POSITIONS.southArrowNorthWest,
	DEFAULT_BALLOON_POSITIONS.southArrowNorthEast,
	DEFAULT_BALLOON_POSITIONS.viewportStickyNorth
];

/**
 * A helper utility that positions the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} instance
 * with respect to the table in the editor content, if one is selected.
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @param {String} target Either "cell" or "table". Determines the target the balloon will
 * be attached to.
 */
function contextualballoon_repositionContextualBalloon( editor, target ) {
	const balloon = editor.plugins.get( 'ContextualBalloon' );

	if ( getTableWidgetAncestor( editor.editing.view.document.selection ) ) {
		let position;

		if ( target === 'cell' ) {
			position = getBalloonCellPositionData( editor );
		} else {
			position = getBalloonTablePositionData( editor );
		}

		balloon.updatePosition( position );
	}
}

/**
 * Returns the positioning options that control the geometry of the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} with respect
 * to the selected table in the editor content.
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @returns {module:utils/dom/position~Options}
 */
function getBalloonTablePositionData( editor ) {
	const firstPosition = editor.model.document.selection.getFirstPosition();
	const modelTable = firstPosition.findAncestor( 'table' );
	const viewTable = editor.editing.mapper.toViewElement( modelTable );

	return {
		target: editor.editing.view.domConverter.mapViewToDom( viewTable ),
		positions: BALLOON_POSITIONS
	};
}

/**
 * Returns the positioning options that control the geometry of the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} with respect
 * to the selected table cell in the editor content.
 *
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @returns {module:utils/dom/position~Options}
 */
function getBalloonCellPositionData( editor ) {
	const mapper = editor.editing.mapper;
	const domConverter = editor.editing.view.domConverter;
	const selection = editor.model.document.selection;

	if ( selection.rangeCount > 1 ) {
		return {
			target: () => createBoundingRect( selection.getRanges(), editor ),
			positions: BALLOON_POSITIONS
		};
	}

	const modelTableCell = getTableCellAtPosition( selection.getFirstPosition() );
	const viewTableCell = mapper.toViewElement( modelTableCell );

	return {
		target: domConverter.mapViewToDom( viewTableCell ),
		positions: BALLOON_POSITIONS
	};
}

// Returns the first selected table cell from a multi-cell or in-cell selection.
//
// @param {module:engine/model/position~Position} position Document position.
// @returns {module:engine/model/element~Element}
function getTableCellAtPosition( position ) {
	const isTableCellSelected = position.nodeAfter && position.nodeAfter.is( 'element', 'tableCell' );

	return isTableCellSelected ? position.nodeAfter : position.findAncestor( 'tableCell' );
}

// Returns bounding rectangle for given model ranges.
//
// @param {Iterable.<module:engine/model/range~Range>} ranges Model ranges that the bounding rect should be returned for.
// @param {module:core/editor/editor~Editor} editor The editor instance.
// @returns {module:utils/dom/rect~Rect}
function createBoundingRect( ranges, editor ) {
	const mapper = editor.editing.mapper;
	const domConverter = editor.editing.view.domConverter;
	const rects = Array.from( ranges ).map( range => {
		const modelTableCell = getTableCellAtPosition( range.start );
		const viewTableCell = mapper.toViewElement( modelTableCell );
		return new rect_Rect( domConverter.mapViewToDom( viewTableCell ) );
	} );

	return rect_Rect.getBoundingRect( rects );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/icons/table-cell-properties.svg
/* harmony default export */ const table_cell_properties = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m11.105 18-.17 1H2.5A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1h15A1.5 1.5 0 0 1 19 2.5v9.975l-.85-.124-.15-.302V8h-5v4h.021l-.172.351-1.916.28-.151.027c-.287.063-.54.182-.755.341L8 13v5h3.105zM2 12h5V8H2v4zm10-4H8v4h4V8zM2 2v5h5V2H2zm0 16h5v-5H2v5zM13 7h5V2h-5v5zM8 2v5h4V2H8z\" opacity=\".6\"/><path d=\"m15.5 11.5 1.323 2.68 2.957.43-2.14 2.085.505 2.946L15.5 18.25l-2.645 1.39.505-2.945-2.14-2.086 2.957-.43L15.5 11.5zM13 6a1 1 0 0 1 1 1v3.172a2.047 2.047 0 0 0-.293.443l-.858 1.736-1.916.28-.151.027A1.976 1.976 0 0 0 9.315 14H7a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1h6zm-1 2H8v4h4V8z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/utils/table-properties.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/utils/table-properties
 */



/**
 * Returns a string if all four values of box sides are equal.
 *
 * If a string is passed, it is treated as a single value (pass-through).
 *
 *		// Returns 'foo':
 *		getSingleValue( { top: 'foo', right: 'foo', bottom: 'foo', left: 'foo' } );
 *		getSingleValue( 'foo' );
 *
 *		// Returns undefined:
 *		getSingleValue( { top: 'foo', right: 'foo', bottom: 'bar', left: 'foo' } );
 *		getSingleValue( { top: 'foo', right: 'foo' } );
 *
 * @param objectOrString
 * @returns {module:engine/view/stylesmap~BoxSides|String}
 */
function getSingleValue( objectOrString ) {
	if ( !objectOrString || !lodash_es_isObject( objectOrString ) ) {
		return objectOrString;
	}

	const { top, right, bottom, left } = objectOrString;

	if ( top == right && right == bottom && bottom == left ) {
		return top;
	}
}

/**
 * Adds a unit to a value if the value is a number or a string representing a number.
 *
 * **Note**: It does nothing to non-numeric values.
 *
 *		getSingleValue( 25, 'px' );		// '25px'
 *		getSingleValue( 25, 'em' );		// '25em'
 *		getSingleValue( '25em', 'px' );	// '25em'
 *		getSingleValue( 'foo', 'px' );	// 'foo'
 *
 * @param {*} value
 * @param {String} defaultUnit A default unit added to a numeric value.
 * @returns {String|*}
 */
function addDefaultUnitToNumericValue( value, defaultUnit ) {
	const numericValue = parseFloat( value );

	if ( Number.isNaN( numericValue ) ) {
		return value;
	}

	if ( String( numericValue ) !== String( value ) ) {
		return value;
	}

	return `${ numericValue }${ defaultUnit }`;
}

/**
 * Returns the normalized configuration.
 *
 * @param {Object} config
 * @param {Object} [options={}]
 * @param {Boolean} [options.includeAlignmentProperty=false] Whether the "alignment" property should be added.
 * @param {Boolean} [options.includePaddingProperty=false] Whether the "padding" property should be added.
 * @param {Boolean} [options.includeVerticalAlignmentProperty=false] Whether the "verticalAlignment" property should be added.
 * @param {Boolean} [options.includeHorizontalAlignmentProperty=false] Whether the "horizontalAlignment" property should be added.
 * @param {Boolean} [options.isRightToLeftContent=false] Whether the content is right-to-left.
 * @returns {Object}
 */
function getNormalizedDefaultProperties( config, options = {} ) {
	const normalizedConfig = Object.assign( {
		borderStyle: 'none',
		borderWidth: '',
		borderColor: '',
		backgroundColor: '',
		width: '',
		height: ''
	}, config );

	if ( options.includeAlignmentProperty && !normalizedConfig.alignment ) {
		normalizedConfig.alignment = 'center';
	}

	if ( options.includePaddingProperty && !normalizedConfig.padding ) {
		normalizedConfig.padding = '';
	}

	if ( options.includeVerticalAlignmentProperty && !normalizedConfig.verticalAlignment ) {
		normalizedConfig.verticalAlignment = 'middle';
	}

	if ( options.includeHorizontalAlignmentProperty && !normalizedConfig.horizontalAlignment ) {
		normalizedConfig.horizontalAlignment = options.isRightToLeftContent ? 'right' : 'left';
	}

	return normalizedConfig;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/tablecellpropertiesui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/tablecellpropertiesui
 */













const ERROR_TEXT_TIMEOUT = 500;

// Map of view properties and related commands.
const propertyToCommandMap = {
	borderStyle: 'tableCellBorderStyle',
	borderColor: 'tableCellBorderColor',
	borderWidth: 'tableCellBorderWidth',
	height: 'tableCellHeight',
	width: 'tableCellWidth',
	padding: 'tableCellPadding',
	backgroundColor: 'tableCellBackgroundColor',
	horizontalAlignment: 'tableCellHorizontalAlignment',
	verticalAlignment: 'tableCellVerticalAlignment'
};

/**
 * The table cell properties UI plugin. It introduces the `'tableCellProperties'` button
 * that opens a form allowing to specify the visual styling of a table cell.
 *
 * It uses the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
 *
 * @extends module:core/plugin~Plugin
 */
class TableCellPropertiesUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ContextualBalloon ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableCellPropertiesUI';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'table.tableCellProperties', {
			borderColors: defaultColors,
			backgroundColors: defaultColors
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		/**
		 * The default table cell properties.
		 *
		 * @protected
		 * @member {module:table/tablecellproperties~TableCellPropertiesOptions}
		 */
		this._defaultTableCellProperties = getNormalizedDefaultProperties(
			editor.config.get( 'table.tableCellProperties.defaultProperties' ),
			{
				includeVerticalAlignmentProperty: true,
				includeHorizontalAlignmentProperty: true,
				includePaddingProperty: true,
				isRightToLeftContent: editor.locale.contentLanguageDirection === 'rtl'
			}
		);

		/**
		 * The contextual balloon plugin instance.
		 *
		 * @private
		 * @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
		 */
		this._balloon = editor.plugins.get( ContextualBalloon );

		/**
		 * The cell properties form view displayed inside the balloon.
		 *
		 * @member {module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView}
		 */
		this.view = this._createPropertiesView();

		/**
		 * The batch used to undo all changes made by the form (which are live, as the user types)
		 * when "Cancel" was pressed. Each time the view is shown, a new batch is created.
		 *
		 * @protected
		 * @member {module:engine/model/batch~Batch}
		 */
		this._undoStepBatch = null;

		editor.ui.componentFactory.add( 'tableCellProperties', locale => {
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Cell properties' ),
				icon: table_cell_properties,
				tooltip: true
			} );

			this.listenTo( view, 'execute', () => this._showView() );

			const commands = Object.values( propertyToCommandMap )
				.map( commandName => editor.commands.get( commandName ) );

			view.bind( 'isEnabled' ).toMany( commands, 'isEnabled', ( ...areEnabled ) => (
				areEnabled.some( isCommandEnabled => isCommandEnabled )
			) );

			return view;
		} );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		// Destroy created UI components as they are not automatically destroyed.
		// See https://github.com/ckeditor/ckeditor5/issues/1341.
		this.view.destroy();
	}

	/**
	 * Creates the {@link module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView} instance.
	 *
	 * @private
	 * @returns {module:table/tablecellproperties/ui/tablecellpropertiesview~TableCellPropertiesView} The cell
	 * properties form view instance.
	 */
	_createPropertiesView() {
		const editor = this.editor;
		const viewDocument = editor.editing.view.document;
		const config = editor.config.get( 'table.tableCellProperties' );
		const borderColorsConfig = normalizeColorOptions( config.borderColors );
		const localizedBorderColors = getLocalizedColorOptions( editor.locale, borderColorsConfig );
		const backgroundColorsConfig = normalizeColorOptions( config.backgroundColors );
		const localizedBackgroundColors = getLocalizedColorOptions( editor.locale, backgroundColorsConfig );
		const view = new TableCellPropertiesView( editor.locale, {
			borderColors: localizedBorderColors,
			backgroundColors: localizedBackgroundColors,
			defaultTableCellProperties: this._defaultTableCellProperties
		} );
		const t = editor.t;

		// Render the view so its #element is available for the clickOutsideHandler.
		view.render();

		this.listenTo( view, 'submit', () => {
			this._hideView();
		} );

		this.listenTo( view, 'cancel', () => {
			// https://github.com/ckeditor/ckeditor5/issues/6180
			if ( this._undoStepBatch.operations.length ) {
				editor.execute( 'undo', this._undoStepBatch );
			}

			this._hideView();
		} );

		// Close the balloon on Esc key press.
		view.keystrokes.set( 'Esc', ( data, cancel ) => {
			this._hideView();
			cancel();
		} );

		// Reposition the balloon or hide the form if a table cell is no longer selected.
		this.listenTo( editor.ui, 'update', () => {
			if ( !getTableWidgetAncestor( viewDocument.selection ) ) {
				this._hideView();
			} else if ( this._isViewVisible ) {
				contextualballoon_repositionContextualBalloon( editor, 'cell' );
			}
		} );

		// Close on click outside of balloon panel element.
		clickoutsidehandler_clickOutsideHandler( {
			emitter: view,
			activator: () => this._isViewInBalloon,
			contextElements: [ this._balloon.view.element ],
			callback: () => this._hideView()
		} );

		const colorErrorText = getLocalizedColorErrorText( t );
		const lengthErrorText = getLocalizedLengthErrorText( t );

		// Create the "UI -> editor data" binding.
		// These listeners update the editor data (via table commands) when any observable
		// property of the view has changed. They also validate the value and display errors in the UI
		// when necessary. This makes the view live, which means the changes are
		// visible in the editing as soon as the user types or changes fields' values.
		view.on(
			'change:borderStyle',
			this._getPropertyChangeCallback( 'tableCellBorderStyle', this._defaultTableCellProperties.borderStyle )
		);

		view.on( 'change:borderColor', this._getValidatedPropertyChangeCallback( {
			viewField: view.borderColorInput,
			commandName: 'tableCellBorderColor',
			errorText: colorErrorText,
			validator: colorFieldValidator,
			defaultValue: this._defaultTableCellProperties.borderColor
		} ) );

		view.on( 'change:borderWidth', this._getValidatedPropertyChangeCallback( {
			viewField: view.borderWidthInput,
			commandName: 'tableCellBorderWidth',
			errorText: lengthErrorText,
			validator: lineWidthFieldValidator,
			defaultValue: this._defaultTableCellProperties.borderWidth
		} ) );

		view.on( 'change:padding', this._getValidatedPropertyChangeCallback( {
			viewField: view.paddingInput,
			commandName: 'tableCellPadding',
			errorText: lengthErrorText,
			validator: lengthFieldValidator,
			defaultValue: this._defaultTableCellProperties.padding
		} ) );

		view.on( 'change:width', this._getValidatedPropertyChangeCallback( {
			viewField: view.widthInput,
			commandName: 'tableCellWidth',
			errorText: lengthErrorText,
			validator: lengthFieldValidator,
			defaultValue: this._defaultTableCellProperties.width
		} ) );

		view.on( 'change:height', this._getValidatedPropertyChangeCallback( {
			viewField: view.heightInput,
			commandName: 'tableCellHeight',
			errorText: lengthErrorText,
			validator: lengthFieldValidator,
			defaultValue: this._defaultTableCellProperties.height
		} ) );

		view.on( 'change:backgroundColor', this._getValidatedPropertyChangeCallback( {
			viewField: view.backgroundInput,
			commandName: 'tableCellBackgroundColor',
			errorText: colorErrorText,
			validator: colorFieldValidator,
			defaultValue: this._defaultTableCellProperties.backgroundColor
		} ) );

		view.on(
			'change:horizontalAlignment',
			this._getPropertyChangeCallback( 'tableCellHorizontalAlignment', this._defaultTableCellProperties.horizontalAlignment )
		);
		view.on(
			'change:verticalAlignment',
			this._getPropertyChangeCallback( 'tableCellVerticalAlignment', this._defaultTableCellProperties.verticalAlignment )
		);

		return view;
	}

	/**
	 * In this method the "editor data -> UI" binding is happening.
	 *
	 * When executed, this method obtains selected cell property values from various table commands
	 * and passes them to the {@link #view}.
	 *
	 * This way, the UI stays up–to–date with the editor data.
	 *
	 * @private
	 */
	_fillViewFormFromCommandValues() {
		const commands = this.editor.commands;
		const borderStyleCommand = commands.get( 'tableCellBorderStyle' );

		Object.entries( propertyToCommandMap )
			.map( ( [ property, commandName ] ) => {
				const defaultValue = this._defaultTableCellProperties[ property ] || '';

				return [ property, commands.get( commandName ).value || defaultValue ];
			} )
			.forEach( ( [ property, value ] ) => {
				// Do not set the `border-color` and `border-width` fields if `border-style:none`.
				if ( ( property === 'borderColor' || property === 'borderWidth' ) && borderStyleCommand.value === 'none' ) {
					return;
				}

				this.view.set( property, value );
			} );
	}

	/**
	 * Shows the {@link #view} in the {@link #_balloon}.
	 *
	 * **Note**: Each time a view is shown, a new {@link #_undoStepBatch} is created. It contains
	 * all changes made to the document when the view is visible, allowing a single undo step
	 * for all of them.
	 *
	 * @protected
	 */
	_showView() {
		const editor = this.editor;

		// Update the view with the model values.
		this._fillViewFormFromCommandValues();

		this._balloon.add( {
			view: this.view,
			position: getBalloonCellPositionData( editor )
		} );

		// Create a new batch. Clicking "Cancel" will undo this batch.
		this._undoStepBatch = editor.model.createBatch();

		// Basic a11y.
		this.view.focus();
	}

	/**
	 * Removes the {@link #view} from the {@link #_balloon}.
	 *
	 * @protected
	 */
	_hideView() {
		if ( !this._isViewInBalloon ) {
			return;
		}

		const editor = this.editor;

		this.stopListening( editor.ui, 'update' );

		// Blur any input element before removing it from DOM to prevent issues in some browsers.
		// See https://github.com/ckeditor/ckeditor5/issues/1501.
		this.view.saveButtonView.focus();

		this._balloon.remove( this.view );

		// Make sure the focus is not lost in the process by putting it directly
		// into the editing view.
		this.editor.editing.view.focus();
	}

	/**
	 * Returns `true` when the {@link #view} is visible in the {@link #_balloon}.
	 *
	 * @private
	 * @type {Boolean}
	 */
	get _isViewVisible() {
		return this._balloon.visibleView === this.view;
	}

	/**
	 * Returns `true` when the {@link #view} is in the {@link #_balloon}.
	 *
	 * @private
	 * @type {Boolean}
	 */
	get _isViewInBalloon() {
		return this._balloon.hasView( this.view );
	}

	/**
	 * Creates a callback that when executed upon the {@link #view view's} property change
	 * executes a related editor command with the new property value.
	 *
	 * @private
	 * @param {String} commandName
	 * @param {String} defaultValue The default value of the command.
	 * @returns {Function}
	 */
	_getPropertyChangeCallback( commandName, defaultValue ) {
		return ( evt, propertyName, newValue, oldValue ) => {
			// If the "oldValue" is missing and "newValue" is set to the default value, do not execute the command.
			// It is an initial call (when opening the table properties view).
			if ( !oldValue && defaultValue === newValue ) {
				return;
			}

			this.editor.execute( commandName, {
				value: newValue,
				batch: this._undoStepBatch
			} );
		};
	}

	/**
	 * Creates a callback that when executed upon the {@link #view view's} property change:
	 * * Executes a related editor command with the new property value if the value is valid,
	 * * Or sets the error text next to the invalid field, if the value did not pass the validation.
	 *
	 * @private
	 * @param {Object} options
	 * @param {String} options.commandName
	 * @param {module:ui/view~View} options.viewField
	 * @param {Function} options.validator
	 * @param {String} options.errorText
	 * @param {String} options.defaultValue
	 * @returns {Function}
	 */
	_getValidatedPropertyChangeCallback( options ) {
		const { commandName, viewField, validator, errorText, defaultValue } = options;
		const setErrorTextDebounced = lodash_es_debounce( () => {
			viewField.errorText = errorText;
		}, ERROR_TEXT_TIMEOUT );

		return ( evt, propertyName, newValue, oldValue ) => {
			setErrorTextDebounced.cancel();

			// If the "oldValue" is missing and "newValue" is set to the default value, do not execute the command.
			// It is an initial call (when opening the table properties view).
			if ( !oldValue && defaultValue === newValue ) {
				return;
			}

			if ( validator( newValue ) ) {
				this.editor.execute( commandName, {
					value: newValue,
					batch: this._undoStepBatch
				} );

				viewField.errorText = null;
			} else {
				setErrorTextDebounced();
			}
		};
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpropertycommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellpropertycommand
 */



/**
 * The table cell attribute command.
 *
 * The command is a base command for other table cell property commands.
 *
 * @extends module:core/command~Command
 */
class TableCellPropertyCommand extends command_Command {
	/**
	 * Creates a new `TableCellPropertyCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} attributeName Table cell attribute name.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, attributeName, defaultValue ) {
		super( editor );

		/**
		 * The attribute that will be set by the command.
		 *
		 * @readonly
		 * @member {String}
		 */
		this.attributeName = attributeName;

		/**
		 * The default value for the attribute.
		 *
		 * @readonly
		 * @protected
		 * @member {String}
		 */
		this._defaultValue = defaultValue;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const selectedTableCells = tableUtils.getSelectionAffectedTableCells( editor.model.document.selection );

		this.isEnabled = !!selectedTableCells.length;
		this.value = this._getSingleValue( selectedTableCells );
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} [options]
	 * @param {*} [options.value] If set, the command will set the attribute on selected table cells.
	 * If it is not set, the command will remove the attribute from the selected table cells.
	 * @param {module:engine/model/batch~Batch} [options.batch] Pass the model batch instance to the command to aggregate changes,
	 * for example to allow a single undo step for multiple executions.
	 */
	execute( options = {} ) {
		const { value, batch } = options;
		const model = this.editor.model;
		const tableUtils = this.editor.plugins.get( 'TableUtils' );
		const tableCells = tableUtils.getSelectionAffectedTableCells( model.document.selection );
		const valueToSet = this._getValueToSet( value );

		model.enqueueChange( batch, writer => {
			if ( valueToSet ) {
				tableCells.forEach( tableCell => writer.setAttribute( this.attributeName, valueToSet, tableCell ) );
			} else {
				tableCells.forEach( tableCell => writer.removeAttribute( this.attributeName, tableCell ) );
			}
		} );
	}

	/**
	 * Returns the attribute value for a table cell.
	 *
	 * @param {module:engine/model/element~Element} tableCell
	 * @returns {String|undefined}
	 * @private
	 */
	_getAttribute( tableCell ) {
		if ( !tableCell ) {
			return;
		}

		const value = tableCell.getAttribute( this.attributeName );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}

	/**
	 * Returns the proper model value. It can be used to add a default unit to numeric values.
	 *
	 * @private
	 * @param {*} value
	 * @returns {*}
	 */
	_getValueToSet( value ) {
		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}

	/**
	 * Returns a single value for all selected table cells. If the value is the same for all cells,
	 * it will be returned (`undefined` otherwise).
	 *
	 * @param {Array.<module:engine/model/element~Element>} tableCell
	 * @returns {*}
	 * @private
	 */
	_getSingleValue( tableCell ) {
		const firstCellValue = this._getAttribute( tableCell[ 0 ] );

		const everyCellHasAttribute = tableCell.every( tableCell => this._getAttribute( tableCell ) === firstCellValue );

		return everyCellHasAttribute ? firstCellValue : undefined;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellwidth/commands/tablecellwidthcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellwidthcommand
 */



/**
 * The table cell width command.
 *
 * The command is registered by the {@link module:table/tablecellwidth/tablecellwidthediting~TableCellWidthEditing} as
 * the `'tableCellWidth'` editor command.
 *
 * To change the width of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellWidth', {
 *			value: '50px'
 *		} );
 *
 * **Note**: This command adds a default `'px'` unit to numeric values. Executing:
 *
 *		editor.execute( 'tableCellWidth', {
 *			value: '50'
 *		} );
 *
 * will set the `width` attribute to `'50px'` in the model.
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellWidthCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellWidthCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellWidth', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getValueToSet( value ) {
		value = addDefaultUnitToNumericValue( value, 'px' );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellwidth/tablecellwidthediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellwidth/tablecellwidthediting
 */








/**
 * The table cell width editing feature.
 *
 * Introduces `tableCellWidth` table cell model attribute alongside with its converters
 * and a command.
 *
 * @extends module:core/plugin~Plugin
 */
class TableCellWidthEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableCellWidthEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableEditing ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		const defaultTableCellProperties = getNormalizedDefaultProperties(
			editor.config.get( 'table.tableCellProperties.defaultProperties' )
		);

		enableProperty( editor.model.schema, editor.conversion, {
			modelAttribute: 'tableCellWidth',
			styleName: 'width',
			defaultValue: defaultTableCellProperties.width
		} );

		editor.commands.add( 'tableCellWidth', new TableCellWidthCommand( editor, defaultTableCellProperties.width ) );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellpaddingcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellpaddingcommand
 */




/**
 * The table cell padding command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellPadding'` editor command.
 *
 * To change the padding of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellPadding', {
 *			value: '5px'
 *		} );
 *
 * **Note**: This command adds the default `'px'` unit to numeric values. Executing:
 *
 *		editor.execute( 'tableCellPadding', {
 *			value: '5'
 *		} );
 *
 * will set the `padding` attribute to `'5px'` in the model.
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellPaddingCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellPaddingCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellPadding', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getAttribute( tableCell ) {
		if ( !tableCell ) {
			return;
		}

		const value = getSingleValue( tableCell.getAttribute( this.attributeName ) );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}

	/**
	 * @inheritDoc
	 */
	_getValueToSet( value ) {
		value = addDefaultUnitToNumericValue( value, 'px' );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellheightcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellheightcommand
 */




/**
 * The table cell height command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellHeight'` editor command.
 *
 * To change the height of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellHeight', {
 *			value: '50px'
 *		} );
 *
 * **Note**: This command adds the default `'px'` unit to numeric values. Executing:
 *
 *		editor.execute( 'tableCellHeight', {
 *			value: '50'
 *		} );
 *
 * will set the `height` attribute to `'50px'` in the model.
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellHeightCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellHeightCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellHeight', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getValueToSet( value ) {
		value = addDefaultUnitToNumericValue( value, 'px' );

		if ( value === this._defaultValue ) {
			return null;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellbackgroundcolorcommand
 */



/**
 * The table cell background color command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellBackgroundColor'` editor command.
 *
 * To change the background color of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellBackgroundColor', {
 *			value: '#f00'
 *		} );
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellBackgroundColorCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellBackgroundColorCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellBackgroundColor', defaultValue );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellverticalalignmentcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellverticalalignmentcommand
 */



/**
 * The table cell vertical alignment command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellVerticalAlignment'` editor command.
 *
 * To change the vertical text alignment of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellVerticalAlignment', {
 *			value: 'top'
 *		} );
 *
 * The following values, corresponding to the
 * [`vertical-align` CSS attribute](https://developer.mozilla.org/en-US/docs/Web/CSS/vertical-align), are allowed:
 *
 * * `'top'`
 * * `'bottom'`
 *
 * The `'middle'` value is the default one so there is no need to set it.
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellVerticalAlignmentCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellVerticalAlignmentCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value for the "alignment" attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellVerticalAlignment', defaultValue );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellhorizontalalignmentcommand
 */



/**
 * The table cell horizontal alignment command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellHorizontalAlignment'` editor command.
 *
 * To change the horizontal text alignment of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellHorizontalAlignment', {
 *			value: 'right'
 *		} );
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellHorizontalAlignmentCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellHorizontalAlignmentCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value for the "alignment" attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellHorizontalAlignment', defaultValue );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellborderstylecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellborderstylecommand
 */




/**
 * The table cell border style command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellBorderStyle'` editor command.
 *
 * To change the border style of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellBorderStyle', {
 *			value: 'dashed'
 *		} );
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellBorderStyleCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellBorderStyleCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellBorderStyle', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getAttribute( tableCell ) {
		if ( !tableCell ) {
			return;
		}

		const value = getSingleValue( tableCell.getAttribute( this.attributeName ) );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellbordercolorcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellbordercolorcommand
 */




/**
 * The table cell border color command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellBorderColor'` editor command.
 *
 * To change the border color of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellBorderColor', {
 *			value: '#f00'
 *		} );
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellBorderColorCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellBorderColorCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellBorderColor', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getAttribute( tableCell ) {
		if ( !tableCell ) {
			return;
		}

		const value = getSingleValue( tableCell.getAttribute( this.attributeName ) );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/commands/tablecellborderwidthcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/commands/tablecellborderwidthcommand
 */




/**
 * The table cell border width command.
 *
 * The command is registered by the {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing} as
 * the `'tableCellBorderWidth'` editor command.
 *
 * To change the border width of selected cells, execute the command:
 *
 *		editor.execute( 'tableCellBorderWidth', {
 *			value: '5px'
 *		} );
 *
 * **Note**: This command adds the default `'px'` unit to numeric values. Executing:
 *
 *		editor.execute( 'tableCellBorderWidth', {
 *			value: '5'
 *		} );
 *
 * will set the `borderWidth` attribute to `'5px'` in the model.
 *
 * @extends module:table/tablecellproperties/commands/tablecellpropertycommand~TableCellPropertyCommand
 */
class TableCellBorderWidthCommand extends TableCellPropertyCommand {
	/**
	 * Creates a new `TableCellBorderWidthCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableCellBorderWidth', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getAttribute( tableCell ) {
		if ( !tableCell ) {
			return;
		}

		const value = getSingleValue( tableCell.getAttribute( this.attributeName ) );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}

	/**
	 * @inheritDoc
	 */
	_getValueToSet( value ) {
		value = addDefaultUnitToNumericValue( value, 'px' );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties/tablecellpropertiesediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties/tablecellpropertiesediting
 */


















const VALIGN_VALUES_REG_EXP = /^(top|middle|bottom)$/;
const ALIGN_VALUES_REG_EXP = /^(left|center|right|justify)$/;

/**
 * The table cell properties editing feature.
 *
 * Introduces table cell model attributes and their conversion:
 *
 * - border: `tableCellBorderStyle`, `tableCellBorderColor` and `tableCellBorderWidth`
 * - background color: `tableCellBackgroundColor`
 * - cell padding: `tableCellPadding`
 * - horizontal and vertical alignment: `tableCellHorizontalAlignment`, `tableCellVerticalAlignment`
 * - cell width and height: `tableCellWidth`, `tableCellHeight`
 *
 * It also registers commands used to manipulate the above attributes:
 *
 * - border: the `'tableCellBorderStyle'`, `'tableCellBorderColor'` and `'tableCellBorderWidth'` commands
 * - background color: the `'tableCellBackgroundColor'` command
 * - cell padding: the `'tableCellPadding'` command
 * - horizontal and vertical alignment: the `'tableCellHorizontalAlignment'` and `'tableCellVerticalAlignment'` commands
 * - width and height: the `'tableCellWidth'` and `'tableCellHeight'` commands
 *
 * @extends module:core/plugin~Plugin
 */
class TableCellPropertiesEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableCellPropertiesEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableEditing, TableCellWidthEditing ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;
		const conversion = editor.conversion;

		editor.config.define( 'table.tableCellProperties.defaultProperties', {} );

		const defaultTableCellProperties = getNormalizedDefaultProperties(
			editor.config.get( 'table.tableCellProperties.defaultProperties' ),
			{
				includeVerticalAlignmentProperty: true,
				includeHorizontalAlignmentProperty: true,
				includePaddingProperty: true,
				isRightToLeftContent: editor.locale.contentLanguageDirection === 'rtl'
			}
		);

		editor.data.addStyleProcessorRules( addBorderRules );
		enableBorderProperties( schema, conversion, {
			color: defaultTableCellProperties.borderColor,
			style: defaultTableCellProperties.borderStyle,
			width: defaultTableCellProperties.borderWidth
		} );
		editor.commands.add( 'tableCellBorderStyle', new TableCellBorderStyleCommand( editor, defaultTableCellProperties.borderStyle ) );
		editor.commands.add( 'tableCellBorderColor', new TableCellBorderColorCommand( editor, defaultTableCellProperties.borderColor ) );
		editor.commands.add( 'tableCellBorderWidth', new TableCellBorderWidthCommand( editor, defaultTableCellProperties.borderWidth ) );

		enableProperty( schema, conversion, {
			modelAttribute: 'tableCellHeight',
			styleName: 'height',
			defaultValue: defaultTableCellProperties.height
		} );
		editor.commands.add( 'tableCellHeight', new TableCellHeightCommand( editor, defaultTableCellProperties.height ) );

		editor.data.addStyleProcessorRules( addPaddingRules );
		enableProperty( schema, conversion, {
			modelAttribute: 'tableCellPadding',
			styleName: 'padding',
			reduceBoxSides: true,
			defaultValue: defaultTableCellProperties.padding
		} );
		editor.commands.add( 'tableCellPadding', new TableCellPaddingCommand( editor, defaultTableCellProperties.padding ) );

		editor.data.addStyleProcessorRules( addBackgroundRules );
		enableProperty( schema, conversion, {
			modelAttribute: 'tableCellBackgroundColor',
			styleName: 'background-color',
			defaultValue: defaultTableCellProperties.backgroundColor
		} );
		editor.commands.add(
			'tableCellBackgroundColor',
			new TableCellBackgroundColorCommand( editor, defaultTableCellProperties.backgroundColor )
		);

		enableHorizontalAlignmentProperty( schema, conversion, defaultTableCellProperties.horizontalAlignment );
		editor.commands.add(
			'tableCellHorizontalAlignment',
			new TableCellHorizontalAlignmentCommand( editor, defaultTableCellProperties.horizontalAlignment )
		);

		enableVerticalAlignmentProperty( schema, conversion, defaultTableCellProperties.verticalAlignment );
		editor.commands.add(
			'tableCellVerticalAlignment',
			new TableCellVerticalAlignmentCommand( editor, defaultTableCellProperties.verticalAlignment )
		);
	}
}

// Enables the `'tableCellBorderStyle'`, `'tableCellBorderColor'` and `'tableCellBorderWidth'` attributes for table cells.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/conversion/conversion~Conversion} conversion
// @param {Object} defaultBorder The default border values.
// @param {String} defaultBorder.color The default `tableCellBorderColor` value.
// @param {String} defaultBorder.style The default `tableCellBorderStyle` value.
// @param {String} defaultBorder.width The default `tableCellBorderWidth` value.
function enableBorderProperties( schema, conversion, defaultBorder ) {
	const modelAttributes = {
		width: 'tableCellBorderWidth',
		color: 'tableCellBorderColor',
		style: 'tableCellBorderStyle'
	};

	schema.extend( 'tableCell', {
		allowAttributes: Object.values( modelAttributes )
	} );

	upcastBorderStyles( conversion, 'td', modelAttributes, defaultBorder );
	upcastBorderStyles( conversion, 'th', modelAttributes, defaultBorder );
	downcastAttributeToStyle( conversion, { modelElement: 'tableCell', modelAttribute: modelAttributes.style, styleName: 'border-style' } );
	downcastAttributeToStyle( conversion, { modelElement: 'tableCell', modelAttribute: modelAttributes.color, styleName: 'border-color' } );
	downcastAttributeToStyle( conversion, { modelElement: 'tableCell', modelAttribute: modelAttributes.width, styleName: 'border-width' } );
}

// Enables the `'tableCellHorizontalAlignment'` attribute for table cells.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/conversion/conversion~Conversion} conversion
// @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
// @param {String} defaultValue The default horizontal alignment value.
function enableHorizontalAlignmentProperty( schema, conversion, defaultValue ) {
	schema.extend( 'tableCell', {
		allowAttributes: [ 'tableCellHorizontalAlignment' ]
	} );

	conversion.for( 'downcast' )
		.attributeToAttribute( {
			model: {
				name: 'tableCell',
				key: 'tableCellHorizontalAlignment'
			},
			view: alignment => ( {
				key: 'style',
				value: {
					'text-align': alignment
				}
			} )
		} );

	conversion.for( 'upcast' )
		// Support for the `text-align:*;` CSS definition for the table cell alignment.
		.attributeToAttribute( {
			view: {
				name: /^(td|th)$/,
				styles: {
					'text-align': ALIGN_VALUES_REG_EXP
				}
			},
			model: {
				key: 'tableCellHorizontalAlignment',
				value: viewElement => {
					const align = viewElement.getStyle( 'text-align' );

					return align === defaultValue ? null : align;
				}
			}
		} )
		// Support for the `align` attribute as the backward compatibility while pasting from other sources.
		.attributeToAttribute( {
			view: {
				name: /^(td|th)$/,
				attributes: {
					align: ALIGN_VALUES_REG_EXP
				}
			},
			model: {
				key: 'tableCellHorizontalAlignment',
				value: viewElement => {
					const align = viewElement.getAttribute( 'align' );

					return align === defaultValue ? null : align;
				}
			}
		} );
}

// Enables the `'verticalAlignment'` attribute for table cells.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/conversion/conversion~Conversion} conversion
// @param {String} defaultValue The default vertical alignment value.
function enableVerticalAlignmentProperty( schema, conversion, defaultValue ) {
	schema.extend( 'tableCell', {
		allowAttributes: [ 'tableCellVerticalAlignment' ]
	} );

	conversion.for( 'downcast' )
		.attributeToAttribute( {
			model: {
				name: 'tableCell',
				key: 'tableCellVerticalAlignment'
			},
			view: alignment => ( {
				key: 'style',
				value: {
					'vertical-align': alignment
				}
			} )
		} );

	conversion.for( 'upcast' )
		// Support for the `vertical-align:*;` CSS definition for the table cell alignment.
		.attributeToAttribute( {
			view: {
				name: /^(td|th)$/,
				styles: {
					'vertical-align': VALIGN_VALUES_REG_EXP
				}
			},
			model: {
				key: 'tableCellVerticalAlignment',
				value: viewElement => {
					const align = viewElement.getStyle( 'vertical-align' );

					return align === defaultValue ? null : align;
				}
			}
		} )
		// Support for the `align` attribute as the backward compatibility while pasting from other sources.
		.attributeToAttribute( {
			view: {
				name: /^(td|th)$/,
				attributes: {
					valign: VALIGN_VALUES_REG_EXP
				}
			},
			model: {
				key: 'tableCellVerticalAlignment',
				value: viewElement => {
					const valign = viewElement.getAttribute( 'valign' );

					return valign === defaultValue ? null : valign;
				}
			}
		} );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecellproperties.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecellproperties
 */





/**
 * The table cell properties feature. Enables support for setting properties of table cells (size, border, background, etc.).
 *
 * Read more in the {@glink features/table#table-and-cell-styling-tools Table and cell styling tools} section.
 * See also the {@link module:table/tableproperties~TableProperties} plugin.
 *
 * This is a "glue" plugin that loads the
 * {@link module:table/tablecellproperties/tablecellpropertiesediting~TableCellPropertiesEditing table cell properties editing feature} and
 * the {@link module:table/tablecellproperties/tablecellpropertiesui~TableCellPropertiesUI table cell properties UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class TableCellProperties extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableCellProperties';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableCellPropertiesEditing, TableCellPropertiesUI ];
	}
}

/**
 * The configuration of the table cell properties user interface (balloon). It allows to define:
 *
 * * The color palette for the cell border color style field (`tableCellProperties.borderColors`),
 * * The color palette for the cell background style field (`tableCellProperties.backgroundColors`).
 *
 *		const tableConfig = {
 *			tableCellProperties: {
 *				borderColors: [
 *					{
 *						color: 'hsl(0, 0%, 90%)',
 *						label: 'Light grey'
 *					},
 *					// ...
 *				],
 *				backgroundColors: [
 *					{
 *						color: 'hsl(120, 75%, 60%)',
 *						label: 'Green'
 *					},
 *					// ...
 *				]
 *			}
 *		};
 *
 * * The default styles for table cells (`tableCellProperties.defaultProperties`):
 *
 *		const tableConfig = {
 *			tableCellProperties: {
 *				defaultProperties: {
 *					horizontalAlignment: 'right',
 *					verticalAlignment: 'bottom',
 *					padding: '5px'
 *				}
 *			}
 *		}
 *
 * 	 {@link module:table/tableproperties~TablePropertiesOptions Read more about the supported properties.}
 *
 * **Note**: The `borderColors` and `backgroundColors` options do not impact the data loaded into the editor,
 * i.e. they do not limit or filter the colors in the data. They are used only in the user interface
 * allowing users to pick colors in a more convenient way. The `defaultProperties` option does impact the data.
 * Default values will not be kept in the editor model.
 *
 * The default color palettes for the cell background and the cell border are the same
 * ({@link module:table/utils/ui/table-properties~defaultColors check out their content}).
 *
 * Both color palette configurations must follow the
 * {@link module:table/table~TableColorConfig table color configuration format}.
 *
 * Read more about configuring the table feature in {@link module:table/table~TableConfig}.
 *
 * @member {Object} module:table/table~TableConfig#tableCellProperties
 */

/**
 * The configuration of the table cell default properties feature.
 *
 * @typedef {Object} module:table/tablecellproperties~TableCellPropertiesOptions
 *
 * @property {String} width The default `width` of the table cell.
 *
 * @property {String} height The default `height` of the table cell.
 *
 * @property {String} padding The default `padding` of the table cell.
 *
 * @property {String} backgroundColor The default `background-color` of the table cell.
 *
 * @property {String} borderColor The default `border-color` of the table cell.
 *
 * @property {String} borderWidth The default `border-width` of the table cell.
 *
 * @property {String} [borderStyle='none'] The default `border-style` of the table cell.
 *
 * @property {String} [horizontalAlignment='center'] The default `horizontalAlignment` of the table cell.
 *
 * @property {String} [verticalAlignment='middle'] The default `verticalAlignment` of the table cell.
 */


;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tablepropertycommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tablepropertycommand
 */



/**
 * The table cell attribute command.
 *
 * This command is a base command for other table property commands.
 *
 * @extends module:core/command~Command
 */
class TablePropertyCommand extends command_Command {
	/**
	 * Creates a new `TablePropertyCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} attributeName Table cell attribute name.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, attributeName, defaultValue ) {
		super( editor );

		/**
		 * The attribute that will be set by the command.
		 *
		 * @readonly
		 * @member {String}
		 */
		this.attributeName = attributeName;

		/**
		 * The default value for the attribute.
		 *
		 * @readonly
		 * @protected
		 * @member {String}
		 */
		this._defaultValue = defaultValue;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		const editor = this.editor;
		const selection = editor.model.document.selection;

		const table = selection.getFirstPosition().findAncestor( 'table' );

		this.isEnabled = !!table;
		this.value = this._getValue( table );
	}

	/**
	 * Executes the command.
	 *
	 * @fires execute
	 * @param {Object} [options]
	 * @param {*} [options.value] If set, the command will set the attribute on the selected table.
	 * If not set, the command will remove the attribute from the selected table.
	 * @param {module:engine/model/batch~Batch} [options.batch] Pass the model batch instance to the command to aggregate changes,
	 * for example, to allow a single undo step for multiple executions.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const selection = model.document.selection;

		const { value, batch } = options;

		const table = selection.getFirstPosition().findAncestor( 'table' );
		const valueToSet = this._getValueToSet( value );

		model.enqueueChange( batch, writer => {
			if ( valueToSet ) {
				writer.setAttribute( this.attributeName, valueToSet, table );
			} else {
				writer.removeAttribute( this.attributeName, table );
			}
		} );
	}

	/**
	 * Returns the attribute value for a table.
	 *
	 * @param {module:engine/model/element~Element} table
	 * @returns {String|undefined}
	 * @private
	 */
	_getValue( table ) {
		if ( !table ) {
			return;
		}

		const value = table.getAttribute( this.attributeName );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}

	/**
	 * Returns the proper model value. It can be used to add a default unit to numeric values.
	 *
	 * @private
	 * @param {*} value
	 * @returns {*}
	 */
	_getValueToSet( value ) {
		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecolumnresize/tablewidthresizecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecolumnresize/tablewidthresizecommand
 */



/**
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableWidthResizeCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableWidthResizeCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		// We create a custom command instead of using the existing `TableWidthCommand`
		// as we also need to change the `columnWidths` property and running both commands
		// separately would make the integration with Track Changes feature more troublesome.
		super( editor, 'tableWidth', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		// The command is always enabled as it doesn't care about the actual selection - table can be resized
		// even if the selection is elsewhere.
		this.isEnabled = true;
	}

	/**
	 * Changes the `tableWidth` and `columnWidths` attribute values for the given or currently selected table.
	 *
	 * @param {Object} options
	 * @param {String} [options.tableWidth] The new table width.
	 * @param {String} [options.columnWidths] The new table column widths.
	 * @param {module:engine/model/element~Element} [options.table] The table that is affected by the resize.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const table = options.table || model.document.selection.getSelectedElement();
		const { tableWidth, columnWidths } = options;

		model.change( writer => {
			if ( tableWidth ) {
				writer.setAttribute( this.attributeName, tableWidth, table );
				writer.setAttribute( 'columnWidths', columnWidths, table );
			} else {
				writer.removeAttribute( this.attributeName, table );
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecolumnresize/tablecolumnwidthscommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecolumnresize/tablecolumnwidthscommand
 */



/**
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableColumnWidthsCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableColumnWidthsCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'columnWidths', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		// The command is always enabled as it doesn't care about the actual selection - table can be resized
		// even if the selection is elsewhere.
		this.isEnabled = true;
	}

	/**
	 * Changes the `columnWidths` attribute value for the given or currently selected table.
	 *
	 * @param {Object} options
	 * @param {String} [options.columnWidths] New value of the `columnWidths` attribute.
	 * @param {module:engine/model/element~Element} [options.table] The table that is having the columns resized.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const table = options.table || model.document.selection.getSelectedElement();
		const { columnWidths } = options;

		model.change( writer => {
			if ( columnWidths ) {
				writer.setAttribute( this.attributeName, columnWidths, table );
			} else {
				writer.removeAttribute( this.attributeName, table );
			}
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecolumnresize/constants.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecolumnresize/constants
 */

/**
 * The minimum column width given as a percentage value. Used in situations when the table is not yet rendered, so it is impossible to
 * calculate how many percentage of the table width would be {@link ~COLUMN_MIN_WIDTH_IN_PIXELS minimum column width in pixels}.
 *
 * @const {Number}
 */
const COLUMN_MIN_WIDTH_AS_PERCENTAGE = 5;

/**
 * The minimum column width in pixels when the maximum table width is known.
 *
 * @const {Number}
 */
const COLUMN_MIN_WIDTH_IN_PIXELS = 40;

/**
 * Determines how many digits after the decimal point are used to store the column width as a percentage value.
 *
 * @const {Number}
 */
const COLUMN_WIDTH_PRECISION = 2;

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecolumnresize/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecolumnresize/utils
 */




/**
 * Returns all the inserted or changed table model elements in a given change set. Only the tables
 * with 'columnsWidth' attribute are taken into account. The returned set may be empty.
 *
 * Most notably if an entire table is removed it will not be included in returned set.
 *
 * @param {module:engine/model/model~Model} model The model to collect the affected elements from.
 * @returns {Set.<module:engine/model/element~Element>} A set of table model elements.
 */
function getChangedResizedTables( model ) {
	const affectedTables = new Set();

	for ( const change of model.document.differ.getChanges() ) {
		let referencePosition = null;

		// Checks if the particular change from the differ is:
		// - an insertion or removal of a table, a row or a cell,
		// - an attribute change on a table, a row or a cell.
		switch ( change.type ) {
			case 'insert':
				referencePosition = [ 'table', 'tableRow', 'tableCell' ].includes( change.name ) ?
					change.position :
					null;

				break;

			case 'remove':
				// If the whole table is removed, there's no need to update its column widths (#12201).
				referencePosition = [ 'tableRow', 'tableCell' ].includes( change.name ) ?
					change.position :
					null;

				break;

			case 'attribute':
				if ( change.range.start.nodeAfter ) {
					referencePosition = [ 'table', 'tableRow', 'tableCell' ].includes( change.range.start.nodeAfter.name ) ?
						change.range.start :
						null;
				}

				break;
		}

		if ( !referencePosition ) {
			continue;
		}

		const tableNode = ( referencePosition.nodeAfter && referencePosition.nodeAfter.name === 'table' ) ?
			referencePosition.nodeAfter : referencePosition.findAncestor( 'table' );

		// We iterate over the whole table looking for the nested tables that are also affected.
		for ( const node of model.createRangeOn( tableNode ).getItems() ) {
			if ( node.is( 'element' ) && node.name === 'table' && node.hasAttribute( 'columnWidths' ) ) {
				affectedTables.add( node );
			}
		}
	}

	return affectedTables;
}

/**
 * Calculates the percentage of the minimum column width given in pixels for a given table.
 *
 * @param {module:engine/model/element~Element} modelTable A table model element.
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @returns {Number} The minimal column width in percentage.
 */
function getColumnMinWidthAsPercentage( modelTable, editor ) {
	return COLUMN_MIN_WIDTH_IN_PIXELS * 100 / getTableWidthInPixels( modelTable, editor );
}

/**
 * Calculates the table width in pixels.
 *
 * @param {module:engine/model/element~Element} modelTable A table model element.
 * @param {module:core/editor/editor~Editor} editor The editor instance.
 * @returns {Number} The width of the table in pixels.
 */
function getTableWidthInPixels( modelTable, editor ) {
	// It is possible for a table to not have a <tbody> element - see #11878.
	const referenceElement = getChildrenViewElement( modelTable, 'tbody', editor ) || getChildrenViewElement( modelTable, 'thead', editor );
	const domReferenceElement = editor.editing.view.domConverter.mapViewToDom( referenceElement );

	return getElementWidthInPixels( domReferenceElement );
}

// Returns the a view element with a given name that is nested directly in a `<table>` element
// related to a given `modelTable`.
//
// @private
// @param {module:engine/model/element~Element} table
// @param {module:core/editor/editor~Editor} editor
// @param {String} elementName Name of a view to be looked for, e.g. `'colgroup`', `'thead`'.
// @returns {module:engine/view/element~Element|undefined} Matched view or `undefined` otherwise.
function getChildrenViewElement( modelTable, elementName, editor ) {
	const viewFigure = editor.editing.mapper.toViewElement( modelTable );
	const viewTable = [ ...viewFigure.getChildren() ].find( viewChild => viewChild.is( 'element', 'table' ) );

	return [ ...viewTable.getChildren() ].find( viewChild => viewChild.is( 'element', elementName ) );
}

/**
 * Returns the computed width (in pixels) of the DOM element without padding and borders.
 *
 * @param {HTMLElement} domElement A DOM element.
 * @returns {Number} The width of the DOM element in pixels.
 */
function getElementWidthInPixels( domElement ) {
	const styles = dom_global.window.getComputedStyle( domElement );

	// In the 'border-box' box sizing algorithm, the element's width
	// already includes the padding and border width (#12335).
	if ( styles.boxSizing === 'border-box' ) {
		return parseFloat( styles.width ) -
			parseFloat( styles.paddingLeft ) -
			parseFloat( styles.paddingRight ) -
			parseFloat( styles.borderLeftWidth ) -
			parseFloat( styles.borderRightWidth );
	} else {
		return parseFloat( styles.width );
	}
}

/**
 * Returns the column indexes on the left and right edges of a cell. They differ if the cell spans
 * across multiple columns.
 *
 * @param {module:engine/model/element~Element} cell A table cell model element.
 * @param {module:table/tableutils~TableUtils} tableUtils The Table Utils plugin instance.
 * @returns {Object} An object containing the indexes of the left and right edges of the cell.
 * @returns {Number} return.leftEdge The index of the left edge of the cell.
 * @returns {Number} return.rightEdge The index of the right edge of the cell.
 */
function getColumnEdgesIndexes( cell, tableUtils ) {
	const cellColumnIndex = tableUtils.getCellLocation( cell ).column;
	const cellWidth = cell.getAttribute( 'colspan' ) || 1;

	return {
		leftEdge: cellColumnIndex,
		rightEdge: cellColumnIndex + cellWidth - 1
	};
}

/**
 * Rounds the provided value to a fixed-point number with defined number of digits after the decimal point.
 *
 * @param {Number|String} value A number to be rounded.
 * @returns {Number} The rounded number.
 */
function toPrecision( value ) {
	const multiplier = Math.pow( 10, COLUMN_WIDTH_PRECISION );
	const number = parseFloat( value );

	return Math.round( number * multiplier ) / multiplier;
}

/**
 * Clamps the number within the inclusive lower (min) and upper (max) bounds. Returned number is rounded using the
 * {@link ~toPrecision `toPrecision()`} function.
 *
 * @param {Number} number A number to be clamped.
 * @param {Number} min A lower bound.
 * @param {Number} max An upper bound.
 * @returns {Number} The clamped number.
 */
function clamp( number, min, max ) {
	if ( number <= min ) {
		return toPrecision( min );
	}

	if ( number >= max ) {
		return toPrecision( max );
	}

	return toPrecision( number );
}

/**
 * Creates an array with defined length and fills all elements with defined value.
 *
 * @param {Number} length The length of the array.
 * @param {*} value The value to fill the array with.
 * @returns {Array.<*>} An array with defined length and filled with defined value.
 */
function createFilledArray( length, value ) {
	return Array( length ).fill( value );
}

/**
 * Sums all array values that can be parsed to a float.
 *
 * @param {Array.<Number>} array An array of numbers.
 * @returns {Number} The sum of all array values.
 */
function sumArray( array ) {
	return array
		.map( value => parseFloat( value ) )
		.filter( value => !Number.isNaN( value ) )
		.reduce( ( result, item ) => result + item, 0 );
}

/**
 * Makes sure that the sum of the widths from all columns is 100%. If the sum of all the widths is not equal 100%, all the widths are
 * changed proportionally so that they all sum back to 100%. If there are columns without specified width, the amount remaining
 * after assigning the known widths will be distributed equally between them.
 *
 * Currently, only widths provided as percentage values are supported.
 *
 * @param {Array.<Number>} columnWidths An array of column widths.
 * @returns {Array.<Number>} An array of column widths guaranteed to sum up to 100%.
 */
function normalizeColumnWidths( columnWidths ) {
	columnWidths = calculateMissingColumnWidths( columnWidths );
	const totalWidth = sumArray( columnWidths );

	if ( totalWidth === 100 ) {
		return columnWidths;
	}

	return columnWidths
		// Adjust all the columns proportionally.
		.map( columnWidth => toPrecision( columnWidth * 100 / totalWidth ) )
		// Due to rounding of numbers it may happen that the sum of the widths of all columns will not be exactly 100%. Therefore, the width
		// of the last column is explicitly adjusted (narrowed or expanded), since all the columns have been proportionally changed already.
		.map( ( columnWidth, columnIndex, columnWidths ) => {
			const isLastColumn = columnIndex === columnWidths.length - 1;

			if ( !isLastColumn ) {
				return columnWidth;
			}

			const totalWidth = sumArray( columnWidths );

			return toPrecision( columnWidth + 100 - totalWidth );
		} );
}

// Initializes the column widths by parsing the attribute value and calculating the uninitialized column widths. The special value 'auto'
// indicates that width for the column must be calculated. The width of such uninitialized column is calculated as follows:
// - If there is enough free space in the table for all uninitialized columns to have at least the minimum allowed width for all of them,
//   then set this width equally for all uninitialized columns.
// - Otherwise, just set the minimum allowed width for all uninitialized columns. The sum of all column widths will be greater than 100%,
//   but then it will be adjusted proportionally to 100% in {@link #normalizeColumnWidths `normalizeColumnWidths()`}.
//
// @private
// @param {Array.<Number>} columnWidths An array of column widths.
// @returns {Array.<Number>} An array with 'auto' values replaced with calculated widths.
function calculateMissingColumnWidths( columnWidths ) {
	const numberOfUninitializedColumns = columnWidths.filter( columnWidth => columnWidth === 'auto' ).length;

	if ( numberOfUninitializedColumns === 0 ) {
		return columnWidths.map( columnWidth => toPrecision( columnWidth ) );
	}

	const totalWidthOfInitializedColumns = sumArray( columnWidths );

	const widthForUninitializedColumn = Math.max(
		( 100 - totalWidthOfInitializedColumns ) / numberOfUninitializedColumns,
		COLUMN_MIN_WIDTH_AS_PERCENTAGE
	);

	return columnWidths
		.map( columnWidth => columnWidth === 'auto' ? widthForUninitializedColumn : columnWidth )
		.map( columnWidth => toPrecision( columnWidth ) );
}

/**
 * Inserts column resizer element into a view cell if it is missing.
 *
 * @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter View writer instance.
 * @param {module:engine/view/element~Element} viewCell View cell where resizer should be put.
 */
function ensureColumnResizerElement( viewWriter, viewCell ) {
	let viewTableColumnResizerElement = [ ...viewCell.getChildren() ]
		.find( viewElement => viewElement.hasClass( 'ck-table-column-resizer' ) );

	if ( viewTableColumnResizerElement ) {
		return;
	}

	viewTableColumnResizerElement = viewWriter.createUIElement( 'div', {
		class: 'ck-table-column-resizer'
	} );

	viewWriter.insert(
		viewWriter.createPositionAt( viewCell, 'end' ),
		viewTableColumnResizerElement
	);
}

/**
 * Calculates the total horizontal space taken by the cell. That includes:
 *  * width,
 *  * left and red padding,
 *  * border width.
 *
 * @param {HTMLElement}  domCell A DOM cell element.
 * @returns {Number} Width in pixels without `px` at the end.
 */
function getDomCellOuterWidth( domCell ) {
	const styles = dom_global.window.getComputedStyle( domCell );

	// In the 'border-box' box sizing algorithm, the element's width
	// already includes the padding and border width (#12335).
	if ( styles.boxSizing === 'border-box' ) {
		return parseInt( styles.width );
	} else {
		return parseFloat( styles.width ) +
			parseFloat( styles.paddingLeft ) +
			parseFloat( styles.paddingRight ) +
			parseFloat( styles.borderWidth );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecolumnresize/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecolumnresize/converters
 */



/**
 * Returns a helper for converting a view `<colgroup>` and `<col>` elements to the model table `columnWidths` attribute.
 *
 * Only the inline width, provided as a percentage value, in the `<col>` element is taken into account. If there are not enough `<col>`
 * elements matching this condition, the special value `auto` is returned. It indicates that the width of a column will be automatically
 * calculated in the
 * {@link module:table/tablecolumnresize/tablecolumnresizeediting~TableColumnResizeEditing#_registerPostFixer post-fixer}, depending
 * on the available table space.
 *
 * @param {module:core/plugin~Plugin} tableUtilsPlugin The {@link module:table/tableutils~TableUtils} plugin instance.
 * @returns {Function} Conversion helper.
 */
function upcastColgroupElement( tableUtilsPlugin ) {
	return dispatcher => dispatcher.on( 'element:colgroup', ( evt, data, conversionApi ) => {
		const viewColgroupElement = data.viewItem;

		if ( !conversionApi.consumable.test( viewColgroupElement, { name: true } ) ) {
			return;
		}

		conversionApi.consumable.consume( viewColgroupElement, { name: true } );

		const modelTable = data.modelCursor.findAncestor( 'table' );
		const numberOfColumns = tableUtilsPlugin.getColumns( modelTable );

		let columnWidths = [ ...Array( numberOfColumns ).keys() ]
			.map( columnIndex => {
				const viewChild = viewColgroupElement.getChild( columnIndex );

				if ( !viewChild || !viewChild.is( 'element', 'col' ) ) {
					return 'auto';
				}

				const viewColWidth = viewChild.getStyle( 'width' );

				if ( !viewColWidth || !viewColWidth.endsWith( '%' ) ) {
					return 'auto';
				}

				return viewColWidth;
			} );

		if ( columnWidths.includes( 'auto' ) ) {
			columnWidths = normalizeColumnWidths( columnWidths ).map( width => width + '%' );
		}

		conversionApi.writer.setAttribute( 'columnWidths', columnWidths.join( ',' ), modelTable );
	} );
}

/**
 * Returns a helper for converting a model table `columnWidths` attribute to view `<colgroup>` and `<col>` elements.
 *
 * @returns {Function} Conversion helper.
 */
function downcastTableColumnWidthsAttribute() {
	return dispatcher => dispatcher.on( 'attribute:columnWidths:table', ( evt, data, conversionApi ) => {
		const viewWriter = conversionApi.writer;
		const modelTable = data.item;

		const viewTable = [ ...conversionApi.mapper.toViewElement( modelTable ).getChildren() ]
			.find( viewChild => viewChild.is( 'element', 'table' ) );

		if ( data.attributeNewValue ) {
			// If new value is the same as the old, the operation is not applied (see the `writer.setAttributeOnItem()`).
			// OTOH the model element has the attribute already applied, so we can't compare the values.
			// Hence we need to just recreate the <colgroup> element every time.
			insertColgroupElement( viewWriter, viewTable, data.attributeNewValue );
			viewWriter.addClass( 'ck-table-resized', viewTable );
		} else {
			removeColgroupElement( viewWriter, viewTable );
			viewWriter.removeClass( 'ck-table-resized', viewTable );
		}
	} );
}

// Inserts the `<colgroup>` with `<col>` elements as the first child in the view table. Each `<col>` element represents a single column
// and it has the inline width style set, taken from the appropriate slot from the `columnWidths` table attribute.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter View writer instance.
// @param {module:engine/view/element~Element} viewTable View table.
// @param {String} columnWidthsAttribute Column widths attribute from model table.
function insertColgroupElement( viewWriter, viewTable, columnWidthsAttribute ) {
	const columnWidths = columnWidthsAttribute.split( ',' );

	let viewColgroupElement = [ ...viewTable.getChildren() ].find( viewElement => viewElement.is( 'element', 'colgroup' ) );

	if ( !viewColgroupElement ) {
		viewColgroupElement = viewWriter.createContainerElement( 'colgroup' );
	} else {
		for ( const viewChild of [ ...viewColgroupElement.getChildren() ] ) {
			viewWriter.remove( viewChild );
		}
	}

	for ( const columnIndex of Array( columnWidths.length ).keys() ) {
		const viewColElement = viewWriter.createEmptyElement( 'col' );

		viewWriter.setStyle( 'width', columnWidths[ columnIndex ], viewColElement );
		viewWriter.insert( viewWriter.createPositionAt( viewColgroupElement, 'end' ), viewColElement );
	}

	viewWriter.insert( viewWriter.createPositionAt( viewTable, 'start' ), viewColgroupElement );
}

// Removes the `<colgroup>` with `<col>` elements from the view table.
//
// @private
// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter View writer instance.
// @param {module:engine/view/element~Element} viewTable View table.
function removeColgroupElement( viewWriter, viewTable ) {
	const viewColgroupElement = [ ...viewTable.getChildren() ].find( viewElement => viewElement.is( 'element', 'colgroup' ) );

	viewWriter.remove( viewColgroupElement );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecolumnresize/tablecolumnresizeediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecolumnresize/tablecolumnresizeediting
 */



















/**
 * The table column resize editing plugin.
 *
 * @extends module:core/plugin~Plugin
 */
class TableColumnResizeEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableEditing, TableUtils ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableColumnResizeEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * A flag indicating if the column resizing is in progress.
		 *
		 * @private
		 * @member {Boolean}
		 */
		this._isResizingActive = false;

		/**
		 * A flag indicating if the column resizing is allowed. It is not allowed if the editor is in read-only
		 * or comments-only mode or the `TableColumnResize` plugin is disabled.
		 *
		 * @private
		 * @observable
		 * @member {Boolean}
		 */
		this.set( '_isResizingAllowed', true );

		/**
		 * A temporary storage for the required data needed to correctly calculate the widths of the resized columns. This storage is
		 * initialized when column resizing begins, and is purged upon completion.
		 *
		 * @private
		 * @member {Object|null}
		 */
		this._resizingData = null;

		/**
		 * DOM emitter.
		 *
		 * @private
		 * @member {DomEmitterMixin}
		 */
		this._domEmitter = Object.create( DomEmitterMixin );

		/**
		 * A local reference to the {@link module:table/tableutils~TableUtils} plugin.
		 *
		 * @private
		 * @member {module:table/tableutils~TableUtils}
		 */
		this._tableUtilsPlugin = editor.plugins.get( 'TableUtils' );

		this.on( 'change:_isResizingAllowed', ( evt, name, value ) => {
			// Toggling the `ck-column-resize_disabled` class shows and hides the resizers through CSS.
			editor.editing.view.change( writer => {
				writer[ value ? 'removeClass' : 'addClass' ]( 'ck-column-resize_disabled', editor.editing.view.document.getRoot() );
			} );
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		this._extendSchema();
		this._registerPostFixer();
		this._registerConverters();
		this._registerResizingListeners();
		this._registerColgroupFixer();
		this._registerResizerInserter();

		const editor = this.editor;
		const columnResizePlugin = editor.plugins.get( 'TableColumnResize' );

		editor.commands.add( 'resizeTableWidth', new TableWidthResizeCommand( editor ) );
		editor.commands.add( 'resizeColumnWidths', new TableColumnWidthsCommand( editor ) );

		const resizeTableWidthCommand = editor.commands.get( 'resizeTableWidth' );
		const resizeColumnWidthsCommand = editor.commands.get( 'resizeColumnWidths' );

		// Currently the states of column resize and table resize (which is actually the last column resize) features
		// are bound together. They can be separated in the future by adding distinct listeners and applying
		// different CSS classes (e.g. `ck-column-resize_disabled` and `ck-table-resize_disabled`) to the editor root.
		// See #12148 for the details.
		this.bind( '_isResizingAllowed' ).to(
			editor, 'isReadOnly',
			columnResizePlugin, 'isEnabled',
			resizeTableWidthCommand, 'isEnabled',
			resizeColumnWidthsCommand, 'isEnabled',
			( isEditorReadOnly, isPluginEnabled, isResizeTableWidthCommandEnabled, isResizeColumnWidthsCommandEnabled ) =>
				!isEditorReadOnly && isPluginEnabled && isResizeTableWidthCommandEnabled && isResizeColumnWidthsCommandEnabled
		);
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		this._domEmitter.stopListening();
		super.destroy();
	}

	/**
	 * Registers new attributes for a table model element.
	 *
	 * @private
	 */
	_extendSchema() {
		this.editor.model.schema.extend( 'table', {
			allowAttributes: [ 'tableWidth', 'columnWidths' ]
		} );
	}

	/**
	 * Registers table column resize post-fixer.
	 *
	 * It checks if the change from the differ concerns a table-related element or attribute. For detected changes it:
	 *  * Adjusts the `columnWidths` attribute to guarantee that the sum of the widths from all columns is 100%.
	 *  * Checks if the `columnWidths` attribute gets updated accordingly after columns have been added or removed.
	 *
	 * @private
	 */
	_registerPostFixer() {
		const editor = this.editor;
		const model = editor.model;

		model.document.registerPostFixer( writer => {
			let changed = false;

			for ( const table of getChangedResizedTables( model ) ) {
				// (1) Adjust the `columnWidths` attribute to guarantee that the sum of the widths from all columns is 100%.
				const columnWidths = normalizeColumnWidths( table.getAttribute( 'columnWidths' ).split( ',' ) );

				// (2) If the number of columns has changed, then we need to adjust the widths of the affected columns.
				adjustColumnWidths( columnWidths, table, this );

				const columnWidthsAttribute = columnWidths.map( width => `${ width }%` ).join( ',' );

				if ( table.getAttribute( 'columnWidths' ) === columnWidthsAttribute ) {
					continue;
				}

				writer.setAttribute( 'columnWidths', columnWidthsAttribute, table );

				changed = true;
			}

			return changed;
		} );

		// Adjusts if necessary the `columnWidths` in case if the number of column has changed.
		//
		// @private
		// @param {Array.<Number>} columnWidths Note: this array **may be modified** by the function.
		// @param {module:engine/model/element~Element} table Table to be checked.
		// @param {module:table/tablecolumnresize/tablecolumnresizeediting~TableColumnResizeEditing} plugin
		function adjustColumnWidths( columnWidths, table, plugin ) {
			const newTableColumnsCount = plugin._tableUtilsPlugin.getColumns( table );
			const columnsCountDelta = newTableColumnsCount - columnWidths.length;

			if ( columnsCountDelta === 0 ) {
				return;
			}

			// Collect all cells that are affected by the change.
			const cellSet = getAffectedCells( plugin.editor.model.document.differ, table );

			for ( const cell of cellSet ) {
				const currentColumnsDelta = newTableColumnsCount - columnWidths.length;

				if ( currentColumnsDelta === 0 ) {
					continue;
				}

				// If the column count in the table changed, adjust the widths of the affected columns.
				const hasMoreColumns = currentColumnsDelta > 0;
				const currentColumnIndex = plugin._tableUtilsPlugin.getCellLocation( cell ).column;

				if ( hasMoreColumns ) {
					const columnMinWidthAsPercentage = getColumnMinWidthAsPercentage( table, plugin.editor );
					const columnWidthsToInsert = createFilledArray( currentColumnsDelta, columnMinWidthAsPercentage );

					columnWidths.splice( currentColumnIndex, 0, ...columnWidthsToInsert );
				} else {
					// Moves the widths of the removed columns to the preceding one.
					// Other editors either reduce the width of the whole table or adjust the widths
					// proportionally, so change of this behavior can be considered in the future.
					const removedColumnWidths = columnWidths.splice( currentColumnIndex, Math.abs( currentColumnsDelta ) );

					columnWidths[ currentColumnIndex ] += sumArray( removedColumnWidths );
				}
			}
		}

		// Returns a set of cells that have been changed in a given table.
		//
		// @private
		// @param {module:engine/model/differ~Differ} differ
		// @param {module:engine/model/element~Element} table
		// @returns {Set.<module:engine/model/element~Element>}
		function getAffectedCells( differ, table ) {
			const cellSet = new Set();

			for ( const change of differ.getChanges() ) {
				if (
					change.type == 'insert' &&
					change.position.nodeAfter &&
					change.position.nodeAfter.name == 'tableCell' &&
					change.position.nodeAfter.getAncestors().includes( table )
				) {
					cellSet.add( change.position.nodeAfter );
				} else if ( change.type == 'remove' ) {
					// If the first cell was removed, use the node after the change position instead.
					const referenceNode = change.position.nodeBefore || change.position.nodeAfter;

					if ( referenceNode.name == 'tableCell' && referenceNode.getAncestors().includes( table ) ) {
						cellSet.add( referenceNode );
					}
				}
			}

			return cellSet;
		}
	}

	/**
	 * Registers table column resize converters.
	 *
	 * @private
	 */
	_registerConverters() {
		const editor = this.editor;
		const conversion = editor.conversion;
		const widthStyleToTableWidthDefinition = {
			view: {
				name: 'figure',
				key: 'style',
				value: {
					width: /[\s\S]+/
				}
			},
			model: {
				name: 'table',
				key: 'tableWidth',
				value: viewElement => viewElement.getStyle( 'width' )
			}
		};
		const tableWidthToWidthStyleDefinition = {
			model: {
				name: 'table',
				key: 'tableWidth'
			},
			view: width => ( {
				name: 'figure',
				key: 'style',
				value: {
					width
				}
			} )
		};

		conversion.for( 'upcast' ).attributeToAttribute( widthStyleToTableWidthDefinition );
		conversion.for( 'upcast' ).add( upcastColgroupElement( this._tableUtilsPlugin ) );

		conversion.for( 'downcast' ).attributeToAttribute( tableWidthToWidthStyleDefinition );
		conversion.for( 'downcast' ).add( downcastTableColumnWidthsAttribute() );
	}

	/**
	 * Registers listeners to handle resizing process.
	 *
	 * @private
	 */
	_registerResizingListeners() {
		const editingView = this.editor.editing.view;

		editingView.addObserver( MouseEventsObserver );
		editingView.document.on( 'mousedown', this._onMouseDownHandler.bind( this ), { priority: 'high' } );

		this._domEmitter.listenTo( dom_global.window.document, 'mousemove', lodash_es_throttle( this._onMouseMoveHandler.bind( this ), 50 ) );
		this._domEmitter.listenTo( dom_global.window.document, 'mouseup', this._onMouseUpHandler.bind( this ) );
	}

	/**
	 * Handles the `mousedown` event on column resizer element:
	 *  * calculates the initial column pixel widths,
	 *  * inserts the `<colgroup>` element if it is not present in the `<table>`,
	 *  * puts the necessary data in the temporary storage,
	 *  * applies the attributes to the `<table>` view element.
	 *
	 * @private
	 * @param {module:utils/eventinfo~EventInfo} eventInfo An object containing information about the fired event.
	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData The data related to the DOM event.
	 */
	_onMouseDownHandler( eventInfo, domEventData ) {
		const target = domEventData.target;

		if ( !target.hasClass( 'ck-table-column-resizer' ) ) {
			return;
		}

		if ( !this._isResizingAllowed ) {
			return;
		}

		domEventData.preventDefault();
		eventInfo.stop();

		const editor = this.editor;
		const modelTable = editor.editing.mapper.toModelElement( target.findAncestor( 'figure' ) );

		// The column widths are calculated upon mousedown to allow lazy applying the `columnWidths` attribute on the table.
		const columnWidthsInPx = _calculateDomColumnWidths( modelTable, this._tableUtilsPlugin, editor );
		const viewTable = target.findAncestor( 'table' );
		const editingView = editor.editing.view;

		// Insert colgroup for the table that is resized for the first time.
		if ( ![ ...viewTable.getChildren() ].find( viewCol => viewCol.is( 'element', 'colgroup' ) ) ) {
			editingView.change( viewWriter => {
				_insertColgroupElement( viewWriter, columnWidthsInPx, viewTable );
			} );
		}

		this._isResizingActive = true;
		this._resizingData = this._getResizingData( domEventData, columnWidthsInPx );

		// At this point we change only the editor view - we don't want other users to see our changes yet,
		// so we can't apply them in the model.
		editingView.change( writer => _applyResizingAttributesToTable( writer, viewTable, this._resizingData ) );

		// Calculates the DOM columns' widths. It is done by taking the width of the widest cell
		// from each table column (we rely on the  {@link module:table/tablewalker~TableWalker}
		// to determine which column the cell belongs to).
		//
		// @private
		// @param {module:engine/model/element~Element} modelTable A table which columns should be measured.
		// @param {module:table/tableutils~TableUtils} tableUtils The Table Utils plugin instance.
		// @param {module:core/editor/editor~Editor} editor The editor instance.
		// @returns {Array.<Number>} Columns' widths expressed in pixels (without unit).
		function _calculateDomColumnWidths( modelTable, tableUtilsPlugin, editor ) {
			const columnWidthsInPx = Array( tableUtilsPlugin.getColumns( modelTable ) );
			const tableWalker = new TableWalker( modelTable );

			for ( const cellSlot of tableWalker ) {
				const viewCell = editor.editing.mapper.toViewElement( cellSlot.cell );
				const domCell = editor.editing.view.domConverter.mapViewToDom( viewCell );
				const domCellWidth = getDomCellOuterWidth( domCell );

				if ( !columnWidthsInPx[ cellSlot.column ] || domCellWidth < columnWidthsInPx[ cellSlot.column ] ) {
					columnWidthsInPx[ cellSlot.column ] = toPrecision( domCellWidth );
				}
			}

			return columnWidthsInPx;
		}

		// Creates a `<colgroup>` element with `<col>`s and inserts it into a given view table.
		//
		// @private
		// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter A writer instance.
		// @param {Array.<Number>} columnWidthsInPx Column widths.
		// @param {module:engine/view/element~Element} viewTable A table view element.
		function _insertColgroupElement( viewWriter, columnWidthsInPx, viewTable ) {
			const colgroup = viewWriter.createContainerElement( 'colgroup' );

			for ( let i = 0; i < columnWidthsInPx.length; i++ ) {
				const viewColElement = viewWriter.createEmptyElement( 'col' );
				const columnWidthInPc = `${ toPrecision( columnWidthsInPx[ i ] / sumArray( columnWidthsInPx ) * 100 ) }%`;

				viewWriter.setStyle( 'width', columnWidthInPc, viewColElement );
				viewWriter.insert( viewWriter.createPositionAt( colgroup, 'end' ), viewColElement );
			}

			viewWriter.insert( viewWriter.createPositionAt( viewTable, 'start' ), colgroup );
		}

		// Applies the style and classes to the view table as the resizing begun.
		//
		// @private
		// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter A writer instance.
		// @param {module:engine/view/element~Element} viewTable A table containing the clicked resizer.
		// @param {Object} resizingData Data related to the resizing.
		function _applyResizingAttributesToTable( viewWriter, viewTable, resizingData ) {
			const figureInitialPcWidth = resizingData.widths.viewFigureWidth / resizingData.widths.viewFigureParentWidth;

			viewWriter.addClass( 'ck-table-resized', viewTable );
			viewWriter.addClass( 'ck-table-column-resizer__active', resizingData.elements.viewResizer );
			viewWriter.setStyle( 'width', `${ toPrecision( figureInitialPcWidth * 100 ) }%`, viewTable.findAncestor( 'figure' ) );
		}
	}

	/**
	 * Handles the `mousemove` event.
	 *  * If resizing process is not in progress, it does nothing.
	 *  * If resizing is active but not allowed, it stops the resizing process instantly calling the `mousedown` event handler.
	 *  * Otherwise it dynamically updates the widths of the resized columns.
	 *
	 * @private
	 * @param {module:utils/eventinfo~EventInfo} eventInfo An object containing information about the fired event.
	 * @param {Event} mouseEventData The native DOM event.
	 */
	_onMouseMoveHandler( eventInfo, mouseEventData ) {
		if ( !this._isResizingActive ) {
			return;
		}

		if ( !this._isResizingAllowed ) {
			this._onMouseUpHandler();

			return;
		}

		const {
			columnPosition,
			flags: {
				isRightEdge,
				isTableCentered,
				isLtrContent
			},
			elements: {
				viewFigure,
				viewLeftColumn,
				viewRightColumn
			},
			widths: {
				viewFigureParentWidth,
				tableWidth,
				leftColumnWidth,
				rightColumnWidth
			}
		} = this._resizingData;

		const dxLowerBound = -leftColumnWidth + COLUMN_MIN_WIDTH_IN_PIXELS;

		const dxUpperBound = isRightEdge ?
			viewFigureParentWidth - tableWidth :
			rightColumnWidth - COLUMN_MIN_WIDTH_IN_PIXELS;

		// The multiplier is needed for calculating the proper movement offset:
		// - it should negate the sign if content language direction is right-to-left,
		// - it should double the offset if the table edge is resized and table is centered.
		const multiplier = ( isLtrContent ? 1 : -1 ) * ( isRightEdge && isTableCentered ? 2 : 1 );

		const dx = clamp(
			( mouseEventData.clientX - columnPosition ) * multiplier,
			Math.min( dxLowerBound, 0 ),
			Math.max( dxUpperBound, 0 )
		);

		if ( dx === 0 ) {
			return;
		}

		this.editor.editing.view.change( writer => {
			const leftColumnWidthAsPercentage = toPrecision( ( leftColumnWidth + dx ) * 100 / tableWidth );

			writer.setStyle( 'width', `${ leftColumnWidthAsPercentage }%`, viewLeftColumn );

			if ( isRightEdge ) {
				const tableWidthAsPercentage = toPrecision( ( tableWidth + dx ) * 100 / viewFigureParentWidth );

				writer.setStyle( 'width', `${ tableWidthAsPercentage }%`, viewFigure );
			} else {
				const rightColumnWidthAsPercentage = toPrecision( ( rightColumnWidth - dx ) * 100 / tableWidth );

				writer.setStyle( 'width', `${ rightColumnWidthAsPercentage }%`, viewRightColumn );
			}
		} );
	}

	/**
	 * Handles the `mouseup` event.
	 *  * If resizing process is not in progress, it does nothing.
	 *  * If resizing is active but not allowed, it cancels the resizing process restoring the original widths.
	 *  * Otherwise it propagates the changes from view to the model by executing the adequate commands.
	 *
	 * @private
	 */
	_onMouseUpHandler() {
		if ( !this._isResizingActive ) {
			return;
		}

		const {
			viewResizer,
			modelTable,
			viewFigure,
			viewColgroup
		} = this._resizingData.elements;

		const editor = this.editor;
		const editingView = editor.editing.view;

		const columnWidthsAttributeOld = modelTable.getAttribute( 'columnWidths' );
		const columnWidthsAttributeNew = [ ...viewColgroup.getChildren() ]
			.map( viewCol => viewCol.getStyle( 'width' ) )
			.join( ',' );

		const isColumnWidthsAttributeChanged = columnWidthsAttributeOld !== columnWidthsAttributeNew;

		const tableWidthAttributeOld = modelTable.getAttribute( 'tableWidth' );
		const tableWidthAttributeNew = viewFigure.getStyle( 'width' );

		const isTableWidthAttributeChanged = tableWidthAttributeOld !== tableWidthAttributeNew;

		if ( isColumnWidthsAttributeChanged || isTableWidthAttributeChanged ) {
			if ( this._isResizingAllowed ) {
				// Commit all changes to the model.
				if ( isTableWidthAttributeChanged ) {
					editor.execute(
						'resizeTableWidth',
						{
							table: modelTable,
							tableWidth: `${ toPrecision( tableWidthAttributeNew ) }%`,
							columnWidths: columnWidthsAttributeNew
						}
					);
				} else {
					editor.execute( 'resizeColumnWidths', { columnWidths: columnWidthsAttributeNew, table: modelTable } );
				}
			} else {
				// In read-only mode revert all changes in the editing view. The model is not touched so it does not need to be restored.
				// This case can occur if the read-only mode kicks in during the resizing process.
				editingView.change( writer => {
					// If table had resized columns before, restore the previous column widths.
					// Otherwise clean up the view from the temporary column resizing markup.
					if ( columnWidthsAttributeOld ) {
						const columnWidths = columnWidthsAttributeOld.split( ',' );

						for ( const viewCol of viewColgroup.getChildren() ) {
							writer.setStyle( 'width', columnWidths.shift(), viewCol );
						}
					} else {
						writer.remove( viewColgroup );
					}

					if ( isTableWidthAttributeChanged ) {
						// If the whole table was already resized before, restore the previous table width.
						// Otherwise clean up the view from the temporary table resizing markup.
						if ( tableWidthAttributeOld ) {
							writer.setStyle( 'width', tableWidthAttributeOld, viewFigure );
						} else {
							writer.removeStyle( 'width', viewFigure );
						}
					}

					// If a table and its columns weren't resized before,
					// prune the remaining common resizing markup.
					if ( !columnWidthsAttributeOld && !tableWidthAttributeOld ) {
						writer.removeClass(
							'ck-table-resized',
							[ ...viewFigure.getChildren() ].find( element => element.name === 'table' )
						);
					}
				} );
			}
		}

		editingView.change( writer => {
			writer.removeClass( 'ck-table-column-resizer__active', viewResizer );
		} );

		this._isResizingActive = false;
		this._resizingData = null;
	}

	/**
	 * Retrieves and returns required data needed for the resizing process.
	 *
	 * @private
	 * @param {module:engine/view/observer/domeventdata~DomEventData} domEventData The data of the `mousedown` event.
	 * @param {Array.<Number>} columnWidths The current widths of the columns.
	 * @returns {Object} The data needed for the resizing process.
	 */
	_getResizingData( domEventData, columnWidths ) {
		const editor = this.editor;

		const columnPosition = domEventData.domEvent.clientX;

		const viewResizer = domEventData.target;
		const viewLeftCell = viewResizer.findAncestor( 'td' ) || viewResizer.findAncestor( 'th' );
		const modelLeftCell = editor.editing.mapper.toModelElement( viewLeftCell );
		const modelTable = modelLeftCell.findAncestor( 'table' );

		const leftColumnIndex = getColumnEdgesIndexes( modelLeftCell, this._tableUtilsPlugin ).rightEdge;
		const lastColumnIndex = this._tableUtilsPlugin.getColumns( modelTable ) - 1;

		const isRightEdge = leftColumnIndex === lastColumnIndex;
		const isTableCentered = !modelTable.hasAttribute( 'tableAlignment' );
		const isLtrContent = editor.locale.contentLanguageDirection !== 'rtl';

		const viewTable = viewLeftCell.findAncestor( 'table' );
		const viewFigure = viewTable.findAncestor( 'figure' );
		const viewColgroup = [ ...viewTable.getChildren() ].find( viewCol => viewCol.is( 'element', 'colgroup' ) );
		const viewLeftColumn = viewColgroup.getChild( leftColumnIndex );
		const viewRightColumn = isRightEdge ? undefined : viewColgroup.getChild( leftColumnIndex + 1 );

		const viewFigureParentWidth = getElementWidthInPixels( editor.editing.view.domConverter.mapViewToDom( viewFigure.parent ) );
		const viewFigureWidth = getElementWidthInPixels( editor.editing.view.domConverter.mapViewToDom( viewFigure ) );
		const tableWidth = getTableWidthInPixels( modelTable, editor );
		const leftColumnWidth = columnWidths[ leftColumnIndex ];
		const rightColumnWidth = isRightEdge ? undefined : columnWidths[ leftColumnIndex + 1 ];

		return {
			columnPosition,
			flags: {
				isRightEdge,
				isTableCentered,
				isLtrContent
			},
			elements: {
				viewResizer,
				modelTable,
				viewFigure,
				viewColgroup,
				viewLeftColumn,
				viewRightColumn
			},
			widths: {
				viewFigureParentWidth,
				viewFigureWidth,
				tableWidth,
				leftColumnWidth,
				rightColumnWidth
			}
		};
	}

	/**
	 * Inserts the `<colgroup>` element if it is missing in the view table (e.g. after table insertion into table).
	 *
	 * @private
	 */
	_registerColgroupFixer() {
		const editor = this.editor;

		this.listenTo( editor.editing.view.document, 'layoutChanged', () => {
			const viewTable = editor.editing.view.document.selection.getFirstPosition().getAncestors().reverse().find(
				viewElement => viewElement.name === 'table'
			);
			const viewTableContainsColgroup = viewTable && [ ...viewTable.getChildren() ].find(
				viewElement => viewElement.is( 'element', 'colgroup' )
			);
			const modelTable = editor.model.document.selection.getFirstPosition().findAncestor( 'table' );

			if ( modelTable && modelTable.hasAttribute( 'columnWidths' ) && viewTable && !viewTableContainsColgroup ) {
				editor.editing.reconvertItem( modelTable );
			}
		}, { priority: 'low' } );
	}

	/**
	 * Registers a listener ensuring that each resizable cell have a resizer handle.
	 *
	 * @private
	 */
	_registerResizerInserter() {
		const view = this.editor.editing.view;

		view.on( 'render', () => {
			for ( const item of view.createRangeIn( view.document.getRoot() ) ) {
				if ( ![ 'td', 'th' ].includes( item.item.name ) ) {
					continue;
				}

				view.change( viewWriter => {
					ensureColumnResizerElement( viewWriter, item.item );
				} );
			}
		}, { priority: 'lowest' } );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/tablecolumnresize.css
var tablecolumnresize = __webpack_require__(728);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/tablecolumnresize.css

            

var tablecolumnresize_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tablecolumnresize_options.insert = "head";
tablecolumnresize_options.singleton = true;

var tablecolumnresize_update = injectStylesIntoStyleTag_default()(tablecolumnresize/* default */.Z, tablecolumnresize_options);



/* harmony default export */ const theme_tablecolumnresize = (tablecolumnresize/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tablecolumnresize.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tablecolumnresize
 */







/**
 * The table column resize feature.
 *
 * It provides the possibility to set the width of each column in a table using a resize handler.
 *
 * @extends module:core/plugin~Plugin
 */
class TableColumnResize extends plugin_Plugin {
	/**
	 * @inheritDoc
 	 */
	static get requires() {
		return [ TableColumnResizeEditing, TableCellWidthEditing ];
	}

	/**
	 * @inheritDoc
 	 */
	static get pluginName() {
		return 'TableColumnResize';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tablebackgroundcolorcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tablebackgroundcolorcommand
 */



/**
 * The table background color command.
 *
 * The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
 * the `'tableBackgroundColor'` editor command.
 *
 * To change the background color of the selected table, execute the command:
 *
 *		editor.execute( 'tableBackgroundColor', {
 *			value: '#f00'
 *		} );
 *
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableBackgroundColorCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableBackgroundColorCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableBackgroundColor', defaultValue );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tablebordercolorcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tablebordercolorcommand
 */




/**
 * The table border color command.
 *
 * The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
 * the `'tableBorderColor'` editor command.
 *
 * To change the border color of the selected table, execute the command:
 *
 *		editor.execute( 'tableBorderColor', {
 *			value: '#f00'
 *		} );
 *
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableBorderColorCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableBorderColorCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableBorderColor', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getValue( table ) {
		if ( !table ) {
			return;
		}

		const value = getSingleValue( table.getAttribute( this.attributeName ) );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tableborderstylecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tableborderstylecommand
 */




/**
 * The table style border command.
 *
 * The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
 * the `'tableBorderStyle'` editor command.
 *
 * To change the border style of the selected table, execute the command:
 *
 *		editor.execute( 'tableBorderStyle', {
 *			value: 'dashed'
 *		} );
 *
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableBorderStyleCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableBorderStyleCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableBorderStyle', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getValue( table ) {
		if ( !table ) {
			return;
		}

		const value = getSingleValue( table.getAttribute( this.attributeName ) );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tableborderwidthcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tableborderwidthcommand
 */




/**
 * The table width border command.
 *
 * The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
 * the `'tableBorderWidth'` editor command.
 *
 * To change the border width of the selected table, execute the command:
 *
 *		editor.execute( 'tableBorderWidth', {
 *			value: '5px'
 *		} );
 *
 * **Note**: This command adds the default `'px'` unit to numeric values. Executing:
 *
 *		editor.execute( 'tableBorderWidth', {
 *			value: '5'
 *		} );
 *
 * will set the `borderWidth` attribute to `'5px'` in the model.
 *
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableBorderWidthCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableBorderWidthCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableBorderWidth', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getValue( table ) {
		if ( !table ) {
			return;
		}

		const value = getSingleValue( table.getAttribute( this.attributeName ) );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}

	/**
	 * @inheritDoc
	 */
	_getValueToSet( value ) {
		value = addDefaultUnitToNumericValue( value, 'px' );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tablewidthcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tablewidthcommand
 */




/**
 * The table width command.
 *
 * The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
 * the `'tableWidth'` editor command.
 *
 * To change the width of the selected table, execute the command:
 *
 *		editor.execute( 'tableWidth', {
 *			value: '400px'
 *		} );
 *
 * **Note**: This command adds the default `'px'` unit to numeric values. Executing:
 *
 *		editor.execute( 'tableWidth', {
 *			value: '50'
 *		} );
 *
 * will set the `width` attribute to `'50px'` in the model.
 *
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableWidthCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableWidthCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableWidth', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getValueToSet( value ) {
		value = addDefaultUnitToNumericValue( value, 'px' );

		if ( value === this._defaultValue ) {
			return;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tableheightcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tableheightcommand
 */




/**
 * The table height command.
 *
 * The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
 * the `'tableHeight'` editor command.
 *
 * To change the height of the selected table, execute the command:
 *
 *		editor.execute( 'tableHeight', {
 *			value: '500px'
 *		} );
 *
 * **Note**: This command adds the default `'px'` unit to numeric values. Executing:
 *
 *		editor.execute( 'tableHeight', {
 *			value: '50'
 *		} );
 *
 * will set the `height` attribute to `'50px'` in the model.
 *
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableHeightCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableHeightCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value of the attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableHeight', defaultValue );
	}

	/**
	 * @inheritDoc
	 */
	_getValueToSet( value ) {
		value = addDefaultUnitToNumericValue( value, 'px' );

		if ( value === this._defaultValue ) {
			return null;
		}

		return value;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/commands/tablealignmentcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/commands/tablealignmentcommand
 */



/**
 * The table alignment command.
 *
 * The command is registered by the {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing} as
 * the `'tableAlignment'` editor command.
 *
 * To change the alignment of the selected table, execute the command:
 *
 *		editor.execute( 'tableAlignment', {
 *			value: 'right'
 *		} );
 *
 * @extends module:table/tableproperties/commands/tablepropertycommand~TablePropertyCommand
 */
class TableAlignmentCommand extends TablePropertyCommand {
	/**
	 * Creates a new `TableAlignmentCommand` instance.
	 *
	 * @param {module:core/editor/editor~Editor} editor An editor in which this command will be used.
	 * @param {String} defaultValue The default value for the "alignment" attribute.
	 */
	constructor( editor, defaultValue ) {
		super( editor, 'tableAlignment', defaultValue );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/tablepropertiesediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/tablepropertiesediting
 */















const tablepropertiesediting_ALIGN_VALUES_REG_EXP = /^(left|center|right)$/;
const FLOAT_VALUES_REG_EXP = /^(left|none|right)$/;

/**
 * The table properties editing feature.
 *
 * Introduces table's model attributes and their conversion:
 *
 * - border: `tableBorderStyle`, `tableBorderColor` and `tableBorderWidth`
 * - background color: `tableBackgroundColor`
 * - horizontal alignment: `tableAlignment`
 * - width & height: `tableWidth` & `tableHeight`
 *
 * It also registers commands used to manipulate the above attributes:
 *
 * - border: `'tableBorderStyle'`, `'tableBorderColor'` and `'tableBorderWidth'` commands
 * - background color: `'tableBackgroundColor'`
 * - horizontal alignment: `'tableAlignment'`
 * - width & height: `'tableWidth'` & `'tableHeight'`
 *
 * @extends module:core/plugin~Plugin
 */
class TablePropertiesEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TablePropertiesEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TableEditing ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const schema = editor.model.schema;
		const conversion = editor.conversion;

		editor.config.define( 'table.tableProperties.defaultProperties', {} );

		const defaultTableProperties = getNormalizedDefaultProperties( editor.config.get( 'table.tableProperties.defaultProperties' ), {
			includeAlignmentProperty: true
		} );

		editor.data.addStyleProcessorRules( addBorderRules );
		tablepropertiesediting_enableBorderProperties( schema, conversion, {
			color: defaultTableProperties.borderColor,
			style: defaultTableProperties.borderStyle,
			width: defaultTableProperties.borderWidth
		} );
		editor.commands.add( 'tableBorderColor', new TableBorderColorCommand( editor, defaultTableProperties.borderColor ) );
		editor.commands.add( 'tableBorderStyle', new TableBorderStyleCommand( editor, defaultTableProperties.borderStyle ) );
		editor.commands.add( 'tableBorderWidth', new TableBorderWidthCommand( editor, defaultTableProperties.borderWidth ) );

		enableAlignmentProperty( schema, conversion, defaultTableProperties.alignment );
		editor.commands.add( 'tableAlignment', new TableAlignmentCommand( editor, defaultTableProperties.alignment ) );

		enableTableToFigureProperty( schema, conversion, {
			modelAttribute: 'tableWidth',
			styleName: 'width',
			defaultValue: defaultTableProperties.width
		} );
		editor.commands.add( 'tableWidth', new TableWidthCommand( editor, defaultTableProperties.width ) );

		enableTableToFigureProperty( schema, conversion, {
			modelAttribute: 'tableHeight',
			styleName: 'height',
			defaultValue: defaultTableProperties.height
		} );
		editor.commands.add( 'tableHeight', new TableHeightCommand( editor, defaultTableProperties.height ) );

		editor.data.addStyleProcessorRules( addBackgroundRules );
		tablepropertiesediting_enableProperty( schema, conversion, {
			modelAttribute: 'tableBackgroundColor',
			styleName: 'background-color',
			defaultValue: defaultTableProperties.backgroundColor
		} );
		editor.commands.add(
			'tableBackgroundColor',
			new TableBackgroundColorCommand( editor, defaultTableProperties.backgroundColor )
		);
	}
}

// Enables `tableBorderStyle'`, `tableBorderColor'` and `tableBorderWidth'` attributes for table.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/conversion/conversion~Conversion} conversion
// @param {Object} defaultBorder The default border values.
// @param {String} defaultBorder.color The default `tableBorderColor` value.
// @param {String} defaultBorder.style The default `tableBorderStyle` value.
// @param {String} defaultBorder.width The default `tableBorderWidth` value.
function tablepropertiesediting_enableBorderProperties( schema, conversion, defaultBorder ) {
	const modelAttributes = {
		width: 'tableBorderWidth',
		color: 'tableBorderColor',
		style: 'tableBorderStyle'
	};

	schema.extend( 'table', {
		allowAttributes: Object.values( modelAttributes )
	} );

	upcastBorderStyles( conversion, 'table', modelAttributes, defaultBorder );

	downcastTableAttribute( conversion, { modelAttribute: modelAttributes.color, styleName: 'border-color' } );
	downcastTableAttribute( conversion, { modelAttribute: modelAttributes.style, styleName: 'border-style' } );
	downcastTableAttribute( conversion, { modelAttribute: modelAttributes.width, styleName: 'border-width' } );
}

// Enables the `'alignment'` attribute for table.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/conversion/conversion~Conversion} conversion
// @param {String} defaultValue The default alignment value.
function enableAlignmentProperty( schema, conversion, defaultValue ) {
	schema.extend( 'table', {
		allowAttributes: [ 'tableAlignment' ]
	} );

	conversion.for( 'downcast' )
		.attributeToAttribute( {
			model: {
				name: 'table',
				key: 'tableAlignment'
			},
			view: alignment => ( {
				key: 'style',
				value: {
					// Model: `alignment:center` => CSS: `float:none`.
					float: alignment === 'center' ? 'none' : alignment
				}
			} ),
			converterPriority: 'high'
		} );

	conversion.for( 'upcast' )
		// Support for the `float:*;` CSS definition for the table alignment.
		.attributeToAttribute( {
			view: {
				name: /^(table|figure)$/,
				styles: {
					float: FLOAT_VALUES_REG_EXP
				}
			},
			model: {
				key: 'tableAlignment',
				value: viewElement => {
					let align = viewElement.getStyle( 'float' );

					// CSS: `float:none` => Model: `alignment:center`.
					if ( align === 'none' ) {
						align = 'center';
					}

					return align === defaultValue ? null : align;
				}
			}
		} )
		// Support for the `align` attribute as the backward compatibility while pasting from other sources.
		.attributeToAttribute( {
			view: {
				attributes: {
					align: tablepropertiesediting_ALIGN_VALUES_REG_EXP
				}
			},
			model: {
				name: 'table',
				key: 'tableAlignment',
				value: viewElement => {
					const align = viewElement.getAttribute( 'align' );

					return align === defaultValue ? null : align;
				}
			}
		} );
}

// Enables conversion for an attribute for simple view-model mappings.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/conversion/conversion~Conversion} conversion
// @param {Object} options
// @param {String} options.modelAttribute
// @param {String} options.styleName
// @param {String} options.defaultValue The default value for the specified `modelAttribute`.
function tablepropertiesediting_enableProperty( schema, conversion, options ) {
	const { modelAttribute } = options;

	schema.extend( 'table', {
		allowAttributes: [ modelAttribute ]
	} );
	upcastStyleToAttribute( conversion, { viewElement: 'table', ...options } );
	downcastTableAttribute( conversion, options );
}

// Enables conversion for an attribute for simple view (figure) to model (table) mappings.
//
// @param {module:engine/model/schema~Schema} schema
// @param {module:engine/conversion/conversion~Conversion} conversion
// @param {Object} options
// @param {String} options.modelAttribute
// @param {String} options.styleName
function enableTableToFigureProperty( schema, conversion, options ) {
	const { modelAttribute } = options;

	schema.extend( 'table', {
		allowAttributes: [ modelAttribute ]
	} );
	upcastStyleToAttribute( conversion, { viewElement: /^(table|figure)$/, ...options } );
	downcastAttributeToStyle( conversion, { modelElement: 'table', ...options } );
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-table/theme/tableproperties.css
var tableproperties = __webpack_require__(9221);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/tableproperties.css

            

var tableproperties_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

tableproperties_options.insert = "head";
tableproperties_options.singleton = true;

var tableproperties_update = injectStylesIntoStyleTag_default()(tableproperties/* default */.Z, tableproperties_options);



/* harmony default export */ const theme_tableproperties = (tableproperties/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/ui/tablepropertiesview.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/ui/tablepropertiesview
 */












const tablepropertiesview_ALIGNMENT_ICONS = {
	left: icons.objectLeft,
	center: icons.objectCenter,
	right: icons.objectRight
};

/**
 * The class representing a table properties form, allowing users to customize
 * certain style aspects of a table, for instance, border, background color, alignment, etc..
 *
 * @extends module:ui/view~View
 */
class TablePropertiesView extends src_view_View {
	/**
	 * @param {module:utils/locale~Locale} locale The {@link module:core/editor/editor~Editor#locale} instance.
	 * @param {Object} options Additional configuration of the view.
	 * @param {module:table/table~TableColorConfig} options.borderColors A configuration of the border
	 * color palette used by the
	 * {@link module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView#borderColorInput}.
	 * @param {module:table/table~TableColorConfig} options.backgroundColors A configuration of the background
	 * color palette used by the
	 * {@link module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView#backgroundInput}.
	 * @param {module:table/tableproperties~TablePropertiesOptions} options.defaultTableProperties The default table properties.
	 */
	constructor( locale, options ) {
		super( locale );

		this.set( {
			/**
			 * The value of the border style.
			 *
			 * @observable
			 * @default ''
			 * @member #borderStyle
			 */
			borderStyle: '',

			/**
			 * The value of the border width style.
			 *
			 * @observable
			 * @default ''
			 * @member #borderWidth
			 */
			borderWidth: '',

			/**
			 * The value of the border color style.
			 *
			 * @observable
			 * @default ''
			 * @member #borderColor
			 */
			borderColor: '',

			/**
			 * The value of the background color style.
			 *
			 * @observable
			 * @default ''
			 * @member #backgroundColor
			 */
			backgroundColor: '',

			/**
			 * The value of the table width style.
			 *
			 * @observable
			 * @default ''
			 * @member #width
			 */
			width: '',

			/**
			 * The value of the table height style.
			 *
			 * @observable
			 * @default ''
			 * @member #height
			 */
			height: '',

			/**
			 * The value of the table alignment style.
			 *
			 * @observable
			 * @default ''
			 * @member #alignment
			 */
			alignment: ''
		} );

		/**
		 * Options passed to the view. See {@link #constructor} to learn more.
		 *
		 * @protected
		 * @member {Object}
		 */
		this.options = options;

		const { borderStyleDropdown, borderWidthInput, borderColorInput, borderRowLabel } = this._createBorderFields();
		const { backgroundRowLabel, backgroundInput } = this._createBackgroundFields();
		const { widthInput, operatorLabel, heightInput, dimensionsLabel } = this._createDimensionFields();
		const { alignmentToolbar, alignmentLabel } = this._createAlignmentFields();

		/**
		 * Tracks information about the DOM focus in the form.
		 *
		 * @readonly
		 * @member {module:utils/focustracker~FocusTracker}
		 */
		this.focusTracker = new FocusTracker();

		/**
		 * An instance of the {@link module:utils/keystrokehandler~KeystrokeHandler}.
		 *
		 * @readonly
		 * @member {module:utils/keystrokehandler~KeystrokeHandler}
		 */
		this.keystrokes = new KeystrokeHandler();

		/**
		 * A collection of child views in the form.
		 *
		 * @readonly
		 * @type {module:ui/viewcollection~ViewCollection}
		 */
		this.children = this.createCollection();

		/**
		 * A dropdown that allows selecting the style of the table border.
		 *
		 * @readonly
		 * @member {module:ui/dropdown/dropdownview~DropdownView}
		 */
		this.borderStyleDropdown = borderStyleDropdown;

		/**
		 * An input that allows specifying the width of the table border.
		 *
		 * @readonly
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.borderWidthInput = borderWidthInput;

		/**
		 * An input that allows specifying the color of the table border.
		 *
		 * @readonly
		 * @member {module:table/ui/colorinputview~ColorInputView}
		 */
		this.borderColorInput = borderColorInput;

		/**
		 * An input that allows specifying the table background color.
		 *
		 * @readonly
		 * @member {module:table/ui/colorinputview~ColorInputView}
		 */
		this.backgroundInput = backgroundInput;

		/**
		 * An input that allows specifying the table width.
		 *
		 * @readonly
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.widthInput = widthInput;

		/**
		 * An input that allows specifying the table height.
		 *
		 * @readonly
		 * @member {module:ui/inputtext/inputtextview~InputTextView}
		 */
		this.heightInput = heightInput;

		/**
		 * A toolbar with buttons that allow changing the alignment of an entire table.
		 * @readonly
		 * @member {module:ui/toolbar/toolbarview~ToolbarView}
		 */
		this.alignmentToolbar = alignmentToolbar;

		// Defer creating to make sure other fields are present and the Save button can
		// bind its #isEnabled to their error messages so there's no way to save unless all
		// fields are valid.
		const { saveButtonView, cancelButtonView } = this._createActionButtons();

		/**
		 * The "Save" button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.saveButtonView = saveButtonView;

		/**
		 * The "Cancel" button view.
		 *
		 * @member {module:ui/button/buttonview~ButtonView}
		 */
		this.cancelButtonView = cancelButtonView;

		/**
		 * A collection of views that can be focused in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/viewcollection~ViewCollection}
		 */
		this._focusables = new ViewCollection();

		/**
		 * Helps cycling over {@link #_focusables} in the form.
		 *
		 * @readonly
		 * @protected
		 * @member {module:ui/focuscycler~FocusCycler}
		 */
		this._focusCycler = new FocusCycler( {
			focusables: this._focusables,
			focusTracker: this.focusTracker,
			keystrokeHandler: this.keystrokes,
			actions: {
				// Navigate form fields backwards using the Shift + Tab keystroke.
				focusPrevious: 'shift + tab',

				// Navigate form fields forwards using the Tab key.
				focusNext: 'tab'
			}
		} );

		// Form header.
		this.children.add( new FormHeaderView( locale, {
			label: this.t( 'Table properties' )
		} ) );

		// Border row.
		this.children.add( new FormRowView( locale, {
			labelView: borderRowLabel,
			children: [
				borderRowLabel,
				borderStyleDropdown,
				borderColorInput,
				borderWidthInput
			],
			class: 'ck-table-form__border-row'
		} ) );

		// Background row.
		this.children.add( new FormRowView( locale, {
			labelView: backgroundRowLabel,
			children: [
				backgroundRowLabel,
				backgroundInput
			],
			class: 'ck-table-form__background-row'
		} ) );

		this.children.add( new FormRowView( locale, {
			children: [
				// Dimensions row.
				new FormRowView( locale, {
					labelView: dimensionsLabel,
					children: [
						dimensionsLabel,
						widthInput,
						operatorLabel,
						heightInput
					],
					class: 'ck-table-form__dimensions-row'
				} ),
				// Alignment row.
				new FormRowView( locale, {
					labelView: alignmentLabel,
					children: [
						alignmentLabel,
						alignmentToolbar
					],
					class: 'ck-table-properties-form__alignment-row'
				} )
			]
		} ) );

		// Action row.
		this.children.add( new FormRowView( locale, {
			children: [
				this.saveButtonView,
				this.cancelButtonView
			],
			class: 'ck-table-form__action-row'
		} ) );

		this.setTemplate( {
			tag: 'form',
			attributes: {
				class: [
					'ck',
					'ck-form',
					'ck-table-form',
					'ck-table-properties-form'
				],
				// https://github.com/ckeditor/ckeditor5-link/issues/90
				tabindex: '-1'
			},
			children: this.children
		} );
	}

	/**
	 * @inheritDoc
	 */
	render() {
		super.render();

		// Enable the "submit" event for this view. It can be triggered by the #saveButtonView
		// which is of the "submit" DOM "type".
		submitHandler( {
			view: this
		} );

		[
			this.borderStyleDropdown,
			this.borderColorInput,
			this.borderColorInput.fieldView.dropdownView.buttonView,
			this.borderWidthInput,
			this.backgroundInput,
			this.backgroundInput.fieldView.dropdownView.buttonView,
			this.widthInput,
			this.heightInput,
			this.alignmentToolbar,
			this.saveButtonView,
			this.cancelButtonView
		].forEach( view => {
			// Register the view as focusable.
			this._focusables.add( view );

			// Register the view in the focus tracker.
			this.focusTracker.add( view.element );
		} );

		// Mainly for closing using "Esc" and navigation using "Tab".
		this.keystrokes.listenTo( this.element );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		this.focusTracker.destroy();
		this.keystrokes.destroy();
	}

	/**
	 * Focuses the fist focusable field in the form.
	 */
	focus() {
		this._focusCycler.focusFirst();
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #borderStyleDropdown},
	 * * {@link #borderWidthInput},
	 * * {@link #borderColorInput}.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createBorderFields() {
		const defaultTableProperties = this.options.defaultTableProperties;
		const defaultBorder = {
			style: defaultTableProperties.borderStyle,
			width: defaultTableProperties.borderWidth,
			color: defaultTableProperties.borderColor
		};

		const colorInputCreator = getLabeledColorInputCreator( {
			colorConfig: this.options.borderColors,
			columns: 5,
			defaultColorValue: defaultBorder.color
		} );
		const locale = this.locale;
		const t = this.t;

		// -- Group label ---------------------------------------------

		const borderRowLabel = new LabelView( locale );
		borderRowLabel.text = t( 'Border' );

		// -- Style ---------------------------------------------------

		const styleLabels = getBorderStyleLabels( this.t );
		const borderStyleDropdown = new LabeledFieldView( locale, createLabeledDropdown );
		borderStyleDropdown.set( {
			label: t( 'Style' ),
			class: 'ck-table-form__border-style'
		} );

		borderStyleDropdown.fieldView.buttonView.set( {
			isOn: false,
			withText: true,
			tooltip: t( 'Style' )
		} );

		borderStyleDropdown.fieldView.buttonView.bind( 'label' ).to( this, 'borderStyle', value => {
			return styleLabels[ value ? value : 'none' ];
		} );

		borderStyleDropdown.fieldView.on( 'execute', evt => {
			this.borderStyle = evt.source._borderStyleValue;
		} );

		borderStyleDropdown.bind( 'isEmpty' ).to( this, 'borderStyle', value => !value );

		addListToDropdown( borderStyleDropdown.fieldView, getBorderStyleDefinitions( this, defaultBorder.style ) );

		// -- Width ---------------------------------------------------

		const borderWidthInput = new LabeledFieldView( locale, createLabeledInputText );

		borderWidthInput.set( {
			label: t( 'Width' ),
			class: 'ck-table-form__border-width'
		} );

		borderWidthInput.fieldView.bind( 'value' ).to( this, 'borderWidth' );
		borderWidthInput.bind( 'isEnabled' ).to( this, 'borderStyle', tablepropertiesview_isBorderStyleSet );
		borderWidthInput.fieldView.on( 'input', () => {
			this.borderWidth = borderWidthInput.fieldView.element.value;
		} );

		// -- Color ---------------------------------------------------

		const borderColorInput = new LabeledFieldView( locale, colorInputCreator );

		borderColorInput.set( {
			label: t( 'Color' ),
			class: 'ck-table-form__border-color'
		} );

		borderColorInput.fieldView.bind( 'value' ).to( this, 'borderColor' );
		borderColorInput.bind( 'isEnabled' ).to( this, 'borderStyle', tablepropertiesview_isBorderStyleSet );

		borderColorInput.fieldView.on( 'input', () => {
			this.borderColor = borderColorInput.fieldView.value;
		} );

		// Reset the border color and width fields depending on the `border-style` value.
		this.on( 'change:borderStyle', ( evt, name, newValue, oldValue ) => {
			// When removing the border (`border-style:none`), clear the remaining `border-*` properties.
			// See: https://github.com/ckeditor/ckeditor5/issues/6227.
			if ( !tablepropertiesview_isBorderStyleSet( newValue ) ) {
				this.borderColor = '';
				this.borderWidth = '';
			}

			// When setting the `border-style` from `none`, set the default `border-color` and `border-width` properties.
			if ( !tablepropertiesview_isBorderStyleSet( oldValue ) ) {
				this.borderColor = defaultBorder.color;
				this.borderWidth = defaultBorder.width;
			}
		} );

		return {
			borderRowLabel,
			borderStyleDropdown,
			borderColorInput,
			borderWidthInput
		};
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #backgroundInput}.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createBackgroundFields() {
		const locale = this.locale;
		const t = this.t;

		// -- Group label ---------------------------------------------

		const backgroundRowLabel = new LabelView( locale );
		backgroundRowLabel.text = t( 'Background' );

		// -- Background color input -----------------------------------

		const backgroundInputCreator = getLabeledColorInputCreator( {
			colorConfig: this.options.backgroundColors,
			columns: 5,
			defaultColorValue: this.options.defaultTableProperties.backgroundColor
		} );

		const backgroundInput = new LabeledFieldView( locale, backgroundInputCreator );

		backgroundInput.set( {
			label: t( 'Color' ),
			class: 'ck-table-properties-form__background'
		} );

		backgroundInput.fieldView.bind( 'value' ).to( this, 'backgroundColor' );
		backgroundInput.fieldView.on( 'input', () => {
			this.backgroundColor = backgroundInput.fieldView.value;
		} );

		return {
			backgroundRowLabel,
			backgroundInput
		};
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #widthInput}.
	 * * {@link #heightInput}.
	 *
	 * @private
	 * @returns {module:ui/labeledfield/labeledfieldview~LabeledFieldView}
	 */
	_createDimensionFields() {
		const locale = this.locale;
		const t = this.t;

		// -- Label ---------------------------------------------------

		const dimensionsLabel = new LabelView( locale );
		dimensionsLabel.text = t( 'Dimensions' );

		// -- Width ---------------------------------------------------

		const widthInput = new LabeledFieldView( locale, createLabeledInputText );

		widthInput.set( {
			label: t( 'Width' ),
			class: 'ck-table-form__dimensions-row__width'
		} );

		widthInput.fieldView.bind( 'value' ).to( this, 'width' );
		widthInput.fieldView.on( 'input', () => {
			this.width = widthInput.fieldView.element.value;
		} );

		// -- Operator ---------------------------------------------------

		const operatorLabel = new src_view_View( locale );
		operatorLabel.setTemplate( {
			tag: 'span',
			attributes: {
				class: [
					'ck-table-form__dimension-operator'
				]
			},
			children: [
				{ text: '×' }
			]
		} );

		// -- Height ---------------------------------------------------

		const heightInput = new LabeledFieldView( locale, createLabeledInputText );

		heightInput.set( {
			label: t( 'Height' ),
			class: 'ck-table-form__dimensions-row__height'
		} );

		heightInput.fieldView.bind( 'value' ).to( this, 'height' );
		heightInput.fieldView.on( 'input', () => {
			this.height = heightInput.fieldView.element.value;
		} );

		return {
			dimensionsLabel,
			widthInput,
			operatorLabel,
			heightInput
		};
	}

	/**
	 * Creates the following form fields:
	 *
	 * * {@link #alignmentToolbar},
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createAlignmentFields() {
		const locale = this.locale;
		const t = this.t;

		// -- Label ---------------------------------------------------

		const alignmentLabel = new LabelView( locale );
		alignmentLabel.text = t( 'Alignment' );

		// -- Toolbar ---------------------------------------------------

		const alignmentToolbar = new toolbarview_ToolbarView( locale );
		alignmentToolbar.set( {
			isCompact: true,
			ariaLabel: t( 'Table alignment toolbar' )
		} );

		fillToolbar( {
			view: this,
			icons: tablepropertiesview_ALIGNMENT_ICONS,
			toolbar: alignmentToolbar,
			labels: this._alignmentLabels,
			propertyName: 'alignment',
			defaultValue: this.options.defaultTableProperties.alignment
		} );

		return {
			alignmentLabel,
			alignmentToolbar
		};
	}

	/**
	 * Creates the following form controls:
	 *
	 * * {@link #saveButtonView},
	 * * {@link #cancelButtonView}.
	 *
	 * @private
	 * @returns {Object.<String,module:ui/view~View>}
	 */
	_createActionButtons() {
		const locale = this.locale;
		const t = this.t;

		const saveButtonView = new buttonview_ButtonView( locale );
		const cancelButtonView = new buttonview_ButtonView( locale );
		const fieldsThatShouldValidateToSave = [
			this.borderWidthInput,
			this.borderColorInput,
			this.backgroundInput,
			this.widthInput,
			this.heightInput
		];

		saveButtonView.set( {
			label: t( 'Save' ),
			icon: icons.check,
			class: 'ck-button-save',
			type: 'submit',
			withText: true
		} );

		saveButtonView.bind( 'isEnabled' ).toMany( fieldsThatShouldValidateToSave, 'errorText', ( ...errorTexts ) => {
			return errorTexts.every( errorText => !errorText );
		} );

		cancelButtonView.set( {
			label: t( 'Cancel' ),
			icon: icons.cancel,
			class: 'ck-button-cancel',
			withText: true
		} );

		cancelButtonView.delegate( 'execute' ).to( this, 'cancel' );

		return {
			saveButtonView, cancelButtonView
		};
	}

	/**
	 * Provides localized labels for {@link #alignmentToolbar} buttons.
	 *
	 * @private
	 * @type {Object.<String,String>}
	 */
	get _alignmentLabels() {
		const locale = this.locale;
		const t = this.t;

		const left = t( 'Align table to the left' );
		const center = t( 'Center table' );
		const right = t( 'Align table to the right' );

		// Returns object with a proper order of labels.
		if ( locale.uiLanguageDirection === 'rtl' ) {
			return { right, center, left };
		} else {
			return { left, center, right };
		}
	}
}

function tablepropertiesview_isBorderStyleSet( value ) {
	return value !== 'none';
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/theme/icons/table-properties.svg
/* harmony default export */ const table_properties = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M8 2v5h4V2h1v5h5v1h-5v4h.021l-.172.351-1.916.28-.151.027c-.287.063-.54.182-.755.341L8 13v5H7v-5H2v-1h5V8H2V7h5V2h1zm4 6H8v4h4V8z\" opacity=\".6\"/><path d=\"m15.5 11.5 1.323 2.68 2.957.43-2.14 2.085.505 2.946L15.5 18.25l-2.645 1.39.505-2.945-2.14-2.086 2.957-.43L15.5 11.5zM17 1a2 2 0 0 1 2 2v9.475l-.85-.124-.857-1.736a2.048 2.048 0 0 0-.292-.44L17 3H3v14h7.808l.402.392L10.935 19H3a2 2 0 0 1-2-2V3a2 2 0 0 1 2-2h14z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties/tablepropertiesui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties/tablepropertiesui
 */













const tablepropertiesui_ERROR_TEXT_TIMEOUT = 500;

// Map of view properties and related commands.
const tablepropertiesui_propertyToCommandMap = {
	borderStyle: 'tableBorderStyle',
	borderColor: 'tableBorderColor',
	borderWidth: 'tableBorderWidth',
	backgroundColor: 'tableBackgroundColor',
	width: 'tableWidth',
	height: 'tableHeight',
	alignment: 'tableAlignment'
};

/**
 * The table properties UI plugin. It introduces the `'tableProperties'` button
 * that opens a form allowing to specify visual styling of an entire table.
 *
 * It uses the
 * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon plugin}.
 *
 * @extends module:core/plugin~Plugin
 */
class TablePropertiesUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ContextualBalloon ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TablePropertiesUI';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		editor.config.define( 'table.tableProperties', {
			borderColors: defaultColors,
			backgroundColors: defaultColors
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		/**
		 * The default table properties.
		 *
		 * @protected
		 * @member {module:table/tableproperties~TablePropertiesOptions}
		 */
		this._defaultTableProperties = getNormalizedDefaultProperties( editor.config.get( 'table.tableProperties.defaultProperties' ), {
			includeAlignmentProperty: true
		} );

		/**
		 * The contextual balloon plugin instance.
		 *
		 * @private
		 * @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
		 */
		this._balloon = editor.plugins.get( ContextualBalloon );

		/**
		 * The properties form view displayed inside the balloon.
		 *
		 * @member {module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView}
		 */
		this.view = this._createPropertiesView();

		/**
		 * The batch used to undo all changes made by the form (which are live, as the user types)
		 * when "Cancel" was pressed. Each time the view is shown, a new batch is created.
		 *
		 * @protected
		 * @member {module:engine/model/batch~Batch}
		 */
		this._undoStepBatch = null;

		editor.ui.componentFactory.add( 'tableProperties', locale => {
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Table properties' ),
				icon: table_properties,
				tooltip: true
			} );

			this.listenTo( view, 'execute', () => this._showView() );

			const commands = Object.values( tablepropertiesui_propertyToCommandMap )
				.map( commandName => editor.commands.get( commandName ) );

			view.bind( 'isEnabled' ).toMany( commands, 'isEnabled', ( ...areEnabled ) => (
				areEnabled.some( isCommandEnabled => isCommandEnabled )
			) );

			return view;
		} );
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		super.destroy();

		// Destroy created UI components as they are not automatically destroyed.
		// See https://github.com/ckeditor/ckeditor5/issues/1341.
		this.view.destroy();
	}

	/**
	 * Creates the {@link module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView} instance.
	 *
	 * @private
	 * @returns {module:table/tableproperties/ui/tablepropertiesview~TablePropertiesView} The table
	 * properties form view instance.
	 */
	_createPropertiesView() {
		const editor = this.editor;
		const config = editor.config.get( 'table.tableProperties' );
		const borderColorsConfig = normalizeColorOptions( config.borderColors );
		const localizedBorderColors = getLocalizedColorOptions( editor.locale, borderColorsConfig );
		const backgroundColorsConfig = normalizeColorOptions( config.backgroundColors );
		const localizedBackgroundColors = getLocalizedColorOptions( editor.locale, backgroundColorsConfig );

		const view = new TablePropertiesView( editor.locale, {
			borderColors: localizedBorderColors,
			backgroundColors: localizedBackgroundColors,
			defaultTableProperties: this._defaultTableProperties
		} );
		const t = editor.t;

		// Render the view so its #element is available for the clickOutsideHandler.
		view.render();

		this.listenTo( view, 'submit', () => {
			this._hideView();
		} );

		this.listenTo( view, 'cancel', () => {
			// https://github.com/ckeditor/ckeditor5/issues/6180
			if ( this._undoStepBatch.operations.length ) {
				editor.execute( 'undo', this._undoStepBatch );
			}

			this._hideView();
		} );

		// Close the balloon on Esc key press.
		view.keystrokes.set( 'Esc', ( data, cancel ) => {
			this._hideView();
			cancel();
		} );

		// Close on click outside of balloon panel element.
		clickoutsidehandler_clickOutsideHandler( {
			emitter: view,
			activator: () => this._isViewInBalloon,
			contextElements: [ this._balloon.view.element ],
			callback: () => this._hideView()
		} );

		const colorErrorText = getLocalizedColorErrorText( t );
		const lengthErrorText = getLocalizedLengthErrorText( t );

		// Create the "UI -> editor data" binding.
		// These listeners update the editor data (via table commands) when any observable
		// property of the view has changed. They also validate the value and display errors in the UI
		// when necessary. This makes the view live, which means the changes are
		// visible in the editing as soon as the user types or changes fields' values.
		view.on(
			'change:borderStyle',
			this._getPropertyChangeCallback( 'tableBorderStyle', this._defaultTableProperties.borderStyle )
		);

		view.on( 'change:borderColor', this._getValidatedPropertyChangeCallback( {
			viewField: view.borderColorInput,
			commandName: 'tableBorderColor',
			errorText: colorErrorText,
			validator: colorFieldValidator,
			defaultValue: this._defaultTableProperties.borderColor
		} ) );

		view.on( 'change:borderWidth', this._getValidatedPropertyChangeCallback( {
			viewField: view.borderWidthInput,
			commandName: 'tableBorderWidth',
			errorText: lengthErrorText,
			validator: lineWidthFieldValidator,
			defaultValue: this._defaultTableProperties.borderWidth
		} ) );

		view.on( 'change:backgroundColor', this._getValidatedPropertyChangeCallback( {
			viewField: view.backgroundInput,
			commandName: 'tableBackgroundColor',
			errorText: colorErrorText,
			validator: colorFieldValidator,
			defaultValue: this._defaultTableProperties.backgroundColor
		} ) );

		view.on( 'change:width', this._getValidatedPropertyChangeCallback( {
			viewField: view.widthInput,
			commandName: 'tableWidth',
			errorText: lengthErrorText,
			validator: lengthFieldValidator,
			defaultValue: this._defaultTableProperties.width
		} ) );

		view.on( 'change:height', this._getValidatedPropertyChangeCallback( {
			viewField: view.heightInput,
			commandName: 'tableHeight',
			errorText: lengthErrorText,
			validator: lengthFieldValidator,
			defaultValue: this._defaultTableProperties.height
		} ) );

		view.on(
			'change:alignment',
			this._getPropertyChangeCallback( 'tableAlignment', this._defaultTableProperties.alignment )
		);

		return view;
	}

	/**
	 * In this method the "editor data -> UI" binding is happening.
	 *
	 * When executed, this method obtains selected table property values from various table commands
	 * and passes them to the {@link #view}.
	 *
	 * This way, the UI stays up–to–date with the editor data.
	 *
	 * @private
	 */
	_fillViewFormFromCommandValues() {
		const commands = this.editor.commands;
		const borderStyleCommand = commands.get( 'tableBorderStyle' );

		Object.entries( tablepropertiesui_propertyToCommandMap )
			.map( ( [ property, commandName ] ) => {
				const defaultValue = this._defaultTableProperties[ property ] || '';

				return [ property, commands.get( commandName ).value || defaultValue ];
			} )
			.forEach( ( [ property, value ] ) => {
				// Do not set the `border-color` and `border-width` fields if `border-style:none`.
				if ( ( property === 'borderColor' || property === 'borderWidth' ) && borderStyleCommand.value === 'none' ) {
					return;
				}

				this.view.set( property, value );
			} );
	}

	/**
	 * Shows the {@link #view} in the {@link #_balloon}.
	 *
	 * **Note**: Each time a view is shown, the new {@link #_undoStepBatch} is created that contains
	 * all changes made to the document when the view is visible, allowing a single undo step
	 * for all of them.
	 *
	 * @protected
	 */
	_showView() {
		const editor = this.editor;

		this.listenTo( editor.ui, 'update', () => {
			this._updateView();
		} );

		// Update the view with the model values.
		this._fillViewFormFromCommandValues();

		this._balloon.add( {
			view: this.view,
			position: getBalloonTablePositionData( editor )
		} );

		// Create a new batch. Clicking "Cancel" will undo this batch.
		this._undoStepBatch = editor.model.createBatch();

		// Basic a11y.
		this.view.focus();
	}

	/**
	 * Removes the {@link #view} from the {@link #_balloon}.
	 *
	 * @protected
	 */
	_hideView() {
		const editor = this.editor;

		this.stopListening( editor.ui, 'update' );

		// Blur any input element before removing it from DOM to prevent issues in some browsers.
		// See https://github.com/ckeditor/ckeditor5/issues/1501.
		this.view.saveButtonView.focus();

		this._balloon.remove( this.view );

		// Make sure the focus is not lost in the process by putting it directly
		// into the editing view.
		this.editor.editing.view.focus();
	}

	/**
	 * Repositions the {@link #_balloon} or hides the {@link #view} if a table is no longer selected.
	 *
	 * @protected
	 */
	_updateView() {
		const editor = this.editor;
		const viewDocument = editor.editing.view.document;

		if ( !getTableWidgetAncestor( viewDocument.selection ) ) {
			this._hideView();
		} else if ( this._isViewVisible ) {
			contextualballoon_repositionContextualBalloon( editor, 'table' );
		}
	}

	/**
	 * Returns `true` when the {@link #view} is the visible in the {@link #_balloon}.
	 *
	 * @private
	 * @type {Boolean}
	 */
	get _isViewVisible() {
		return this._balloon.visibleView === this.view;
	}

	/**
	 * Returns `true` when the {@link #view} is in the {@link #_balloon}.
	 *
	 * @private
	 * @type {Boolean}
	 */
	get _isViewInBalloon() {
		return this._balloon.hasView( this.view );
	}

	/**
	 * Creates a callback that when executed upon {@link #view view's} property change
	 * executes a related editor command with the new property value.
	 *
	 * If new value will be set to the default value, the command will not be executed.
	 *
	 * @private
	 * @param {String} commandName The command that will be executed.
	 * @param {String} defaultValue The default value of the command.
	 * @returns {Function}
	 */
	_getPropertyChangeCallback( commandName, defaultValue ) {
		return ( evt, propertyName, newValue, oldValue ) => {
			// If the "oldValue" is missing and "newValue" is set to the default value, do not execute the command.
			// It is an initial call (when opening the table properties view).
			if ( !oldValue && defaultValue === newValue ) {
				return;
			}

			this.editor.execute( commandName, {
				value: newValue,
				batch: this._undoStepBatch
			} );
		};
	}

	/**
	 * Creates a callback that when executed upon {@link #view view's} property change:
	 * * executes a related editor command with the new property value if the value is valid,
	 * * or sets the error text next to the invalid field, if the value did not pass the validation.
	 *
	 * @private
	 * @param {Object} options
	 * @param {String} options.commandName
	 * @param {module:ui/view~View} options.viewField
	 * @param {Function} options.validator
	 * @param {String} options.errorText
	 * @param {String} options.defaultValue
	 * @returns {Function}
	 */
	_getValidatedPropertyChangeCallback( options ) {
		const { commandName, viewField, validator, errorText, defaultValue } = options;
		const setErrorTextDebounced = lodash_es_debounce( () => {
			viewField.errorText = errorText;
		}, tablepropertiesui_ERROR_TEXT_TIMEOUT );

		return ( evt, propertyName, newValue, oldValue ) => {
			setErrorTextDebounced.cancel();

			// If the "oldValue" is missing and "newValue" is set to the default value, do not execute the command.
			// It is an initial call (when opening the table properties view).
			if ( !oldValue && defaultValue === newValue ) {
				return;
			}

			if ( validator( newValue ) ) {
				this.editor.execute( commandName, {
					value: newValue,
					batch: this._undoStepBatch
				} );

				viewField.errorText = null;
			} else {
				setErrorTextDebounced();
			}
		};
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tableproperties.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tableproperties
 */






/**
 * The table properties feature. Enables support for setting properties of tables (size, border, background, etc.).
 *
 * Read more in the {@glink features/table#table-and-cell-styling-tools Table and cell styling tools} section.
 * See also the {@link module:table/tablecellproperties~TableCellProperties} plugin.
 *
 * This is a "glue" plugin that loads the
 * {@link module:table/tableproperties/tablepropertiesediting~TablePropertiesEditing table properties editing feature} and
 * the {@link module:table/tableproperties/tablepropertiesui~TablePropertiesUI table properties UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class TableProperties extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableProperties';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TablePropertiesEditing, TablePropertiesUI ];
	}
}

/**
 * The configuration of the table properties user interface (balloon). It allows to define:
 *
 * * The color palette for the table border color style field (`tableProperties.borderColors`),
 * * The color palette for the table background style field (`tableProperties.backgroundColors`).
 *
 *		const tableConfig = {
 *			tableProperties: {
 *				borderColors: [
 *					{
 *						color: 'hsl(0, 0%, 90%)',
 *						label: 'Light grey'
 *					},
 *					// ...
 *				],
 *				backgroundColors: [
 *					{
 *						color: 'hsl(120, 75%, 60%)',
 *						label: 'Green'
 *					},
 *					// ...
 *				]
 *			}
 *		};
 *
 * * The default styles for tables (`tableProperties.defaultProperties`):
 *
 *		const tableConfig = {
 *			tableProperties: {
 *				defaultProperties: {
 *					borderStyle: 'dashed',
 *					borderColor: 'hsl(0, 0%, 90%)',
 *					borderWidth: '3px',
 *					alignment: 'left'
 *				}
 *			}
 *		}
 *
 * 	 {@link module:table/tableproperties~TablePropertiesOptions Read more about the supported properties.}
 *
 * **Note**: The `borderColors` and `backgroundColors` options do not impact the data loaded into the editor,
 * i.e. they do not limit or filter the colors in the data. They are used only in the user interface
 * allowing users to pick colors in a more convenient way. The `defaultProperties` option does impact the data.
 * Default values will not be kept in the editor model.
 *
 * The default color palettes for the table background and the table border are the same
 * ({@link module:table/utils/ui/table-properties~defaultColors check out their content}).
 *
 * Both color palette configurations must follow the
 * {@link module:table/table~TableColorConfig table color configuration format}.
 *
 * Read more about configuring the table feature in {@link module:table/table~TableConfig}.
 *
 * @member {Object} module:table/table~TableConfig#tableProperties
 */

/**
 * The configuration of the table default properties feature.
 *
 * @typedef {Object} module:table/tableproperties~TablePropertiesOptions
 *
 * @property {String} width The default `width` of the table.
 *
 * @property {String} height The default `height` of the table.
 *
 * @property {String} backgroundColor The default `background-color` of the table.
 *
 * @property {String} borderColor The default `border-color` of the table.
 *
 * @property {String} borderWidth The default `border-width` of the table.
 *
 * @property {String} [borderStyle='none'] The default `border-style` of the table.
 *
 * @property {String} [alignment='center'] The default `alignment` of the table.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-table/src/tabletoolbar.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module table/tabletoolbar
 */





/**
 * The table toolbar class. It creates toolbars for the table feature and its content (for now only for the table cell content).
 *
 * The table toolbar shows up when a table widget is selected. Its components (e.g. buttons) are created based on the
 * {@link module:table/table~TableConfig#tableToolbar `table.tableToolbar` configuration option}.
 *
 * Table content toolbar shows up when the selection is inside the content of a table. It creates its component based on the
 * {@link module:table/table~TableConfig#contentToolbar `table.contentToolbar` configuration option}.
 *
 * @extends module:core/plugin~Plugin
 */
class TableToolbar extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ WidgetToolbarRepository ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TableToolbar';
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const editor = this.editor;
		const t = editor.t;
		const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );

		const tableContentToolbarItems = editor.config.get( 'table.contentToolbar' );

		const tableToolbarItems = editor.config.get( 'table.tableToolbar' );

		if ( tableContentToolbarItems ) {
			widgetToolbarRepository.register( 'tableContent', {
				ariaLabel: t( 'Table toolbar' ),
				items: tableContentToolbarItems,
				getRelatedElement: getTableWidgetAncestor
			} );
		}

		if ( tableToolbarItems ) {
			widgetToolbarRepository.register( 'table', {
				ariaLabel: t( 'Table toolbar' ),
				items: tableToolbarItems,
				getRelatedElement: getSelectedTableWidget
			} );
		}
	}
}

/**
 * Items to be placed in the table content toolbar.
 * The {@link module:table/tabletoolbar~TableToolbar} plugin is required to make this toolbar work.
 *
 * Assuming that you use the {@link module:table/tableui~TableUI} feature, the following toolbar items will be available
 * in {@link module:ui/componentfactory~ComponentFactory}:
 *
 * * `'tableRow'`,
 * * `'tableColumn'`,
 * * `'mergeTableCells'`.
 *
 * You can thus configure the toolbar like this:
 *
 *		const tableConfig = {
 *			contentToolbar: [ 'tableRow', 'tableColumn', 'mergeTableCells' ]
 *		};
 *
 * Of course, the same buttons can also be used in the
 * {@link module:core/editor/editorconfig~EditorConfig#toolbar main editor toolbar}.
 *
 * Read more about configuring the toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
 *
 * @member {Array.<String>} module:table/table~TableConfig#contentToolbar
 */

/**
 * Items to be placed in the table toolbar.
 * The {@link module:table/tabletoolbar~TableToolbar} plugin is required to make this toolbar work.
 *
 * You can thus configure the toolbar like this:
 *
 *		const tableConfig = {
 *			tableToolbar: [ 'blockQuote' ]
 *		};
 *
 * Of course, the same buttons can also be used in the
 * {@link module:core/editor/editorconfig~EditorConfig#toolbar main editor toolbar}.
 *
 * Read more about configuring the toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
 *
 * @member {Array.<String>} module:table/table~TableConfig#tableToolbar
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-language/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module language/utils
 */



/**
 * Returns the language attribute value in a human-readable text format:
 *
 *		<languageCode>:<textDirection>
 *
 * * `languageCode` - The language code used for the `lang` attribute in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
 * * `textDirection` - One of the following values: `rtl` or `ltr`, indicating the reading direction of the language.
 *
 * See the {@link module:core/editor/editorconfig~LanguageConfig#textPartLanguage text part language configuration}
 * for more information about language properties.
 *
 * If the `textDirection` argument is omitted, it will be automatically detected based on `languageCode`.
 *
 * @param {String} languageCode The language code in the ISO 639-1 format.
 * @param {'ltr'|'rtl'} [textDirection] The language text direction. Automatically detected if omitted.
 * @returns {String}
 */
function stringifyLanguageAttribute( languageCode, textDirection ) {
	textDirection = textDirection || getLanguageDirection( languageCode );
	return `${ languageCode }:${ textDirection }`;
}

/**
 * Retrieves language properties converted to attribute value by the
 * {@link module:language/utils~stringifyLanguageAttribute stringifyLanguageAttribute} function.
 *
 * @param {String} str The attribute value.
 * @returns {Object} result
 * @returns {String} result.languageCode The language code in the ISO 639 format.
 * @returns {String} result.textDirection The language text direction.
 */
function parseLanguageAttribute( str ) {
	const [ languageCode, textDirection ] = str.split( ':' );

	return { languageCode, textDirection };
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-language/src/textpartlanguagecommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module language/textpartlanguagecommand
 */




/**
 * The text part language command plugin.
 *
 * @extends module:core/command~Command
 */
class TextPartLanguageCommand extends command_Command {
	/**
	 * If the selection starts in a language attribute, the value is set to
	 * the value of that language in a format:
	 *
	 *		<languageCode>:<textDirection>
	 *
	 * * `languageCode` - The language code used for the `lang` attribute in the [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1)
	 *    format.
	 * * `textDirection` - One of the following values: `rtl` or `ltr`, indicating the reading direction of the language.
	 *
	 * See the {@link module:core/editor/editorconfig~LanguageConfig#textPartLanguage text part language configuration}
	 * for more information about language properties.
	 *
	 * It is set to `false` otherwise.
	 *
	 * @observable
	 * @readonly
	 * @member {Boolean|String} #value
	 */

	/**
	 * @inheritDoc
	 */
	refresh() {
		const model = this.editor.model;
		const doc = model.document;

		this.value = this._getValueFromFirstAllowedNode();
		this.isEnabled = model.schema.checkAttributeInSelection( doc.selection, 'language' );
	}

	/**
	 * Executes the command. Applies the attribute to the selection or removes it from the selection.
	 *
	 * If `languageCode` is set to `false` or a `null` value, it will remove attributes. Otherwise, it will set
	 * the attribute in the `{@link #value value}` format.
	 *
	 * The execution result differs, depending on the {@link module:engine/model/document~Document#selection}:
	 *
	 * * If the selection is on a range, the command applies the attribute to all nodes in that range
	 * (if they are allowed to have this attribute by the {@link module:engine/model/schema~Schema schema}).
	 * * If the selection is collapsed in a non-empty node, the command applies the attribute to the
	 * {@link module:engine/model/document~Document#selection} itself (note that typed characters copy attributes from the selection).
	 * * If the selection is collapsed in an empty node, the command applies the attribute to the parent node of the selection (note
	 * that the selection inherits all attributes from a node if it is in an empty node).
	 *
	 * @fires execute
	 * @param {Object} [options] Command options.
	 * @param {String|Boolean} [options.languageCode] The language code to be applied to the model.
	 * @param {String} [options.textDirection] The language text direction.
	 */
	execute( { languageCode, textDirection } = {} ) {
		const model = this.editor.model;
		const doc = model.document;
		const selection = doc.selection;

		const value = languageCode ? stringifyLanguageAttribute( languageCode, textDirection ) : false;

		model.change( writer => {
			if ( selection.isCollapsed ) {
				if ( value ) {
					writer.setSelectionAttribute( 'language', value );
				} else {
					writer.removeSelectionAttribute( 'language' );
				}
			} else {
				const ranges = model.schema.getValidRanges( selection.getRanges(), 'language' );

				for ( const range of ranges ) {
					if ( value ) {
						writer.setAttribute( 'language', value, range );
					} else {
						writer.removeAttribute( 'language', range );
					}
				}
			}
		} );
	}

	/**
	 * Returns the attribute value of the first node in the selection that allows the attribute.
	 * For a collapsed selection it returns the selection attribute.
	 *
	 * @private
	 * @returns {Boolean|String} The attribute value.
	 */
	_getValueFromFirstAllowedNode() {
		const model = this.editor.model;
		const schema = model.schema;
		const selection = model.document.selection;

		if ( selection.isCollapsed ) {
			return selection.getAttribute( 'language' ) || false;
		}

		for ( const range of selection.getRanges() ) {
			for ( const item of range.getItems() ) {
				if ( schema.checkAttribute( item, 'language' ) ) {
					return item.getAttribute( 'language' ) || false;
				}
			}
		}

		return false;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-language/src/textpartlanguageediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module language/textpartlanguageediting
 */





/**
 * The text part language editing.
 *
 * Introduces the `'textPartLanguage'` command and the `'language'` model element attribute.
 *
 * @extends module:core/plugin~Plugin
 */
class TextPartLanguageEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TextPartLanguageEditing';
	}

	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		// Text part language options are only used to ensure that the feature works by default.
		// In the real usage it should be reconfigured by a developer. We are not providing
		// translations for `title` properties on purpose, as it's only an example configuration.
		editor.config.define( 'language', {
			textPartLanguage: [
				{ title: 'Arabic', languageCode: 'ar' },
				{ title: 'French', languageCode: 'fr' },
				{ title: 'Spanish', languageCode: 'es' }
			]
		} );
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		editor.model.schema.extend( '$text', { allowAttributes: 'language' } );
		editor.model.schema.setAttributeProperties( 'language', {
			copyOnEnter: true
		} );

		this._defineConverters();

		editor.commands.add( 'textPartLanguage', new TextPartLanguageCommand( editor ) );
	}

	/**
	 * @private
	 */
	_defineConverters() {
		const conversion = this.editor.conversion;

		conversion.for( 'upcast' ).elementToAttribute( {
			model: {
				key: 'language',
				value: viewElement => {
					const languageCode = viewElement.getAttribute( 'lang' );
					const textDirection = viewElement.getAttribute( 'dir' );

					return stringifyLanguageAttribute( languageCode, textDirection );
				}
			},
			view: {
				name: 'span',
				attributes: { lang: /[\s\S]+/ }
			}
		} );

		conversion.for( 'downcast' ).attributeToElement( {
			model: 'language',
			view: ( attributeValue, { writer }, data ) => {
				if ( !attributeValue ) {
					return;
				}

				if ( !data.item.is( '$textProxy' ) && !data.item.is( 'documentSelection' ) ) {
					return;
				}

				const { languageCode, textDirection } = parseLanguageAttribute( attributeValue );

				return writer.createAttributeElement( 'span', {
					lang: languageCode,
					dir: textDirection
				} );
			}
		} );
	}
}

// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./node_modules/@ckeditor/ckeditor5-language/theme/language.css
var language = __webpack_require__(4704);
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-language/theme/language.css

            

var language_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

language_options.insert = "head";
language_options.singleton = true;

var language_update = injectStylesIntoStyleTag_default()(language/* default */.Z, language_options);



/* harmony default export */ const theme_language = (language/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-language/src/textpartlanguageui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module language/textpartlanguageui
 */








/**
 * The text part language UI plugin.
 *
 * It introduces the `'language'` dropdown.
 *
 * @extends module:core/plugin~Plugin
 */
class TextPartLanguageUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TextPartLanguageUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;
		const options = editor.config.get( 'language.textPartLanguage' );
		const defaultTitle = t( 'Choose language' );
		const removeTitle = t( 'Remove language' );
		const dropdownTooltip = t( 'Language' );

		// Register UI component.
		editor.ui.componentFactory.add( 'textPartLanguage', locale => {
			const itemDefinitions = new Collection();
			const titles = {};

			const languageCommand = editor.commands.get( 'textPartLanguage' );

			// Item definition with false `languageCode` will behave as remove lang button.
			itemDefinitions.add( {
				type: 'button',
				model: new model_Model( {
					label: removeTitle,
					languageCode: false,
					withText: true
				} )
			} );

			itemDefinitions.add( {
				type: 'separator'
			} );

			for ( const option of options ) {
				const def = {
					type: 'button',
					model: new model_Model( {
						label: option.title,
						languageCode: option.languageCode,
						textDirection: option.textDirection,
						withText: true
					} )
				};

				const language = stringifyLanguageAttribute( option.languageCode, option.textDirection );

				def.model.bind( 'isOn' ).to( languageCommand, 'value', value => value === language );

				itemDefinitions.add( def );

				titles[ language ] = option.title;
			}

			const dropdownView = createDropdown( locale );
			addListToDropdown( dropdownView, itemDefinitions );

			dropdownView.buttonView.set( {
				isOn: false,
				withText: true,
				tooltip: dropdownTooltip
			} );

			dropdownView.extendTemplate( {
				attributes: {
					class: [
						'ck-text-fragment-language-dropdown'
					]
				}
			} );

			dropdownView.bind( 'isEnabled' ).to( languageCommand, 'isEnabled' );
			dropdownView.buttonView.bind( 'label' ).to( languageCommand, 'value', value => {
				return titles[ value ] || defaultTitle;
			} );

			// Execute command when an item from the dropdown is selected.
			this.listenTo( dropdownView, 'execute', evt => {
				languageCommand.execute( {
					languageCode: evt.source.languageCode,
					textDirection: evt.source.textDirection
				} );

				editor.editing.view.focus();
			} );

			return dropdownView;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-language/src/textpartlanguage.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module language/textpartlanguage
 */






/**
 * The text part language feature.
 *
 * This feature allows setting a language of the document's text part to support
 * [WCAG 3.1.2 Language of Parts](https://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html) specification.
 *
 * To change the editor's UI language, refer to the {@glink features/ui-language Setting the UI language} guide.
 *
 * For more information about this feature, check the {@glink api/language package page} as well as the {@glink features/language
 * Text part language} feature guide.
 *
 * This is a "glue" plugin which loads the
 * {@link module:language/textpartlanguageediting~TextPartLanguageEditing text part language editing feature}
 * and the {@link module:language/textpartlanguageui~TextPartLanguageUI text part language UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class TextPartLanguage extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ TextPartLanguageEditing, TextPartLanguageUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'TextPartLanguage';
	}
}

/**
 * The available {@link module:language/textpartlanguage~TextPartLanguage}
 * options that allow setting the language of parts of the content.
 *
 * This configuration option is available only with the {@glink api/language text part language feature} enabled.
 *
 * Refer to [WCAG 3.1.2 Language of Parts](https://www.w3.org/TR/UNDERSTANDING-WCAG20/meaning-other-lang-id.html) specification
 * to learn more.
 *
 * To change the editor's UI language, refer to the {@glink features/ui-language Setting the UI language} guide.
 *
 * The default value is:
 *
 *		const config = [
 *			{ title: 'Arabic', languageCode: 'ar' },
 *			{ title: 'French', languageCode: 'fr' },
 *			{ title: 'Spanish', languageCode: 'es' }
 *		];
 *
 * The `title` property will be used by the text part language dropdown to render available options.
 *
 * The `languageCode` property is used for the `lang` attribute in [ISO 639-1](https://en.wikipedia.org/wiki/ISO_639-1) format.
 *
 * You can also specify the optional `textDirection` property indicating the reading direction of the language.
 * Correct values are `ltr` and `rtl`. When the `textDirection` property is missing, the text part language feature will
 * specify the text direction by itself.
 *
 * @member {Array.<module:language/textpartlanguage~TextPartLanguageOption>}
 * module:core/editor/editorconfig~LanguageConfig#textPartLanguage
 */

/**
 * The text part language feature option descriptor.
 *
 * @typedef {Object} module:language/textpartlanguage~TextPartLanguageOption
 * @property {String} title The user-readable title of the option.
 * @property {String} languageCode The language code in the ISO 639 format.
 * @property {'ltr'|'rtl'} [textDirection] The language text direction. Automatically detected if omitted.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/underline/underlineediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/underline/underlineediting
 */




const UNDERLINE = 'underline';

/**
 * The underline editing feature.
 *
 * It registers the `'underline'` command, the <kbd>Ctrl+U</kbd> keystroke
 * and introduces the `underline` attribute in the model which renders to the view as an `<u>` element.
 *
 * @extends module:core/plugin~Plugin
 */
class UnderlineEditing extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'UnderlineEditing';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Allow strikethrough attribute on text nodes.
		editor.model.schema.extend( '$text', { allowAttributes: UNDERLINE } );
		editor.model.schema.setAttributeProperties( UNDERLINE, {
			isFormatting: true,
			copyOnEnter: true
		} );

		editor.conversion.attributeToElement( {
			model: UNDERLINE,
			view: 'u',
			upcastAlso: {
				styles: {
					'text-decoration': 'underline'
				}
			}
		} );

		// Create underline command.
		editor.commands.add( UNDERLINE, new AttributeCommand( editor, UNDERLINE ) );

		// Set the Ctrl+U keystroke.
		editor.keystrokes.set( 'CTRL+U', 'underline' );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/theme/icons/underline.svg
/* harmony default export */ const underline = ("<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3 18v-1.5h14V18zm2.2-8V3.6c0-.4.4-.6.8-.6.3 0 .7.2.7.6v6.2c0 2 1.3 2.8 3.2 2.8 1.9 0 3.4-.9 3.4-2.9V3.6c0-.3.4-.5.8-.5.3 0 .7.2.7.5V10c0 2.7-2.2 4-4.9 4-2.6 0-4.7-1.2-4.7-4z\"/></svg>");
;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/underline/underlineui.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/underline/underlineui
 */






const underlineui_UNDERLINE = 'underline';

/**
 * The underline UI feature. It introduces the Underline button.
 *
 * @extends module:core/plugin~Plugin
 */
class UnderlineUI extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'UnderlineUI';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;
		const t = editor.t;

		// Add bold button to feature components.
		editor.ui.componentFactory.add( underlineui_UNDERLINE, locale => {
			const command = editor.commands.get( underlineui_UNDERLINE );
			const view = new buttonview_ButtonView( locale );

			view.set( {
				label: t( 'Underline' ),
				icon: underline,
				keystroke: 'CTRL+U',
				tooltip: true,
				isToggleable: true
			} );

			view.bind( 'isOn', 'isEnabled' ).to( command, 'value', 'isEnabled' );

			// Execute command.
			this.listenTo( view, 'execute', () => {
				editor.execute( underlineui_UNDERLINE );
				editor.editing.view.focus();
			} );

			return view;
		} );
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-basic-styles/src/underline.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module basic-styles/underline
 */





/**
 * The underline feature.
 *
 * For a detailed overview check the {@glink features/basic-styles Basic styles feature documentation}
 * and the {@glink api/basic-styles package page}.
 *
 * This is a "glue" plugin which loads the {@link module:basic-styles/underline/underlineediting~UnderlineEditing} and
 * {@link module:basic-styles/underline/underlineui~UnderlineUI} plugins.
 *
 * @extends module:core/plugin~Plugin
 */
class Underline extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ UnderlineEditing, UnderlineUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'Underline';
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-word-count/src/utils.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module word-count/utils
 */

/**
 * Returns a plain text representation of an element and its children.
 *
 * @param {module:engine/model/element~Element} element
 * @returns {String} Plain text representing the model's data.
 */
function modelElementToPlainText( element ) {
	if ( element.is( '$text' ) || element.is( '$textProxy' ) ) {
		return element.data;
	}

	let text = '';
	let prev = null;

	for ( const child of element.getChildren() ) {
		const childText = modelElementToPlainText( child );

		// If last block was finish, start from new line.
		if ( prev && prev.is( 'element' ) ) {
			text += '\n';
		}

		text += childText;

		prev = child;
	}

	return text;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-word-count/src/wordcount.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module word-count/wordcount
 */







/**
 * The word count plugin.
 *
 * This plugin calculates all words and characters in all {@link module:engine/model/text~Text text nodes} available in the model.
 * It also provides an HTML element that updates its state whenever the editor content is changed.
 *
 * The model's data is first converted to plain text using {@link module:word-count/utils~modelElementToPlainText}.
 * The number of words and characters in your text are determined based on the created plain text. Please keep in mind
 * that every block in the editor is separated with a newline character, which is included in the calculation.
 *
 * Here are some examples of how the word and character calculations are made:
 *
 * 		<paragraph>foo</paragraph>
 * 		<paragraph>bar</paragraph>
 * 		// Words: 2, Characters: 7
 *
 * 		<paragraph><$text bold="true">foo</$text>bar</paragraph>
 * 		// Words: 1, Characters: 6
 *
 * 		<paragraph>*&^%)</paragraph>
 * 		// Words: 0, Characters: 5
 *
 * 		<paragraph>foo(bar)</paragraph>
 * 		//Words: 1, Characters: 8
 *
 * 		<paragraph>12345</paragraph>
 * 		// Words: 1, Characters: 5
 *
 * @extends module:core/plugin~Plugin
 */
class WordCount extends plugin_Plugin {
	/**
	 * @inheritDoc
	 */
	constructor( editor ) {
		super( editor );

		/**
		 * The number of characters in the editor.
		 *
		 * @observable
		 * @readonly
		 * @member {Number} module:word-count/wordcount~WordCount#characters
		 */
		this.set( 'characters', 0 );

		/**
		 * The number of words in the editor.
		 *
		 * @observable
		 * @readonly
		 * @member {Number} module:word-count/wordcount~WordCount#words
		 */
		this.set( 'words', 0 );

		// Don't wait for the #update event to set the value of the properties but obtain it right away.
		// This way, accessing the properties directly returns precise numbers, e.g. for validation, etc.
		// If not accessed directly, the properties will be refreshed upon #update anyway.
		Object.defineProperties( this, {
			characters: {
				get() {
					return ( this.characters = this._getCharacters() );
				}
			},
			words: {
				get() {
					return ( this.words = this._getWords() );
				}
			}
		} );

		/**
		 * The label used to display the words value in the {@link #wordCountContainer output container}.
		 *
		 * @observable
		 * @private
		 * @readonly
		 * @member {String} module:word-count/wordcount~WordCount#_wordsLabel
		 */
		this.set( '_wordsLabel' );

		/**
		 * The label used to display the characters value in the {@link #wordCountContainer output container}.
		 *
		 * @observable
		 * @private
		 * @readonly
		 * @member {String} module:word-count/wordcount~WordCount#_charactersLabel
		 */
		this.set( '_charactersLabel' );

		/**
		 * The configuration of this plugin.
		 *
		 * @private
		 * @type {Object}
		 */
		this._config = editor.config.get( 'wordCount' ) || {};

		/**
		 * The reference to a {@link module:ui/view~View view object} that contains the self-updating HTML container.
		 *
		 * @private
		 * @readonly
		 * @type {module:ui/view~View}
		 */
		this._outputView = undefined;

		/**
		 * A regular expression used to recognize words in the editor's content.
		 *
		 * @readonly
		 * @private
		 * @type {RegExp}
		 */
		this._wordsMatchRegExp = src_env.features.isRegExpUnicodePropertySupported ?
			// Usage of regular expression literal cause error during build (ckeditor/ckeditor5-dev#534).
			// Groups:
			// {L} - Any kind of letter from any language.
			// {N} - Any kind of numeric character in any script.
			new RegExp( '([\\p{L}\\p{N}]+\\S?)+', 'gu' ) :
			/([a-zA-Z0-9À-ž]+\S?)+/gu;
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'WordCount';
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		editor.model.document.on( 'change:data', lodash_es_throttle( this._refreshStats.bind( this ), 250 ) );

		if ( typeof this._config.onUpdate == 'function' ) {
			this.on( 'update', ( evt, data ) => {
				this._config.onUpdate( data );
			} );
		}

		if ( lodash_es_isElement( this._config.container ) ) {
			this._config.container.appendChild( this.wordCountContainer );
		}
	}

	/**
	 * @inheritDoc
	 */
	destroy() {
		if ( this._outputView ) {
			this._outputView.element.remove();
			this._outputView.destroy();
		}

		super.destroy();
	}

	/**
	 * Creates a self-updating HTML element. Repeated executions return the same element.
	 * The returned element has the following HTML structure:
	 *
	 * 		<div class="ck ck-word-count">
	 * 			<div class="ck-word-count__words">Words: 4</div>
	 * 			<div class="ck-word-count__characters">Characters: 28</div>
	 * 		</div>
	 *
	 * @type {HTMLElement}
	 */
	get wordCountContainer() {
		const editor = this.editor;
		const t = editor.t;
		const displayWords = editor.config.get( 'wordCount.displayWords' );
		const displayCharacters = editor.config.get( 'wordCount.displayCharacters' );
		const bind = Template.bind( this, this );
		const children = [];

		if ( !this._outputView ) {
			this._outputView = new src_view_View();

			if ( displayWords || displayWords === undefined ) {
				this.bind( '_wordsLabel' ).to( this, 'words', words => {
					return t( 'Words: %0', words );
				} );

				children.push( {
					tag: 'div',
					children: [
						{
							text: [ bind.to( '_wordsLabel' ) ]
						}
					],
					attributes: {
						class: 'ck-word-count__words'
					}
				} );
			}

			if ( displayCharacters || displayCharacters === undefined ) {
				this.bind( '_charactersLabel' ).to( this, 'characters', words => {
					return t( 'Characters: %0', words );
				} );

				children.push( {
					tag: 'div',
					children: [
						{
							text: [ bind.to( '_charactersLabel' ) ]
						}
					],
					attributes: {
						class: 'ck-word-count__characters'
					}
				} );
			}

			this._outputView.setTemplate( {
				tag: 'div',
				attributes: {
					class: [
						'ck',
						'ck-word-count'
					]
				},
				children
			} );

			this._outputView.render();
		}

		return this._outputView.element;
	}

	/**
	 * Determines the number of characters in the current editor's model.
	 *
	 * @private
	 * @returns {Number}
	 */
	_getCharacters() {
		const txt = modelElementToPlainText( this.editor.model.document.getRoot() );

		return txt.replace( /\n/g, '' ).length;
	}

	/**
	 * Determines the number of words in the current editor's model.
	 *
	 * @private
	 * @returns {Number}
	 */
	_getWords() {
		const txt = modelElementToPlainText( this.editor.model.document.getRoot() );
		const detectedWords = txt.match( this._wordsMatchRegExp ) || [];

		return detectedWords.length;
	}

	/**
	 * Determines the number of words and characters in the current editor's model and assigns it to {@link #characters} and {@link #words}.
	 * It also fires the {@link #event:update}.
	 *
	 * @private
	 * @fires update
	 */
	_refreshStats() {
		const words = this.words = this._getWords();
		const characters = this.characters = this._getCharacters();

		this.fire( 'update', {
			words,
			characters
		} );
	}
}

/**
 * An event fired after {@link #words} and {@link #characters} are updated.
 *
 * @event update
 * @param {Object} data
 * @param {Number} data.words The number of words in the current model.
 * @param {Number} data.characters The number of characters in the current model.
 */

/**
 * The configuration of the word count feature.
 *
 *		ClassicEditor
 *			.create( {
 *				wordCount: ... // Word count feature configuration.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface module:word-count/wordcount~WordCountConfig
 */

/**
 * The configuration of the word count feature.
 * It is introduced by the {@link module:word-count/wordcount~WordCount} feature.
 *
 * Read more in {@link module:word-count/wordcount~WordCountConfig}.
 *
 * @member {module:word-count/wordcount~WordCountConfig} module:core/editor/editorconfig~EditorConfig#wordCount
 */

/**
 * This option allows for hiding the word counter. The element obtained through
 * {@link module:word-count/wordcount~WordCount#wordCountContainer} will only preserve
 * the characters part. Word counter is displayed by default when this configuration option is not defined.
 *
 *		const wordCountConfig = {
 *			displayWords: false
 *		};
 *
 * The configuration above will result in the following container:
 *
 *		<div class="ck ck-word-count">
 *			<div class="ck-word-count__characters">Characters: 28</div>
 *		</div>
 *
 * @member {Boolean} module:word-count/wordcount~WordCountConfig#displayWords
 */

/**
 * This option allows for hiding the character counter. The element obtained through
 * {@link module:word-count/wordcount~WordCount#wordCountContainer} will only preserve
 * the words part. Character counter is displayed by default when this configuration option is not defined.
 *
 *		const wordCountConfig = {
 *			displayCharacters: false
 *		};
 *
 * The configuration above will result in the following container:
 *
 *		<div class="ck ck-word-count">
 *			<div class="ck-word-count__words">Words: 4</div>
 *		</div>
 *
 * @member {Boolean} module:word-count/wordcount~WordCountConfig#displayCharacters
 */

/**
 * This configuration takes a function that is executed whenever the word count plugin updates its values.
 * This function is called with one argument, which is an object with the `words` and `characters` keys containing
 * the number of detected words and characters in the document.
 *
 *		const wordCountConfig = {
 *			onUpdate: function( stats ) {
 *				doSthWithWordNumber( stats.words );
 *				doSthWithCharacterNumber( stats.characters );
 *			}
 *		};
 *
 * @member {Function} module:word-count/wordcount~WordCountConfig#onUpdate
 */

/**
 * Allows for providing the HTML element that the
 * {@link module:word-count/wordcount~WordCount#wordCountContainer word count container} will be appended to automatically.
 *
 *		const wordCountConfig = {
 *			container: document.getElementById( 'container-for-word-count' );
 *		};
 *
 * @member {HTMLElement} module:word-count/wordcount~WordCountConfig#container
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-watchdog/src/utils/getsubnodes.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module watchdog/utils/getsubnodes
 */

/* globals EventTarget, Event */

function getsubnodes_getSubNodes( head, excludedProperties = new Set() ) {
	const nodes = [ head ];

	// @if CK_DEBUG_WATCHDOG // const prevNodeMap = new Map();

	// Nodes are stored to prevent infinite looping.
	const subNodes = new Set();
	let nodeIndex = 0;

	while ( nodes.length > nodeIndex ) {
		// Incrementing the iterator is much faster than changing size of the array with Array.prototype.shift().
		const node = nodes[ nodeIndex++ ];

		if ( subNodes.has( node ) || shouldNodeBeSkipped( node ) || excludedProperties.has( node ) ) {
			continue;
		}

		subNodes.add( node );

		// Handle arrays, maps, sets, custom collections that implements `[ Symbol.iterator ]()`, etc.
		if ( node[ Symbol.iterator ] ) {
			// The custom editor iterators might cause some problems if the editor is crashed.
			try {
				for ( const n of node ) {
					nodes.push( n );

					// @if CK_DEBUG_WATCHDOG // if ( !prevNodeMap.has( n ) ) {
					// @if CK_DEBUG_WATCHDOG // 	prevNodeMap.set( n, node );
					// @if CK_DEBUG_WATCHDOG // }
				}
			} catch ( err ) {
				// Do not log errors for broken structures
				// since we are in the error handling process already.
				// eslint-disable-line no-empty
			}
		} else {
			for ( const key in node ) {
				// We share a reference via the protobuf library within the editors,
				// hence the shared value should be skipped. Although, it's not a perfect
				// solution since new places like that might occur in the future.
				if ( key === 'defaultValue' ) {
					continue;
				}

				nodes.push( node[ key ] );

				// @if CK_DEBUG_WATCHDOG // if ( !prevNodeMap.has( node[ key ] ) ) {
				// @if CK_DEBUG_WATCHDOG // 	prevNodeMap.set( node[ key ], node );
				// @if CK_DEBUG_WATCHDOG // }
			}
		}
	}

	// @if CK_DEBUG_WATCHDOG // return { subNodes, prevNodeMap };

	return subNodes;
}

function shouldNodeBeSkipped( node ) {
	const type = Object.prototype.toString.call( node );
	const typeOfNode = typeof node;

	return (
		typeOfNode === 'number' ||
		typeOfNode === 'boolean' ||
		typeOfNode === 'string' ||
		typeOfNode === 'symbol' ||
		typeOfNode === 'function' ||
		type === '[object Date]' ||
		type === '[object RegExp]' ||
		type === '[object Module]' ||

		node === undefined ||
		node === null ||

		// This flag is meant to exclude singletons shared across editor instances. So when an error is thrown in one editor,
		// the other editors connected through the reference to the same singleton are not restarted. This is a temporary workaround
		// until a better solution is found.
		// More in https://github.com/ckeditor/ckeditor5/issues/12292.
		node._watchdogExcluded === true ||

		// Skip native DOM objects, e.g. Window, nodes, events, etc.
		node instanceof EventTarget ||
		node instanceof Event
	);
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-watchdog/src/utils/areconnectedthroughproperties.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module watchdog/utils/areconnectedthroughproperties
 */

/* globals console */



/**
 * Traverses both structures to find out whether there is a reference that is shared between both structures.
 *
 * @param {Object|Array} target1
 * @param {Object|Array} target2
 * @returns {Boolean}
 */
function areConnectedThroughProperties( target1, target2, excludedNodes = new Set() ) {
	if ( target1 === target2 && areconnectedthroughproperties_isObject( target1 ) ) {
		return true;
	}

	// @if CK_DEBUG_WATCHDOG // return checkConnectionBetweenProps( target1, target2, excludedNodes );

	const subNodes1 = getsubnodes_getSubNodes( target1, excludedNodes );
	const subNodes2 = getsubnodes_getSubNodes( target2, excludedNodes );

	for ( const node of subNodes1 ) {
		if ( subNodes2.has( node ) ) {
			return true;
		}
	}

	return false;
}

/* istanbul ignore next */
// eslint-disable-next-line
function checkConnectionBetweenProps( target1, target2, excludedNodes ) {
	const { subNodes: subNodes1, prevNodeMap: prevNodeMap1 } = getSubNodes( target1, excludedNodes.subNodes );
	const { subNodes: subNodes2, prevNodeMap: prevNodeMap2 } = getSubNodes( target2, excludedNodes.subNodes );

	for ( const sharedNode of subNodes1 ) {
		if ( subNodes2.has( sharedNode ) ) {
			const connection = [];

			connection.push( sharedNode );

			let node = prevNodeMap1.get( sharedNode );

			while ( node && node !== target1 ) {
				connection.push( node );
				node = prevNodeMap1.get( node );
			}

			node = prevNodeMap2.get( sharedNode );

			while ( node && node !== target2 ) {
				connection.unshift( node );
				node = prevNodeMap2.get( node );
			}

			console.log( '--------' );
			console.log( { target1 } );
			console.log( { sharedNode } );
			console.log( { target2 } );
			console.log( { connection } );

			return true;
		}
	}

	return false;
}

function areconnectedthroughproperties_isObject( structure ) {
	return typeof structure === 'object' && structure !== null;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-watchdog/src/watchdog.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module watchdog/watchdog
 */

/* globals window */

/**
 * An abstract watchdog class that handles most of the error handling process and the state of the underlying component.
 *
 * See the {@glink features/watchdog Watchdog feature guide} to learn the rationale behind it and how to use it.
 *
 * @private
 * @abstract
 */
class Watchdog {
	/**
	 * @param {module:watchdog/watchdog~WatchdogConfig} config The watchdog plugin configuration.
	 */
	constructor( config ) {
		/**
		 * An array of crashes saved as an object with the following properties:
		 *
		 * * `message`: `String`,
		 * * `stack`: `String`,
		 * * `date`: `Number`,
		 * * `filename`: `String | undefined`,
		 * * `lineno`: `Number | undefined`,
		 * * `colno`: `Number | undefined`,
		 *
		 * @public
		 * @readonly
		 * @type {Array.<Object>}
		 */
		this.crashes = [];

		/**
		 * Specifies the state of the item watched by the watchdog. The state can be one of the following values:
		 *
		 * * `initializing` &ndash; Before the first initialization, and after crashes, before the item is ready.
		 * * `ready` &ndash; A state when the user can interact with the item.
		 * * `crashed` &ndash; A state when an error occurs. It quickly changes to `initializing` or `crashedPermanently`
		 * depending on how many and how frequent errors have been caught recently.
		 * * `crashedPermanently` &ndash; A state when the watchdog stops reacting to errors and keeps the item it is watching crashed,
		 * * `destroyed` &ndash; A state when the item is manually destroyed by the user after calling `watchdog.destroy()`.
		 *
		 * @public
		 * @type {'initializing'|'ready'|'crashed'|'crashedPermanently'|'destroyed'}
		 */
		this.state = 'initializing';

		/**
		 * @protected
		 * @type {Number}
		 * @see module:watchdog/watchdog~WatchdogConfig
		 */
		this._crashNumberLimit = typeof config.crashNumberLimit === 'number' ? config.crashNumberLimit : 3;

		/**
		 * Returns the result of the `Date.now()` call. It can be overridden in tests to mock time as some popular
		 * approaches like `sinon.useFakeTimers()` do not work well with error handling.
		 *
		 * @protected
		 */
		this._now = Date.now;

		/**
		 * @protected
		 * @type {Number}
		 * @see module:watchdog/watchdog~WatchdogConfig
		 */
		this._minimumNonErrorTimePeriod = typeof config.minimumNonErrorTimePeriod === 'number' ? config.minimumNonErrorTimePeriod : 5000;

		/**
		 * Checks if the event error comes from the underlying item and restarts the item.
		 *
		 * @private
		 * @type {Function}
		 */
		this._boundErrorHandler = evt => {
			// `evt.error` is exposed by EventError while `evt.reason` is available in PromiseRejectionEvent.
			const error = evt.error || evt.reason;

			// Note that `evt.reason` might be everything that is in the promise rejection.
			// Similarly everything that is thrown lands in `evt.error`.
			if ( error instanceof Error ) {
				this._handleError( error, evt );
			}
		};

		/**
		 * The creation method.
		 *
		 * @protected
		 * @member {Function} #_creator
		 * @see #setCreator
		 */

		/**
		 * The destruction method.
		 *
		 * @protected
		 * @member {Function} #_destructor
		 * @see #setDestructor
		 */

		/**
		 * The watched item.
		 *
		 * @abstract
		 * @protected
		 * @member {Object|undefined} #_item
		 */

		/**
		 * The method responsible for restarting the watched item.
		 *
		 * @abstract
		 * @protected
		 * @method #_restart
		 */

		/**
		 * Traverses the error context and the watched item to find out whether the error should
		 * be handled by the given item.
		 *
		 * @abstract
		 * @protected
		 * @method #_isErrorComingFromThisItem
		 * @param {module:utils/ckeditorerror~CKEditorError} error
		 */

		/**
		 * A dictionary of event emitter listeners.
		 *
		 * @private
		 * @type {Object.<String,Array.<Function>>}
		 */
		this._listeners = {};

		if ( !this._restart ) {
			throw new Error(
				'The Watchdog class was split into the abstract `Watchdog` class and the `EditorWatchdog` class. ' +
				'Please, use `EditorWatchdog` if you have used the `Watchdog` class previously.'
			);
		}
	}

	/**
	 * Sets the function that is responsible for creating watched items.
	 *
	 * @param {Function} creator A callback responsible for creating an item. Returns a promise
	 * that is resolved when the item is created.
	 */
	setCreator( creator ) {
		this._creator = creator;
	}

	/**
	 * Sets the function that is responsible for destroying watched items.
	 *
	 * @param {Function} destructor A callback that takes the item and returns the promise
	 * to the destroying process.
	 */
	setDestructor( destructor ) {
		this._destructor = destructor;
	}

	/**
	 * Destroys the watchdog and releases the resources.
	 */
	destroy() {
		this._stopErrorHandling();

		this._listeners = {};
	}

	/**
	 * Starts listening to a specific event name by registering a callback that will be executed
	 * whenever an event with a given name fires.
	 *
	 * Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
	 *
	 * @param {String} eventName The event name.
	 * @param {Function} callback A callback which will be added to event listeners.
	 */
	on( eventName, callback ) {
		if ( !this._listeners[ eventName ] ) {
			this._listeners[ eventName ] = [];
		}

		this._listeners[ eventName ].push( callback );
	}

	/**
	 * Stops listening to the specified event name by removing the callback from event listeners.
	 *
	 * Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
	 *
	 * @param {String} eventName The event name.
	 * @param {Function} callback A callback which will be removed from event listeners.
	 */
	off( eventName, callback ) {
		this._listeners[ eventName ] = this._listeners[ eventName ]
			.filter( cb => cb !== callback );
	}

	/**
	 * Fires an event with a given event name and arguments.
	 *
	 * Note that this method differs from the CKEditor 5's default `EventEmitterMixin` implementation.
	 *
	 * @protected
	 * @param {String} eventName The event name.
	 * @param  {...*} args Event arguments.
	 */
	_fire( eventName, ...args ) {
		const callbacks = this._listeners[ eventName ] || [];

		for ( const callback of callbacks ) {
			callback.apply( this, [ null, ...args ] );
		}
	}

	/**
	 * Starts error handling by attaching global error handlers.
	 *
	 * @protected
	 */
	_startErrorHandling() {
		window.addEventListener( 'error', this._boundErrorHandler );
		window.addEventListener( 'unhandledrejection', this._boundErrorHandler );
	}

	/**
	 * Stops error handling by detaching global error handlers.
	 *
	 * @protected
	 */
	_stopErrorHandling() {
		window.removeEventListener( 'error', this._boundErrorHandler );
		window.removeEventListener( 'unhandledrejection', this._boundErrorHandler );
	}

	/**
	 * Checks if an error comes from the watched item and restarts it.
	 * It reacts to {@link module:utils/ckeditorerror~CKEditorError `CKEditorError` errors} only.
	 *
	 * @private
	 * @fires error
	 * @param {Error} error Error.
	 * @param {ErrorEvent|PromiseRejectionEvent} evt An error event.
	 */
	_handleError( error, evt ) {
		// @if CK_DEBUG // if ( error.is && error.is( 'CKEditorError' ) && error.context === undefined ) {
		// @if CK_DEBUG // console.warn( 'The error is missing its context and Watchdog cannot restart the proper item.' );
		// @if CK_DEBUG // }

		if ( this._shouldReactToError( error ) ) {
			this.crashes.push( {
				message: error.message,
				stack: error.stack,

				// `evt.filename`, `evt.lineno` and `evt.colno` are available only in ErrorEvent events
				filename: evt.filename,
				lineno: evt.lineno,
				colno: evt.colno,
				date: this._now()
			} );

			const causesRestart = this._shouldRestart();

			this.state = 'crashed';
			this._fire( 'stateChange' );
			this._fire( 'error', { error, causesRestart } );

			if ( causesRestart ) {
				this._restart();
			} else {
				this.state = 'crashedPermanently';
				this._fire( 'stateChange' );
			}
		}
	}

	/**
	 * Checks whether an error should be handled by the watchdog.
	 *
	 * @private
	 * @param {Error} error An error that was caught by the error handling process.
	 */
	_shouldReactToError( error ) {
		return (
			error.is &&
			error.is( 'CKEditorError' ) &&
			error.context !== undefined &&

			// In some cases the watched item should not be restarted - e.g. during the item initialization.
			// That's why the `null` was introduced as a correct error context which does cause restarting.
			error.context !== null &&

			// Do not react to errors if the watchdog is in states other than `ready`.
			this.state === 'ready' &&

			this._isErrorComingFromThisItem( error )
		);
	}

	/**
	 * Checks if the watchdog should restart the underlying item.
	 *
	 * @private
	 */
	_shouldRestart() {
		if ( this.crashes.length <= this._crashNumberLimit ) {
			return true;
		}

		const lastErrorTime = this.crashes[ this.crashes.length - 1 ].date;
		const firstMeaningfulErrorTime = this.crashes[ this.crashes.length - 1 - this._crashNumberLimit ].date;

		const averageNonErrorTimePeriod = ( lastErrorTime - firstMeaningfulErrorTime ) / this._crashNumberLimit;

		return averageNonErrorTimePeriod > this._minimumNonErrorTimePeriod;
	}

	/**
	 * Fired when a new {@link module:utils/ckeditorerror~CKEditorError `CKEditorError`} error connected to the watchdog instance occurs
	 * and the watchdog will react to it.
	 *
	 * 	watchdog.on( 'error', ( evt, { error, causesRestart } ) => {
	 * 		console.log( 'An error occurred.' );
	 * 	} );
	 *
	 * @event error
	 */
}

/**
 * The watchdog plugin configuration.
 *
 * @typedef {Object} WatchdogConfig
 *
 * @property {Number} [crashNumberLimit=3] A threshold specifying the number of watched item crashes
 * when the watchdog stops restarting the item in case of errors.
 * After this limit is reached and the time between the last errors is shorter than `minimumNonErrorTimePeriod`,
 * the watchdog changes its state to `crashedPermanently` and it stops restarting the item. This prevents an infinite restart loop.
 *
 * @property {Number} [minimumNonErrorTimePeriod=5000] An average number of milliseconds between the last watched item errors
 * (defaults to 5000). When the period of time between errors is lower than that and the `crashNumberLimit` is also reached,
 * the watchdog changes its state to `crashedPermanently` and it stops restarting the item. This prevents an infinite restart loop.
 *
 * @property {Number} [saveInterval=5000] A minimum number of milliseconds between saving the editor data internally (defaults to 5000).
 * Note that for large documents this might impact the editor performance.
 */

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-watchdog/src/editorwatchdog.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module watchdog/editorwatchdog
 */

/* globals console */





/**
 * A watchdog for CKEditor 5 editors.
 *
 * See the {@glink features/watchdog Watchdog feature guide} to learn the rationale behind it and
 * how to use it.
 *
 * @extends {module:watchdog/watchdog~Watchdog}
 */
class EditorWatchdog extends Watchdog {
	/**
	 * @param {*} Editor The editor class.
	 * @param {module:watchdog/watchdog~WatchdogConfig} [watchdogConfig] The watchdog plugin configuration.
	 */
	constructor( Editor, watchdogConfig = {} ) {
		super( watchdogConfig );

		/**
		 * The current editor instance.
		 *
		 * @private
		 * @type {module:core/editor/editor~Editor}
		 */
		this._editor = null;

		/**
		 * Throttled save method. The `save()` method is called the specified `saveInterval` after `throttledSave()` is called,
		 * unless a new action happens in the meantime.
		 *
		 * @private
		 * @type {Function}
		 */
		this._throttledSave = lodash_es_throttle(
			this._save.bind( this ),
			typeof watchdogConfig.saveInterval === 'number' ? watchdogConfig.saveInterval : 5000
		);

		/**
		 * The latest saved editor data represented as a root name -> root data object.
		 *
		 * @private
		 * @member {Object.<String,String>} #_data
		 */

		/**
		 * The last document version.
		 *
		 * @private
		 * @member {Number} #_lastDocumentVersion
		 */

		/**
		 * The editor source element or data.
		 *
		 * @private
		 * @member {HTMLElement|String|Object.<String|String>} #_elementOrData
		 */

		/**
		 * The editor configuration.
		 *
		 * @private
		 * @member {Object|undefined} #_config
		 */

		// Set default creator and destructor functions:
		this._creator = ( ( elementOrData, config ) => Editor.create( elementOrData, config ) );
		this._destructor = editor => editor.destroy();
	}

	/**
	 * The current editor instance.
	 *
	 * @readonly
	 * @type {module:core/editor/editor~Editor}
	 */
	get editor() {
		return this._editor;
	}

	/**
	 * @inheritDoc
	 */
	get _item() {
		return this._editor;
	}

	/**
	 * Sets the function that is responsible for the editor creation.
	 * It expects a function that should return a promise.
	 *
	 *		watchdog.setCreator( ( element, config ) => ClassicEditor.create( element, config ) );
	 *
	 * @method #setCreator
	 * @param {Function} creator
	 */

	/**
	 * Sets the function that is responsible for the editor destruction.
	 * Overrides the default destruction function, which destroys only the editor instance.
	 * It expects a function that should return a promise or `undefined`.
	 *
	 *		watchdog.setDestructor( editor => {
	 *			// Do something before the editor is destroyed.
	 *
	 *			return editor
	 *				.destroy()
	 *				.then( () => {
	 *					// Do something after the editor is destroyed.
	 *				} );
	 *		} );
	 *
	 * @method #setDestructor
	 * @param {Function} destructor
	 */

	/**
	 * Restarts the editor instance. This method is called whenever an editor error occurs. It fires the `restart` event and changes
	 * the state to `initializing`.
	 *
	 * @protected
	 * @fires restart
	 * @returns {Promise}
	 */
	_restart() {
		return Promise.resolve()
			.then( () => {
				this.state = 'initializing';
				this._fire( 'stateChange' );

				return this._destroy();
			} )
			.catch( err => {
				console.error( 'An error happened during the editor destroying.', err );
			} )
			.then( () => {
				if ( typeof this._elementOrData === 'string' ) {
					return this.create( this._data, this._config, this._config.context );
				} else {
					const updatedConfig = Object.assign( {}, this._config, {
						initialData: this._data
					} );

					return this.create( this._elementOrData, updatedConfig, updatedConfig.context );
				}
			} )
			.then( () => {
				this._fire( 'restart' );
			} );
	}

	/**
	 * Creates the editor instance and keeps it running, using the defined creator and destructor.
	 *
	 * @param {HTMLElement|String|Object.<String|String>} [elementOrData] The editor source element or the editor data.
	 * @param {module:core/editor/editorconfig~EditorConfig} [config] The editor configuration.
	 * @param {Object} [context] A context for the editor.
	 *
	 * @returns {Promise}
	 */
	create( elementOrData = this._elementOrData, config = this._config, context ) {
		return Promise.resolve()
			.then( () => {
				super._startErrorHandling();

				this._elementOrData = elementOrData;

				// Clone configuration because it might be shared within multiple watchdog instances. Otherwise,
				// when an error occurs in one of these editors, the watchdog will restart all of them.
				this._config = this._cloneEditorConfiguration( config ) || {};

				this._config.context = context;

				return this._creator( elementOrData, this._config );
			} )
			.then( editor => {
				this._editor = editor;

				editor.model.document.on( 'change:data', this._throttledSave );

				this._lastDocumentVersion = editor.model.document.version;
				this._data = this._getData();

				this.state = 'ready';
				this._fire( 'stateChange' );
			} );
	}

	/**
	 * Destroys the watchdog and the current editor instance. It fires the callback
	 * registered in {@link #setDestructor `setDestructor()`} and uses it to destroy the editor instance.
	 * It also sets the state to `destroyed`.
	 *
	 * @returns {Promise}
	 */
	destroy() {
		return Promise.resolve()
			.then( () => {
				this.state = 'destroyed';
				this._fire( 'stateChange' );

				super.destroy();

				return this._destroy();
			} );
	}

	/**
	 * @private
	 * @returns {Promise}
	 */
	_destroy() {
		return Promise.resolve()
			.then( () => {
				this._stopErrorHandling();

				// Save data if there is a remaining editor data change.
				this._throttledSave.flush();

				const editor = this._editor;

				this._editor = null;

				// Remove the `change:data` listener before destroying the editor.
				// Incorrectly written plugins may trigger firing `change:data` events during the editor destruction phase
				// causing the watchdog to call `editor.getData()` when some parts of editor are already destroyed.
				editor.model.document.off( 'change:data', this._throttledSave );

				return this._destructor( editor );
			} );
	}

	/**
	 * Saves the editor data, so it can be restored after the crash even if the data cannot be fetched at
	 * the moment of the crash.
	 *
	 * @private
	 */
	_save() {
		const version = this._editor.model.document.version;

		try {
			this._data = this._getData();
			this._lastDocumentVersion = version;
		} catch ( err ) {
			console.error(
				err,
				'An error happened during restoring editor data. ' +
				'Editor will be restored from the previously saved data.'
			);
		}
	}

	/**
	 * @protected
	 * @param {Set} props
	 */
	_setExcludedProperties( props ) {
		this._excludedProps = props;
	}

	/**
	 * Returns the editor data.
	 *
	 * @private
	 * @returns {Object<String,String>}
	 */
	_getData() {
		const data = {};

		for ( const rootName of this._editor.model.document.getRootNames() ) {
			data[ rootName ] = this._editor.data.get( { rootName } );
		}

		return data;
	}

	/**
	 * Traverses the error context and the current editor to find out whether these structures are connected
	 * to each other via properties.
	 *
	 * @protected
	 * @param {module:utils/ckeditorerror~CKEditorError} error
	 */
	_isErrorComingFromThisItem( error ) {
		return areConnectedThroughProperties( this._editor, error.context, this._excludedProps );
	}

	/**
	 * Clones the editor configuration.
	 *
	 * @private
	 * @param {Object} config
	 */
	_cloneEditorConfiguration( config ) {
		return lodash_es_cloneDeepWith( config, ( value, key ) => {
			// Leave DOM references.
			if ( lodash_es_isElement( value ) ) {
				return value;
			}

			if ( key === 'context' ) {
				return value;
			}
		} );
	}

	/**
	 * Fired after the watchdog restarts the error in case of a crash.
	 *
	 * @event restart
	 */
}

;// CONCATENATED MODULE: ./ckeditor5-fullscreen/icons/fullscreen-big.svg
/* harmony default export */ const fullscreen_big = ("<?xml version=\"1.0\" ?><svg enable-background=\"new 0 0 32 32\" height=\"32px\" id=\"svg2\" version=\"1.1\" viewBox=\"0 0 32 32\" width=\"32px\" xml:space=\"preserve\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:svg=\"http://www.w3.org/2000/svg\"><g id=\"background\"><rect fill=\"none\" height=\"32\" width=\"32\"/></g><g id=\"fullscreen\"><path d=\"M20,8l8,8V8H20z M4,24h8l-8-8V24z\"/><path d=\"M32,28V4H0v24h14v2H8v2h16v-2h-6v-2H32z M2,26V6h28v20H2z\"/></g></svg>");
;// CONCATENATED MODULE: ./ckeditor5-fullscreen/icons/fullscreen-cancel.svg
/* harmony default export */ const fullscreen_cancel = ("<?xml version=\"1.0\" ?><svg enable-background=\"new 0 0 32 32\" height=\"32px\" id=\"svg2\" version=\"1.1\" viewBox=\"0 0 32 32\" width=\"32px\" xml:space=\"preserve\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:cc=\"http://creativecommons.org/ns#\" xmlns:dc=\"http://purl.org/dc/elements/1.1/\" xmlns:inkscape=\"http://www.inkscape.org/namespaces/inkscape\" xmlns:rdf=\"http://www.w3.org/1999/02/22-rdf-syntax-ns#\" xmlns:sodipodi=\"http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd\" xmlns:svg=\"http://www.w3.org/2000/svg\"><g id=\"background\"><rect fill=\"none\" height=\"32\" width=\"32\"/></g><g id=\"fullscreen_x5F_cancel\"><path d=\"M4,16v8h8L4,16z M0,4v24h14v2H8v2h16v-0.06c2.702-0.299,5.042-1.791,6.481-3.94H32V4H0z M23,29.999   c-3.865-0.008-6.994-3.135-7-6.999c0.006-3.865,3.135-6.994,7-7c3.864,0.006,6.991,3.135,6.999,7   C29.991,26.864,26.864,29.991,23,29.999z M30,17.35c-0.57-0.707-1.244-1.326-2-1.832V8h-8l6.896,6.896   C25.717,14.328,24.398,14,23,14c-4.972,0-9,4.028-9,9c0,1.054,0.19,2.061,0.523,3H2V6h28V17.35z\"/><polygon points=\"19,25 21,27 23,25 25,27 27,25 25,23 27,21 25,19 23,21 21,19 19,21 21,23  \"/></g></svg>");
// EXTERNAL MODULE: ./node_modules/css-loader/dist/cjs.js!./node_modules/postcss-loader/dist/cjs.js??ruleSet[1].rules[1].use[2]!./ckeditor5-fullscreen/css/style.css
var style = __webpack_require__(277);
;// CONCATENATED MODULE: ./ckeditor5-fullscreen/css/style.css

            

var style_options = {"injectType":"singletonStyleTag","attributes":{"data-cke":true}};

style_options.insert = "head";
style_options.singleton = true;

var style_update = injectStylesIntoStyleTag_default()(style/* default */.Z, style_options);



/* harmony default export */ const css_style = (style/* default.locals */.Z.locals || {});
;// CONCATENATED MODULE: ./ckeditor5-fullscreen/FullScreen.js






class FullScreen extends plugin_Plugin {
    init() {
        const editor = this.editor;

        editor.ui.componentFactory.add( 'fullScreen', locale => {
            const view = new buttonview_ButtonView( locale );
            let etat = 0; //si 0 position normale
            view.set( {
                label: 'Enable fullscreen mode',
                icon: fullscreen_big,
                tooltip: true
            } );

            // Callback executed once the image is clicked.
            view.on( 'execute', () => {
                if(etat==1){
                    editor.sourceElement.nextElementSibling.removeAttribute('id');
                    document.body.removeAttribute('id');
                    view.set( {
                        label: 'Enable fullscreen mode',
                        icon: fullscreen_big,
                        tooltip: true
                    } );
                    etat=0;
                }else{
                    editor.sourceElement.nextElementSibling.setAttribute("id", 'fullscreeneditor');
                    document.body.setAttribute("id", "fullscreenoverlay");
                    view.set( {
                        label: 'Disable fullscreen mode',
                        icon: fullscreen_cancel,
                        tooltip: true
                    } );
                    etat=1;
                }
            } );

            return view;
        } );
    }
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/list/listcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/list/listcommand
 */




/**
 * The list command. It is used by the {@link module:list/list~List list feature}.
 *
 * @extends module:core/command~Command
 */
class listcommand_ListCommand extends (/* unused pure expression or super */ null && (Command)) {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {'numbered'|'bulleted'} type List type that will be handled by this command.
	 */
	constructor( editor, type ) {
		super( editor );

		/**
		 * The type of the list created by the command.
		 *
		 * @readonly
		 * @member {'numbered'|'bulleted'|'todo'}
		 */
		this.type = type;

		/**
		 * A flag indicating whether the command is active, which means that the selection starts in a list of the same type.
		 *
		 * @observable
		 * @readonly
		 * @member {Boolean} #value
		 */
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.value = this._getValue();
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Executes the list command.
	 *
	 * @fires execute
	 * @param {Object} [options] Command options.
	 * @param {Boolean} [options.forceValue] If set, it will force the command behavior. If `true`, the command will try to convert the
	 * selected items and potentially the neighbor elements to the proper list items. If set to `false`, it will convert selected elements
	 * to paragraphs. If not set, the command will toggle selected elements to list items or paragraphs, depending on the selection.
	 */
	execute( options = {} ) {
		const model = this.editor.model;
		const document = model.document;
		const blocks = Array.from( document.selection.getSelectedBlocks() )
			.filter( block => checkCanBecomeListItem( block, model.schema ) );

		// Whether we are turning off some items.
		const turnOff = options.forceValue !== undefined ? !options.forceValue : this.value;

		// If we are turning off items, we are going to rename them to paragraphs.

		model.change( writer => {
			// If part of a list got turned off, we need to handle (outdent) all of sub-items of the last turned-off item.
			// To be sure that model is all the time in a good state, we first fix items below turned-off item.
			if ( turnOff ) {
				// Start from the model item that is just after the last turned-off item.
				let next = blocks[ blocks.length - 1 ].nextSibling;
				let currentIndent = Number.POSITIVE_INFINITY;
				let changes = [];

				// Correct indent of all items after the last turned off item.
				// Rules that should be followed:
				// 1. All direct sub-items of turned-off item should become indent 0, because the first item after it
				//    will be the first item of a new list. Other items are at the same level, so should have same 0 index.
				// 2. All items with indent lower than indent of turned-off item should become indent 0, because they
				//    should not end up as a child of any of list items that they were not children of before.
				// 3. All other items should have their indent changed relatively to it's parent.
				//
				// For example:
				// 1  * --------
				// 2     * --------
				// 3        * --------			<-- this is turned off.
				// 4           * --------		<-- this has to become indent = 0, because it will be first item on a new list.
				// 5              * --------	<-- this should be still be a child of item above, so indent = 1.
				// 6        * --------			<-- this has to become indent = 0, because it should not be a child of any of items above.
				// 7           * --------		<-- this should be still be a child of item above, so indent = 1.
				// 8     * --------				<-- this has to become indent = 0.
				// 9        * --------			<-- this should still be a child of item above, so indent = 1.
				// 10          * --------		<-- this should still be a child of item above, so indent = 2.
				// 11          * --------		<-- this should still be at the same level as item above, so indent = 2.
				// 12 * --------				<-- this and all below are left unchanged.
				// 13    * --------
				// 14       * --------
				//
				// After turning off 3 the list becomes:
				//
				// 1  * --------
				// 2     * --------
				//
				// 3  --------
				//
				// 4  * --------
				// 5     * --------
				// 6  * --------
				// 7     * --------
				// 8  * --------
				// 9     * --------
				// 10       * --------
				// 11       * --------
				// 12 * --------
				// 13    * --------
				// 14       * --------
				//
				// Thanks to this algorithm no lists are mismatched and no items get unexpected children/parent, while
				// those parent-child connection which are possible to maintain are still maintained. It's worth noting
				// that this is the same effect that we would be get by multiple use of outdent command. However doing
				// it like this is much more efficient because it's less operation (less memory usage, easier OT) and
				// less conversion (faster).
				while ( next && next.name == 'listItem' && next.getAttribute( 'listIndent' ) !== 0 ) {
					// Check each next list item, as long as its indent is bigger than 0.
					// If the indent is 0 we are not going to change anything anyway.
					const indent = next.getAttribute( 'listIndent' );

					// We check if that's item indent is lower as current relative indent.
					if ( indent < currentIndent ) {
						// If it is, current relative indent becomes that indent.
						currentIndent = indent;
					}

					// Fix indent relatively to current relative indent.
					// Note, that if we just changed the current relative indent, the newIndent will be equal to 0.
					const newIndent = indent - currentIndent;

					// Save the entry in changes array. We do not apply it at the moment, because we will need to
					// reverse the changes so the last item is changed first.
					// This is to keep model in correct state all the time.
					changes.push( { element: next, listIndent: newIndent } );

					// Find next item.
					next = next.nextSibling;
				}

				changes = changes.reverse();

				for ( const item of changes ) {
					writer.setAttribute( 'listIndent', item.listIndent, item.element );
				}
			}

			// If we are turning on, we might change some items that are already `listItem`s but with different type.
			// Changing one nested list item to other type should also trigger changing all its siblings so the
			// whole nested list is of the same type.
			// Example (assume changing to numbered list):
			// * ------				<-- do not fix, top level item
			//   * ------			<-- fix, because latter list item of this item's list is changed
			//      * ------		<-- do not fix, item is not affected (different list)
			//   * ------			<-- fix, because latter list item of this item's list is changed
			//      * ------		<-- fix, because latter list item of this item's list is changed
			//      * ---[--		<-- already in selection
			//   * ------			<-- already in selection
			//   * ------			<-- already in selection
			// * ------				<-- already in selection, but does not cause other list items to change because is top-level
			//   * ---]--			<-- already in selection
			//   * ------			<-- fix, because preceding list item of this item's list is changed
			//      * ------		<-- do not fix, item is not affected (different list)
			// * ------				<-- do not fix, top level item
			if ( !turnOff ) {
				// Find lowest indent among selected items. This will be indicator what is the indent of
				// top-most list affected by the command.
				let lowestIndent = Number.POSITIVE_INFINITY;

				for ( const item of blocks ) {
					if ( item.is( 'element', 'listItem' ) && item.getAttribute( 'listIndent' ) < lowestIndent ) {
						lowestIndent = item.getAttribute( 'listIndent' );
					}
				}

				// Do not execute the fix for top-level lists.
				lowestIndent = lowestIndent === 0 ? 1 : lowestIndent;

				// Fix types of list items that are "before" the selected blocks.
				_fixType( blocks, true, lowestIndent );

				// Fix types of list items that are "after" the selected blocks.
				_fixType( blocks, false, lowestIndent );
			}

			// Phew! Now it will be easier :).
			// For each block element that was in the selection, we will either: turn it to list item,
			// turn it to paragraph, or change it's type. Or leave it as it is.
			// Do it in reverse as there might be multiple blocks (same as with changing indents).
			for ( const element of blocks.reverse() ) {
				if ( turnOff && element.name == 'listItem' ) {
					// We are turning off and the element is a `listItem` - it should be converted to `paragraph`.
					// List item specific attributes are removed by post fixer.
					writer.rename( element, 'paragraph' );
				} else if ( !turnOff && element.name != 'listItem' ) {
					// We are turning on and the element is not a `listItem` - it should be converted to `listItem`.
					// The order of operations is important to keep model in correct state.
					writer.setAttributes( { listType: this.type, listIndent: 0 }, element );
					writer.rename( element, 'listItem' );
				} else if ( !turnOff && element.name == 'listItem' && element.getAttribute( 'listType' ) != this.type ) {
					// We are turning on and the element is a `listItem` but has different type - change it's type and
					// type of it's all siblings that have same indent.
					writer.setAttribute( 'listType', this.type, element );
				}
			}

			/**
			 * Event fired by the {@link #execute} method.
			 *
			 * It allows to execute an action after executing the {@link ~ListCommand#execute} method, for example adjusting
			 * attributes of changed blocks.
			 *
			 * @protected
			 * @event _executeCleanup
			 */
			this.fire( '_executeCleanup', blocks );
		} );
	}

	/**
	 * Checks the command's {@link #value}.
	 *
	 * @private
	 * @returns {Boolean} The current value.
	 */
	_getValue() {
		// Check whether closest `listItem` ancestor of the position has a correct type.
		const listItem = first( this.editor.model.document.selection.getSelectedBlocks() );

		return !!listItem && listItem.is( 'element', 'listItem' ) && listItem.getAttribute( 'listType' ) == this.type;
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		// If command value is true it means that we are in list item, so the command should be enabled.
		if ( this.value ) {
			return true;
		}

		const selection = this.editor.model.document.selection;
		const schema = this.editor.model.schema;

		const firstBlock = first( selection.getSelectedBlocks() );

		if ( !firstBlock ) {
			return false;
		}

		// Otherwise, check if list item can be inserted at the position start.
		return checkCanBecomeListItem( firstBlock, schema );
	}
}

// Helper function used when one or more list item have their type changed. Fixes type of other list items
// that are affected by the change (are in same lists) but are not directly in selection. The function got extracted
// not to duplicated code, as same fix has to be performed before and after selection.
//
// @param {Array.<module:engine/model/node~Node>} blocks Blocks that are in selection.
// @param {Boolean} isBackward Specified whether fix will be applied for blocks before first selected block (`true`)
// or blocks after last selected block (`false`).
// @param {Number} lowestIndent Lowest indent among selected blocks.
function _fixType( blocks, isBackward, lowestIndent ) {
	// We need to check previous sibling of first changed item and next siblings of last changed item.
	const startingItem = isBackward ? blocks[ 0 ] : blocks[ blocks.length - 1 ];

	if ( startingItem.is( 'element', 'listItem' ) ) {
		let item = startingItem[ isBackward ? 'previousSibling' : 'nextSibling' ];
		// During processing items, keeps the lowest indent of already processed items.
		// This saves us from changing too many items.
		// Following example is for going forward as it is easier to read, however same applies to going backward.
		// * ------
		//   * ------
		//     * --[---
		//   * ------		<-- `lowestIndent` should be 1
		//     * --]---		<-- `startingItem`, `currentIndent` = 2, `lowestIndent` == 1
		//     * ------		<-- should be fixed, `indent` == 2 == `currentIndent`
		//   * ------		<-- should be fixed, set `currentIndent` to 1, `indent` == 1 == `currentIndent`
		//     * ------		<-- should not be fixed, item is in different list, `indent` = 2, `indent` != `currentIndent`
		//   * ------		<-- should be fixed, `indent` == 1 == `currentIndent`
		// * ------			<-- break loop (`indent` < `lowestIndent`)
		let currentIndent = startingItem.getAttribute( 'listIndent' );

		// Look back until a list item with indent lower than reference `lowestIndent`.
		// That would be the parent of nested sublist which contains item having `lowestIndent`.
		while ( item && item.is( 'element', 'listItem' ) && item.getAttribute( 'listIndent' ) >= lowestIndent ) {
			if ( currentIndent > item.getAttribute( 'listIndent' ) ) {
				currentIndent = item.getAttribute( 'listIndent' );
			}

			// Found an item that is in the same nested sublist.
			if ( item.getAttribute( 'listIndent' ) == currentIndent ) {
				// Just add the item to selected blocks like it was selected by the user.
				blocks[ isBackward ? 'unshift' : 'push' ]( item );
			}

			item = item[ isBackward ? 'previousSibling' : 'nextSibling' ];
		}
	}
}

// Checks whether the given block can be replaced by a listItem.
//
// @private
// @param {module:engine/model/element~Element} block A block to be tested.
// @param {module:engine/model/schema~Schema} schema The schema of the document.
// @returns {Boolean}
function checkCanBecomeListItem( block, schema ) {
	return schema.checkChild( block.parent, 'listItem' ) && !schema.isObject( block );
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/list/indentcommand.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/list/indentcommand
 */




/**
 * The list indent command. It is used by the {@link module:list/list~List list feature}.
 *
 * @extends module:core/command~Command
 */
class indentcommand_IndentCommand extends (/* unused pure expression or super */ null && (Command)) {
	/**
	 * Creates an instance of the command.
	 *
	 * @param {module:core/editor/editor~Editor} editor The editor instance.
	 * @param {'forward'|'backward'} indentDirection The direction of indent. If it is equal to `backward`, the command
	 * will outdent a list item.
	 */
	constructor( editor, indentDirection ) {
		super( editor );

		/**
		 * Determines by how much the command will change the list item's indent attribute.
		 *
		 * @readonly
		 * @private
		 * @member {Number}
		 */
		this._indentBy = indentDirection == 'forward' ? 1 : -1;
	}

	/**
	 * @inheritDoc
	 */
	refresh() {
		this.isEnabled = this._checkEnabled();
	}

	/**
	 * Indents or outdents (depending on the {@link #constructor}'s `indentDirection` parameter) selected list items.
	 *
	 * @fires execute
	 * @fires _executeCleanup
	 */
	execute() {
		const model = this.editor.model;
		const doc = model.document;
		let itemsToChange = Array.from( doc.selection.getSelectedBlocks() );

		model.change( writer => {
			const lastItem = itemsToChange[ itemsToChange.length - 1 ];

			// Indenting a list item should also indent all the items that are already sub-items of indented item.
			let next = lastItem.nextSibling;

			// Check all items after last indented item, as long as their indent is bigger than indent of that item.
			while ( next && next.name == 'listItem' && next.getAttribute( 'listIndent' ) > lastItem.getAttribute( 'listIndent' ) ) {
				itemsToChange.push( next );

				next = next.nextSibling;
			}

			// We need to be sure to keep model in correct state after each small change, because converters
			// bases on that state and assumes that model is correct.
			// Because of that, if the command outdents items, we will outdent them starting from the last item, as
			// it is safer.
			if ( this._indentBy < 0 ) {
				itemsToChange = itemsToChange.reverse();
			}

			for ( const item of itemsToChange ) {
				const indent = item.getAttribute( 'listIndent' ) + this._indentBy;

				// If indent is lower than 0, it means that the item got outdented when it was not indented.
				// This means that we need to convert that list item to paragraph.
				if ( indent < 0 ) {
					// To keep the model as correct as possible, first rename listItem, then remove attributes,
					// as listItem without attributes is very incorrect and will cause problems in converters.
					// No need to remove attributes, will be removed by post fixer.
					writer.rename( item, 'paragraph' );
				}
				// If indent is >= 0, change the attribute value.
				else {
					writer.setAttribute( 'listIndent', indent, item );
				}
			}

			/**
			 * Event fired by the {@link #execute} method.
			 *
			 * It allows to execute an action after executing the {@link ~IndentCommand#execute} method, for example adjusting
			 * attributes of changed list items.
			 *
			 * @protected
			 * @event _executeCleanup
			 */
			this.fire( '_executeCleanup', itemsToChange );
		} );
	}

	/**
	 * Checks whether the command can be enabled in the current context.
	 *
	 * @private
	 * @returns {Boolean} Whether the command should be enabled.
	 */
	_checkEnabled() {
		// Check whether any of position's ancestor is a list item.
		const listItem = first( this.editor.model.document.selection.getSelectedBlocks() );

		// If selection is not in a list item, the command is disabled.
		if ( !listItem || !listItem.is( 'element', 'listItem' ) ) {
			return false;
		}

		if ( this._indentBy > 0 ) {
			// Cannot indent first item in it's list. Check if before `listItem` is a list item that is in same list.
			// To be in the same list, the item has to have same attributes and cannot be "split" by an item with lower indent.
			const indent = listItem.getAttribute( 'listIndent' );
			const type = listItem.getAttribute( 'listType' );

			let prev = listItem.previousSibling;

			while ( prev && prev.is( 'element', 'listItem' ) && prev.getAttribute( 'listIndent' ) >= indent ) {
				if ( prev.getAttribute( 'listIndent' ) == indent ) {
					// The item is on the same level.
					// If it has same type, it means that we found a preceding sibling from the same list.
					// If it does not have same type, it means that `listItem` is on different list (this can happen only
					// on top level lists, though).
					return prev.getAttribute( 'listType' ) == type;
				}

				prev = prev.previousSibling;
			}

			// Could not find similar list item, this means that `listItem` is first in its list.
			return false;
		}

		// If we are outdenting it is enough to be in list item. Every list item can always be outdented.
		return true;
	}
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/list/converters.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/list/converters
 */





/**
 * A model-to-view converter for the `listItem` model element insertion.
 *
 * It creates a `<ul><li></li><ul>` (or `<ol>`) view structure out of a `listItem` model element, inserts it at the correct
 * position, and merges the list with surrounding lists (if available).
 *
 * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert
 * @param {module:engine/model/model~Model} model Model instance.
 * @returns {Function} Returns a conversion callback.
 */
function converters_modelViewInsertion( model ) {
	return ( evt, data, conversionApi ) => {
		const consumable = conversionApi.consumable;

		if ( !consumable.test( data.item, 'insert' ) ||
			!consumable.test( data.item, 'attribute:listType' ) ||
			!consumable.test( data.item, 'attribute:listIndent' )
		) {
			return;
		}

		consumable.consume( data.item, 'insert' );
		consumable.consume( data.item, 'attribute:listType' );
		consumable.consume( data.item, 'attribute:listIndent' );

		const modelItem = data.item;
		const viewItem = generateLiInUl( modelItem, conversionApi );

		injectViewList( modelItem, viewItem, conversionApi, model );
	};
}

/**
 * A model-to-view converter for the `listItem` model element removal.
 *
 * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:remove
 * @param {module:engine/model/model~Model} model Model instance.
 * @returns {Function} Returns a conversion callback.
 */
function converters_modelViewRemove( model ) {
	return ( evt, data, conversionApi ) => {
		const viewPosition = conversionApi.mapper.toViewPosition( data.position );
		const viewStart = viewPosition.getLastMatchingPosition( value => !value.item.is( 'element', 'li' ) );
		const viewItem = viewStart.nodeAfter;
		const viewWriter = conversionApi.writer;

		// 1. Break the container after and before the list item.
		// This will create a view list with one view list item - the one to remove.
		viewWriter.breakContainer( viewWriter.createPositionBefore( viewItem ) );
		viewWriter.breakContainer( viewWriter.createPositionAfter( viewItem ) );

		// 2. Remove the list with the item to remove.
		const viewList = viewItem.parent;
		const viewListPrev = viewList.previousSibling;
		const removeRange = viewWriter.createRangeOn( viewList );
		const removed = viewWriter.remove( removeRange );

		// 3. Merge the whole created by breaking and removing the list.
		if ( viewListPrev && viewListPrev.nextSibling ) {
			mergeViewLists( viewWriter, viewListPrev, viewListPrev.nextSibling );
		}

		// 4. Bring back nested list that was in the removed <li>.
		const modelItem = conversionApi.mapper.toModelElement( viewItem );

		hoistNestedLists( modelItem.getAttribute( 'listIndent' ) + 1, data.position, removeRange.start, viewItem, conversionApi, model );

		// 5. Unbind removed view item and all children.
		for ( const child of viewWriter.createRangeIn( removed ).getItems() ) {
			conversionApi.mapper.unbindViewElement( child );
		}

		evt.stop();
	};
}

/**
 * A model-to-view converter for the `type` attribute change on the `listItem` model element.
 *
 * This change means that the `<li>` element parent changes from `<ul>` to `<ol>` (or vice versa). This is accomplished
 * by breaking view elements and changing their name. The next {@link module:list/list/converters~modelViewMergeAfterChangeType}
 * converter will attempt to merge split nodes.
 *
 * Splitting this conversion into 2 steps makes it possible to add an additional conversion in the middle.
 * Check {@link module:list/todolist/todolistconverters~modelViewChangeType} to see an example of it.
 *
 * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Object} data Additional information about the change.
 * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface.
 */
function converters_modelViewChangeType( evt, data, conversionApi ) {
	if ( !conversionApi.consumable.test( data.item, evt.name ) ) {
		return;
	}

	const viewItem = conversionApi.mapper.toViewElement( data.item );
	const viewWriter = conversionApi.writer;

	// Break the container after and before the list item.
	// This will create a view list with one view list item -- the one that changed type.
	viewWriter.breakContainer( viewWriter.createPositionBefore( viewItem ) );
	viewWriter.breakContainer( viewWriter.createPositionAfter( viewItem ) );

	// Change name of the view list that holds the changed view item.
	// We cannot just change name property, because that would not render properly.
	const viewList = viewItem.parent;
	const listName = data.attributeNewValue == 'numbered' ? 'ol' : 'ul';

	viewWriter.rename( listName, viewList );
}

/**
 * A model-to-view converter that attempts to merge nodes split by {@link module:list/list/converters~modelViewChangeType}.
 *
 * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Object} data Additional information about the change.
 * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface.
 */
function converters_modelViewMergeAfterChangeType( evt, data, conversionApi ) {
	conversionApi.consumable.consume( data.item, evt.name );

	const viewItem = conversionApi.mapper.toViewElement( data.item );
	const viewList = viewItem.parent;
	const viewWriter = conversionApi.writer;

	// Merge the changed view list with other lists, if possible.
	mergeViewLists( viewWriter, viewList, viewList.nextSibling );
	mergeViewLists( viewWriter, viewList.previousSibling, viewList );
}

/**
 * A model-to-view converter for the `listIndent` attribute change on the `listItem` model element.
 *
 * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:attribute
 * @param {module:engine/model/model~Model} model Model instance.
 * @returns {Function} Returns a conversion callback.
 */
function converters_modelViewChangeIndent( model ) {
	return ( evt, data, conversionApi ) => {
		if ( !conversionApi.consumable.consume( data.item, 'attribute:listIndent' ) ) {
			return;
		}

		const viewItem = conversionApi.mapper.toViewElement( data.item );
		const viewWriter = conversionApi.writer;

		// 1. Break the container after and before the list item.
		// This will create a view list with one view list item -- the one that changed type.
		viewWriter.breakContainer( viewWriter.createPositionBefore( viewItem ) );
		viewWriter.breakContainer( viewWriter.createPositionAfter( viewItem ) );

		// 2. Extract view list with changed view list item and merge "hole" possibly created by breaking and removing elements.
		const viewList = viewItem.parent;
		const viewListPrev = viewList.previousSibling;
		const removeRange = viewWriter.createRangeOn( viewList );
		viewWriter.remove( removeRange );

		if ( viewListPrev && viewListPrev.nextSibling ) {
			mergeViewLists( viewWriter, viewListPrev, viewListPrev.nextSibling );
		}

		// 3. Bring back nested list that was in the removed <li>.
		hoistNestedLists( data.attributeOldValue + 1, data.range.start, removeRange.start, viewItem, conversionApi, model );

		// 4. Inject view list like it is newly inserted.
		injectViewList( data.item, viewItem, conversionApi, model );

		// 5. Consume insertion of children inside the item. They are already handled by re-building the item in view.
		for ( const child of data.item.getChildren() ) {
			conversionApi.consumable.consume( child, 'insert' );
		}
	};
}

/**
 * A special model-to-view converter introduced by the {@link module:list/list~List list feature}. This converter is fired for
 * insert change of every model item, and should be fired before the actual converter. The converter checks whether the inserted
 * model item is a non-`listItem` element. If it is, and it is inserted inside a view list, the converter breaks the
 * list so the model element is inserted to the view parent element corresponding to its model parent element.
 *
 * The converter prevents such situations:
 *
 *		// Model:                        // View:
 *		<listItem>foo</listItem>         <ul>
 *		<listItem>bar</listItem>             <li>foo</li>
 *		                                     <li>bar</li>
 *		                                 </ul>
 *
 *		// After change:                 // Correct view guaranteed by this converter:
 *		<listItem>foo</listItem>         <ul><li>foo</li></ul><p>xxx</p><ul><li>bar</li></ul>
 *		<paragraph>xxx</paragraph>       // Instead of this wrong view state:
 *		<listItem>bar</listItem>         <ul><li>foo</li><p>xxx</p><li>bar</li></ul>
 *
 * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:insert
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Object} data Additional information about the change.
 * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface.
 */
function converters_modelViewSplitOnInsert( evt, data, conversionApi ) {
	if ( !conversionApi.consumable.test( data.item, evt.name ) ) {
		return;
	}

	if ( data.item.name != 'listItem' ) {
		let viewPosition = conversionApi.mapper.toViewPosition( data.range.start );

		const viewWriter = conversionApi.writer;
		const lists = [];

		// Break multiple ULs/OLs if there are.
		//
		// Imagine following list:
		//
		// 1 --------
		//   1.1 --------
		//     1.1.1 --------
		//     1.1.2 --------
		//     1.1.3 --------
		//       1.1.3.1 --------
		//   1.2 --------
		//     1.2.1 --------
		// 2 --------
		//
		// Insert paragraph after item 1.1.1:
		//
		// 1 --------
		//   1.1 --------
		//     1.1.1 --------
		//
		// Lorem ipsum.
		//
		//     1.1.2 --------
		//     1.1.3 --------
		//       1.1.3.1 --------
		//   1.2 --------
		//     1.2.1 --------
		// 2 --------
		//
		// In this case 1.1.2 has to become beginning of a new list.
		// We need to break list before 1.1.2 (obvious), then we need to break list also before 1.2.
		// Then we need to move those broken pieces one after another and merge:
		//
		// 1 --------
		//   1.1 --------
		//     1.1.1 --------
		//
		// Lorem ipsum.
		//
		// 1.1.2 --------
		//   1.1.3 --------
		//     1.1.3.1 --------
		// 1.2 --------
		//   1.2.1 --------
		// 2 --------
		//
		while ( viewPosition.parent.name == 'ul' || viewPosition.parent.name == 'ol' ) {
			viewPosition = viewWriter.breakContainer( viewPosition );

			if ( viewPosition.parent.name != 'li' ) {
				break;
			}

			// Remove lists that are after inserted element.
			// They will be brought back later, below the inserted element.
			const removeStart = viewPosition;
			const removeEnd = viewWriter.createPositionAt( viewPosition.parent, 'end' );

			// Don't remove if there is nothing to remove.
			if ( !removeStart.isEqual( removeEnd ) ) {
				const removed = viewWriter.remove( viewWriter.createRange( removeStart, removeEnd ) );
				lists.push( removed );
			}

			viewPosition = viewWriter.createPositionAfter( viewPosition.parent );
		}

		// Bring back removed lists.
		if ( lists.length > 0 ) {
			for ( let i = 0; i < lists.length; i++ ) {
				const previousList = viewPosition.nodeBefore;
				const insertedRange = viewWriter.insert( viewPosition, lists[ i ] );
				viewPosition = insertedRange.end;

				// Don't merge first list! We want a split in that place (this is why this converter is introduced).
				if ( i > 0 ) {
					const mergePos = mergeViewLists( viewWriter, previousList, previousList.nextSibling );

					// If `mergePos` is in `previousList` it means that the lists got merged.
					// In this case, we need to fix insert position.
					if ( mergePos && mergePos.parent == previousList ) {
						viewPosition.offset--;
					}
				}
			}

			// Merge last inserted list with element after it.
			mergeViewLists( viewWriter, viewPosition.nodeBefore, viewPosition.nodeAfter );
		}
	}
}

/**
 * A special model-to-view converter introduced by the {@link module:list/list~List list feature}. This converter takes care of
 * merging view lists after something is removed or moved from near them.
 *
 * Example:
 *
 *		// Model:                        // View:
 *		<listItem>foo</listItem>         <ul><li>foo</li></ul>
 *		<paragraph>xxx</paragraph>       <p>xxx</p>
 *		<listItem>bar</listItem>         <ul><li>bar</li></ul>
 *
 *		// After change:                 // Correct view guaranteed by this converter:
 *		<listItem>foo</listItem>         <ul>
 *		<listItem>bar</listItem>             <li>foo</li>
 *		                                     <li>bar</li>
 *		                                 </ul>
 *
 * @see module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:remove
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Object} data Additional information about the change.
 * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi Conversion interface.
 */
function converters_modelViewMergeAfter( evt, data, conversionApi ) {
	const viewPosition = conversionApi.mapper.toViewPosition( data.position );
	const viewItemPrev = viewPosition.nodeBefore;
	const viewItemNext = viewPosition.nodeAfter;

	// Merge lists if something (remove, move) was done from inside of list.
	// Merging will be done only if both items are view lists of the same type.
	// The check is done inside the helper function.
	mergeViewLists( conversionApi.writer, viewItemPrev, viewItemNext );
}

/**
 * A view-to-model converter that converts the `<li>` view elements into the `listItem` model elements.
 *
 * To set correct values of the `listType` and `listIndent` attributes the converter:
 * * checks `<li>`'s parent,
 * * stores and increases the `conversionApi.store.indent` value when `<li>`'s sub-items are converted.
 *
 * @see module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Object} data An object containing conversion input and a placeholder for conversion output and possibly other values.
 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion interface to be used by the callback.
 */
function converters_viewModelConverter( evt, data, conversionApi ) {
	if ( conversionApi.consumable.consume( data.viewItem, { name: true } ) ) {
		const writer = conversionApi.writer;

		// 1. Create `listItem` model element.
		const listItem = writer.createElement( 'listItem' );

		// 2. Handle `listItem` model element attributes.
		const indent = converters_getIndent( data.viewItem );

		writer.setAttribute( 'listIndent', indent, listItem );

		// Set 'bulleted' as default. If this item is pasted into a context,
		const type = data.viewItem.parent && data.viewItem.parent.name == 'ol' ? 'numbered' : 'bulleted';
		writer.setAttribute( 'listType', type, listItem );

		if ( !conversionApi.safeInsert( listItem, data.modelCursor ) ) {
			return;
		}

		const nextPosition = viewToModelListItemChildrenConverter( listItem, data.viewItem.getChildren(), conversionApi );

		// Result range starts before the first item and ends after the last.
		data.modelRange = writer.createRange( data.modelCursor, nextPosition );

		conversionApi.updateConversionResult( listItem, data );
	}
}

/**
 * A view-to-model converter for the `<ul>` and `<ol>` view elements that cleans the input view of garbage.
 * This is mostly to clean whitespaces from between the `<li>` view elements inside the view list element, however, also
 * incorrect data can be cleared if the view was incorrect.
 *
 * @see module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Object} data An object containing conversion input and a placeholder for conversion output and possibly other values.
 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion interface to be used by the callback.
 */
function converters_cleanList( evt, data, conversionApi ) {
	if ( conversionApi.consumable.test( data.viewItem, { name: true } ) ) {
		// Caching children because when we start removing them iterating fails.
		const children = Array.from( data.viewItem.getChildren() );

		for ( const child of children ) {
			const isWrongElement = !( child.is( 'element', 'li' ) || converters_isList( child ) );

			if ( isWrongElement ) {
				child._remove();
			}
		}
	}
}

/**
 * A view-to-model converter for the `<li>` elements that cleans whitespace formatting from the input view.
 *
 * @see module:engine/conversion/upcastdispatcher~UpcastDispatcher#event:element
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Object} data An object containing conversion input and a placeholder for conversion output and possibly other values.
 * @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion interface to be used by the callback.
 */
function converters_cleanListItem( evt, data, conversionApi ) {
	if ( conversionApi.consumable.test( data.viewItem, { name: true } ) ) {
		if ( data.viewItem.childCount === 0 ) {
			return;
		}

		const children = [ ...data.viewItem.getChildren() ];

		let foundList = false;

		for ( const child of children ) {
			if ( foundList && !converters_isList( child ) ) {
				child._remove();
			}

			if ( converters_isList( child ) ) {
				// If this is a <ul> or <ol>, do not process it, just mark that we already visited list element.
				foundList = true;
			}
		}
	}
}

/**
 * Returns a callback for model position to view position mapping for {@link module:engine/conversion/mapper~Mapper}. The callback fixes
 * positions between the `listItem` elements that would be incorrectly mapped because of how list items are represented in the model
 * and in the view.
 *
 * @see module:engine/conversion/mapper~Mapper#event:modelToViewPosition
 * @param {module:engine/view/view~View} view A view instance.
 * @returns {Function}
 */
function converters_modelToViewPosition( view ) {
	return ( evt, data ) => {
		if ( data.isPhantom ) {
			return;
		}

		const modelItem = data.modelPosition.nodeBefore;

		if ( modelItem && modelItem.is( 'element', 'listItem' ) ) {
			const viewItem = data.mapper.toViewElement( modelItem );
			const topmostViewList = viewItem.getAncestors().find( converters_isList );
			const walker = view.createPositionAt( viewItem, 0 ).getWalker();

			for ( const value of walker ) {
				if ( value.type == 'elementStart' && value.item.is( 'element', 'li' ) ) {
					data.viewPosition = value.previousPosition;

					break;
				} else if ( value.type == 'elementEnd' && value.item == topmostViewList ) {
					data.viewPosition = value.nextPosition;

					break;
				}
			}
		}
	};
}

/**
 * The callback for view position to model position mapping for {@link module:engine/conversion/mapper~Mapper}. The callback fixes
 * positions between the `<li>` elements that would be incorrectly mapped because of how list items are represented in the model
 * and in the view.
 *
 * @see module:engine/conversion/mapper~Mapper#event:viewToModelPosition
 * @param {module:engine/model/model~Model} model Model instance.
 * @returns {Function} Returns a conversion callback.
 */
function converters_viewToModelPosition( model ) {
	return ( evt, data ) => {
		const viewPos = data.viewPosition;
		const viewParent = viewPos.parent;
		const mapper = data.mapper;

		if ( viewParent.name == 'ul' || viewParent.name == 'ol' ) {
			// Position is directly in <ul> or <ol>.
			if ( !viewPos.isAtEnd ) {
				// If position is not at the end, it must be before <li>.
				// Get that <li>, map it to `listItem` and set model position before that `listItem`.
				const modelNode = mapper.toModelElement( viewPos.nodeAfter );

				data.modelPosition = model.createPositionBefore( modelNode );
			} else {
				// Position is at the end of <ul> or <ol>, so there is no <li> after it to be mapped.
				// There is <li> before the position, but we cannot just map it to `listItem` and set model position after it,
				// because that <li> may contain nested items.
				// We will check "model length" of that <li>, in other words - how many `listItem`s are in that <li>.
				const modelNode = mapper.toModelElement( viewPos.nodeBefore );
				const modelLength = mapper.getModelLength( viewPos.nodeBefore );

				// Then we get model position before mapped `listItem` and shift it accordingly.
				data.modelPosition = model.createPositionBefore( modelNode ).getShiftedBy( modelLength );
			}

			evt.stop();
		} else if (
			viewParent.name == 'li' &&
			viewPos.nodeBefore &&
			( viewPos.nodeBefore.name == 'ul' || viewPos.nodeBefore.name == 'ol' )
		) {
			// In most cases when view position is in <li> it is in text and this is a correct position.
			// However, if position is after <ul> or <ol> we have to fix it -- because in model <ul>/<ol> are not in the `listItem`.
			const modelNode = mapper.toModelElement( viewParent );

			// Check all <ul>s and <ol>s that are in the <li> but before mapped position.
			// Get model length of those elements and then add it to the offset of `listItem` mapped to the original <li>.
			let modelLength = 1; // Starts from 1 because the original <li> has to be counted in too.
			let viewList = viewPos.nodeBefore;

			while ( viewList && converters_isList( viewList ) ) {
				modelLength += mapper.getModelLength( viewList );

				viewList = viewList.previousSibling;
			}

			data.modelPosition = model.createPositionBefore( modelNode ).getShiftedBy( modelLength );

			evt.stop();
		}
	};
}

/**
 * Post-fixer that reacts to changes on document and fixes incorrect model states.
 *
 * In the example below, there is a correct list structure.
 * Then the middle element is removed so the list structure will become incorrect:
 *
 *		<listItem listType="bulleted" listIndent=0>Item 1</listItem>
 *		<listItem listType="bulleted" listIndent=1>Item 2</listItem>   <--- this is removed.
 *		<listItem listType="bulleted" listIndent=2>Item 3</listItem>
 *
 * The list structure after the middle element is removed:
 *
 * 		<listItem listType="bulleted" listIndent=0>Item 1</listItem>
 *		<listItem listType="bulleted" listIndent=2>Item 3</listItem>
 *
 * Should become:
 *
 *		<listItem listType="bulleted" listIndent=0>Item 1</listItem>
 *		<listItem listType="bulleted" listIndent=1>Item 3</listItem>   <--- note that indent got post-fixed.
 *
 * @param {module:engine/model/model~Model} model The data model.
 * @param {module:engine/model/writer~Writer} writer The writer to do changes with.
 * @returns {Boolean} `true` if any change has been applied, `false` otherwise.
 */
function converters_modelChangePostFixer( model, writer ) {
	const changes = model.document.differ.getChanges();
	const itemToListHead = new Map();

	let applied = false;

	for ( const entry of changes ) {
		if ( entry.type == 'insert' && entry.name == 'listItem' ) {
			_addListToFix( entry.position );
		} else if ( entry.type == 'insert' && entry.name != 'listItem' ) {
			if ( entry.name != '$text' ) {
				// In case of renamed element.
				const item = entry.position.nodeAfter;

				if ( item.hasAttribute( 'listIndent' ) ) {
					writer.removeAttribute( 'listIndent', item );

					applied = true;
				}

				if ( item.hasAttribute( 'listType' ) ) {
					writer.removeAttribute( 'listType', item );

					applied = true;
				}

				if ( item.hasAttribute( 'listStyle' ) ) {
					writer.removeAttribute( 'listStyle', item );

					applied = true;
				}

				if ( item.hasAttribute( 'listReversed' ) ) {
					writer.removeAttribute( 'listReversed', item );

					applied = true;
				}

				if ( item.hasAttribute( 'listStart' ) ) {
					writer.removeAttribute( 'listStart', item );

					applied = true;
				}

				for ( const innerItem of Array.from( model.createRangeIn( item ) ).filter( e => e.item.is( 'element', 'listItem' ) ) ) {
					_addListToFix( innerItem.previousPosition );
				}
			}

			const posAfter = entry.position.getShiftedBy( entry.length );

			_addListToFix( posAfter );
		} else if ( entry.type == 'remove' && entry.name == 'listItem' ) {
			_addListToFix( entry.position );
		} else if ( entry.type == 'attribute' && entry.attributeKey == 'listIndent' ) {
			_addListToFix( entry.range.start );
		} else if ( entry.type == 'attribute' && entry.attributeKey == 'listType' ) {
			_addListToFix( entry.range.start );
		}
	}

	for ( const listHead of itemToListHead.values() ) {
		_fixListIndents( listHead );
		_fixListTypes( listHead );
	}

	return applied;

	function _addListToFix( position ) {
		const previousNode = position.nodeBefore;

		if ( !previousNode || !previousNode.is( 'element', 'listItem' ) ) {
			const item = position.nodeAfter;

			if ( item && item.is( 'element', 'listItem' ) ) {
				itemToListHead.set( item, item );
			}
		} else {
			let listHead = previousNode;

			if ( itemToListHead.has( listHead ) ) {
				return;
			}

			for (
				// Cache previousSibling and reuse for performance reasons. See #6581.
				let previousSibling = listHead.previousSibling;
				previousSibling && previousSibling.is( 'element', 'listItem' );
				previousSibling = listHead.previousSibling
			) {
				listHead = previousSibling;

				if ( itemToListHead.has( listHead ) ) {
					return;
				}
			}

			itemToListHead.set( previousNode, listHead );
		}
	}

	function _fixListIndents( item ) {
		let maxIndent = 0;
		let fixBy = null;

		while ( item && item.is( 'element', 'listItem' ) ) {
			const itemIndent = item.getAttribute( 'listIndent' );

			if ( itemIndent > maxIndent ) {
				let newIndent;

				if ( fixBy === null ) {
					fixBy = itemIndent - maxIndent;
					newIndent = maxIndent;
				} else {
					if ( fixBy > itemIndent ) {
						fixBy = itemIndent;
					}

					newIndent = itemIndent - fixBy;
				}

				writer.setAttribute( 'listIndent', newIndent, item );

				applied = true;
			} else {
				fixBy = null;
				maxIndent = item.getAttribute( 'listIndent' ) + 1;
			}

			item = item.nextSibling;
		}
	}

	function _fixListTypes( item ) {
		let typesStack = [];
		let prev = null;

		while ( item && item.is( 'element', 'listItem' ) ) {
			const itemIndent = item.getAttribute( 'listIndent' );

			if ( prev && prev.getAttribute( 'listIndent' ) > itemIndent ) {
				typesStack = typesStack.slice( 0, itemIndent + 1 );
			}

			if ( itemIndent != 0 ) {
				if ( typesStack[ itemIndent ] ) {
					const type = typesStack[ itemIndent ];

					if ( item.getAttribute( 'listType' ) != type ) {
						writer.setAttribute( 'listType', type, item );

						applied = true;
					}
				} else {
					typesStack[ itemIndent ] = item.getAttribute( 'listType' );
				}
			}

			prev = item;
			item = item.nextSibling;
		}
	}
}

/**
 * A fixer for pasted content that includes list items.
 *
 * It fixes indentation of pasted list items so the pasted items match correctly to the context they are pasted into.
 *
 * Example:
 *
 *		<listItem listType="bulleted" listIndent=0>A</listItem>
 *		<listItem listType="bulleted" listIndent=1>B^</listItem>
 *		// At ^ paste:  <listItem listType="bulleted" listIndent=4>X</listItem>
 *		//              <listItem listType="bulleted" listIndent=5>Y</listItem>
 *		<listItem listType="bulleted" listIndent=2>C</listItem>
 *
 * Should become:
 *
 *		<listItem listType="bulleted" listIndent=0>A</listItem>
 *		<listItem listType="bulleted" listIndent=1>BX</listItem>
 *		<listItem listType="bulleted" listIndent=2>Y/listItem>
 *		<listItem listType="bulleted" listIndent=2>C</listItem>
 *
 * @param {module:utils/eventinfo~EventInfo} evt An object containing information about the fired event.
 * @param {Array} args Arguments of {@link module:engine/model/model~Model#insertContent}.
 */
function converters_modelIndentPasteFixer( evt, [ content, selectable, placeOrOffset ] ) {
	// Check whether inserted content starts from a `listItem`. If it does not, it means that there are some other
	// elements before it and there is no need to fix indents, because even if we insert that content into a list,
	// that list will be broken.
	// Note: we also need to handle singular elements because inserting item with indent 0 into 0,1,[],2
	// would create incorrect model.
	let item = content.is( 'documentFragment' ) ? content.getChild( 0 ) : content;

	let selection;

	if ( !selectable ) {
		selection = this.document.selection;
	} else {
		selection = this.createSelection( selectable, placeOrOffset );
	}

	if ( item && item.is( 'element', 'listItem' ) ) {
		// Get a reference list item. Inserted list items will be fixed according to that item.
		const pos = selection.getFirstPosition();
		let refItem = null;

		if ( pos.parent.is( 'element', 'listItem' ) ) {
			refItem = pos.parent;
		} else if ( pos.nodeBefore && pos.nodeBefore.is( 'element', 'listItem' ) ) {
			refItem = pos.nodeBefore;
		}

		// If there is `refItem` it means that we do insert list items into an existing list.
		if ( refItem ) {
			// First list item in `data` has indent equal to 0 (it is a first list item). It should have indent equal
			// to the indent of reference item. We have to fix the first item and all of it's children and following siblings.
			// Indent of all those items has to be adjusted to reference item.
			const indentChange = refItem.getAttribute( 'listIndent' );

			// Fix only if there is anything to fix.
			if ( indentChange > 0 ) {
				// Adjust indent of all "first" list items in inserted data.
				while ( item && item.is( 'element', 'listItem' ) ) {
					item._setAttribute( 'listIndent', item.getAttribute( 'listIndent' ) + indentChange );

					item = item.nextSibling;
				}
			}
		}
	}
}

// Helper function that converts children of a given `<li>` view element into corresponding model elements.
// The function maintains proper order of elements if model `listItem` is split during the conversion
// due to block children conversion.
//
// @param {module:engine/model/element~Element} listItemModel List item model element to which converted children will be inserted.
// @param {Iterable.<module:engine/view/node~Node>} viewChildren View elements which will be converted.
// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi Conversion interface to be used by the callback.
// @returns {module:engine/model/position~Position} Position on which next elements should be inserted after children conversion.
function viewToModelListItemChildrenConverter( listItemModel, viewChildren, conversionApi ) {
	const { writer, schema } = conversionApi;

	// A position after the last inserted `listItem`.
	let nextPosition = writer.createPositionAfter( listItemModel );

	// Check all children of the converted `<li>`. At this point we assume there are no "whitespace" view text nodes
	// in view list, between view list items. This should be handled by `<ul>` and `<ol>` converters.
	for ( const child of viewChildren ) {
		if ( child.name == 'ul' || child.name == 'ol' ) {
			// If the children is a list, we will insert its conversion result after currently handled `listItem`.
			// Then, next insertion position will be set after all the new list items (and maybe other elements if
			// something split list item).
			//
			// If this is a list, we expect that some `listItem`s and possibly other blocks will be inserted, however `.modelCursor`
			// should be set after last `listItem` (or block). This is why it feels safe to use it as `nextPosition`
			nextPosition = conversionApi.convertItem( child, nextPosition ).modelCursor;
		} else {
			// If this is not a list, try inserting content at the end of the currently handled `listItem`.
			const result = conversionApi.convertItem( child, writer.createPositionAt( listItemModel, 'end' ) );

			// It may end up that the current `listItem` becomes split (if that content cannot be inside `listItem`). For example:
			//
			// <li><p>Foo</p></li>
			//
			// will be converted to:
			//
			// <listItem></listItem><paragraph>Foo</paragraph><listItem></listItem>
			//
			const convertedChild = result.modelRange.start.nodeAfter;
			const wasSplit = convertedChild && convertedChild.is( 'element' ) && !schema.checkChild( listItemModel, convertedChild.name );

			if ( wasSplit ) {
				// As `lastListItem` got split, we need to update it to the second part of the split `listItem` element.
				//
				// `modelCursor` should be set to a position where the conversion should continue. There are multiple possible scenarios
				// that may happen. Usually, `modelCursor` (marked as `#` below) would point to the second list item after conversion:
				//
				//		`<li><p>Foo</p></li>` -> `<listItem></listItem><paragraph>Foo</paragraph><listItem>#</listItem>`
				//
				// However, in some cases, like auto-paragraphing, the position is placed at the end of the block element:
				//
				//		`<li><div>Foo</div></li>` -> `<listItem></listItem><paragraph>Foo#</paragraph><listItem></listItem>`
				//
				// or after an element if another element broken auto-paragraphed element:
				//
				//		`<li><div><h2>Foo</h2></div></li>` -> `<listItem></listItem><heading1>Foo</heading1>#<listItem></listItem>`
				//
				// We need to check for such cases and use proper list item and position based on it.
				//
				if ( result.modelCursor.parent.is( 'element', 'listItem' ) ) {
					// (1).
					listItemModel = result.modelCursor.parent;
				} else {
					// (2), (3).
					listItemModel = findNextListItem( result.modelCursor );
				}

				nextPosition = writer.createPositionAfter( listItemModel );
			}
		}
	}

	return nextPosition;
}

// Helper function that seeks for a next list item starting from given `startPosition`.
function findNextListItem( startPosition ) {
	const treeWalker = new TreeWalker( { startPosition } );

	let value;

	do {
		value = treeWalker.next();
	} while ( !value.value.item.is( 'element', 'listItem' ) );

	return value.value.item;
}

// Helper function that takes all children of given `viewRemovedItem` and moves them in a correct place, according
// to other given parameters.
function hoistNestedLists( nextIndent, modelRemoveStartPosition, viewRemoveStartPosition, viewRemovedItem, conversionApi, model ) {
	// Find correct previous model list item element.
	// The element has to have either same or smaller indent than given reference indent.
	// This will be the model element which will get nested items (if it has smaller indent) or sibling items (if it has same indent).
	// Keep in mind that such element might not be found, if removed item was the first item.
	const prevModelItem = getSiblingListItem( modelRemoveStartPosition.nodeBefore, {
		sameIndent: true,
		smallerIndent: true,
		listIndent: nextIndent,
		foo: 'b'
	} );

	const mapper = conversionApi.mapper;
	const viewWriter = conversionApi.writer;

	// Indent of found element or `null` if the element has not been found.
	const prevIndent = prevModelItem ? prevModelItem.getAttribute( 'listIndent' ) : null;

	let insertPosition;

	if ( !prevModelItem ) {
		// If element has not been found, simply insert lists at the position where the removed item was:
		//
		// Lorem ipsum.
		// 1 --------           <--- this is removed, no previous list item, put nested items in place of removed item.
		//   1.1 --------       <--- this is reference indent.
		//     1.1.1 --------
		//     1.1.2 --------
		//   1.2 --------
		//
		// Becomes:
		//
		// Lorem ipsum.
		// 1.1 --------
		//   1.1.1 --------
		//   1.1.2 --------
		// 1.2 --------
		insertPosition = viewRemoveStartPosition;
	} else if ( prevIndent == nextIndent ) {
		// If element has been found and has same indent as reference indent it means that nested items should
		// become siblings of found element:
		//
		// 1 --------
		//   1.1 --------
		//   1.2 --------       <--- this is `prevModelItem`.
		// 2 --------           <--- this is removed, previous list item has indent same as reference indent.
		//   2.1 --------       <--- this is reference indent, this and 2.2 should become siblings of 1.2.
		//   2.2 --------
		//
		// Becomes:
		//
		// 1 --------
		//   1.1 --------
		//   1.2 --------
		//   2.1 --------
		//   2.2 --------
		const prevViewList = mapper.toViewElement( prevModelItem ).parent;
		insertPosition = viewWriter.createPositionAfter( prevViewList );
	} else {
		// If element has been found and has smaller indent as reference indent it means that nested items
		// should become nested items of found item:
		//
		// 1 --------           <--- this is `prevModelItem`.
		//   1.1 --------       <--- this is removed, previous list item has indent smaller than reference indent.
		//     1.1.1 --------   <--- this is reference indent, this and 1.1.1 should become nested items of 1.
		//     1.1.2 --------
		//   1.2 --------
		//
		// Becomes:
		//
		// 1 --------
		//   1.1.1 --------
		//   1.1.2 --------
		//   1.2 --------
		//
		// Note: in this case 1.1.1 have indent 2 while 1 have indent 0. In model that should not be possible,
		// because following item may have indent bigger only by one. But this is fixed by postfixer.
		const modelPosition = model.createPositionAt( prevModelItem, 'end' );
		insertPosition = mapper.toViewPosition( modelPosition );
	}

	insertPosition = positionAfterUiElements( insertPosition );

	// Handle multiple lists. This happens if list item has nested numbered and bulleted lists. Following lists
	// are inserted after the first list (no need to recalculate insertion position for them).
	for ( const child of [ ...viewRemovedItem.getChildren() ] ) {
		if ( converters_isList( child ) ) {
			insertPosition = viewWriter.move( viewWriter.createRangeOn( child ), insertPosition ).end;

			mergeViewLists( viewWriter, child, child.nextSibling );
			mergeViewLists( viewWriter, child.previousSibling, child );
		}
	}
}

// Checks if view element is a list type (ul or ol).
//
// @param {module:engine/view/element~Element} viewElement
// @returns {Boolean}
function converters_isList( viewElement ) {
	return viewElement.is( 'element', 'ol' ) || viewElement.is( 'element', 'ul' );
}

// Calculates the indent value for a list item. Handles HTML compliant and non-compliant lists.
//
// Also, fixes non HTML compliant lists indents:
//
//		before:                                     fixed list:
//		OL                                          OL
//		|-> LI (parent LIs: 0)                      |-> LI     (indent: 0)
//		    |-> OL                                  |-> OL
//		        |-> OL                                  |
//		        |   |-> OL                              |
//		        |       |-> OL                          |
//		        |           |-> LI (parent LIs: 1)      |-> LI (indent: 1)
//		        |-> LI (parent LIs: 1)                  |-> LI (indent: 1)
//
//		before:                                     fixed list:
//		OL                                          OL
//		|-> OL                                      |
//		    |-> OL                                  |
//		         |-> OL                             |
//		             |-> LI (parent LIs: 0)         |-> LI        (indent: 0)
//
//		before:                                     fixed list:
//		OL                                          OL
//		|-> LI (parent LIs: 0)                      |-> LI         (indent: 0)
//		|-> OL                                          |-> OL
//		    |-> LI (parent LIs: 0)                          |-> LI (indent: 1)
//
// @param {module:engine/view/element~Element} listItem
// @param {Object} conversionStore
// @returns {Number}
function converters_getIndent( listItem ) {
	let indent = 0;

	let parent = listItem.parent;

	while ( parent ) {
		// Each LI in the tree will result in an increased indent for HTML compliant lists.
		if ( parent.is( 'element', 'li' ) ) {
			indent++;
		} else {
			// If however the list is nested in other list we should check previous sibling of any of the list elements...
			const previousSibling = parent.previousSibling;

			// ...because the we might need increase its indent:
			//		before:                           fixed list:
			//		OL                                OL
			//		|-> LI (parent LIs: 0)            |-> LI         (indent: 0)
			//		|-> OL                                |-> OL
			//		    |-> LI (parent LIs: 0)                |-> LI (indent: 1)
			if ( previousSibling && previousSibling.is( 'element', 'li' ) ) {
				indent++;
			}
		}

		parent = parent.parent;
	}

	return indent;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/list/listediting.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/list/listediting
 */










/**
 * The engine of the list feature. It handles creating, editing and removing lists and list items.
 *
 * It registers the `'numberedList'`, `'bulletedList'`, `'indentList'` and `'outdentList'` commands.
 *
 * @extends module:core/plugin~Plugin
 */
class listediting_ListEditing extends (/* unused pure expression or super */ null && (Plugin)) {
	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'ListEditing';
	}

	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ Enter, Delete ];
	}

	/**
	 * @inheritDoc
	 */
	init() {
		const editor = this.editor;

		// Schema.
		// Note: in case `$block` will ever be allowed in `listItem`, keep in mind that this feature
		// uses `Selection#getSelectedBlocks()` without any additional processing to obtain all selected list items.
		// If there are blocks allowed inside list item, algorithms using `getSelectedBlocks()` will have to be modified.
		editor.model.schema.register( 'listItem', {
			inheritAllFrom: '$block',
			allowAttributes: [ 'listType', 'listIndent' ]
		} );

		// Converters.
		const data = editor.data;
		const editing = editor.editing;

		editor.model.document.registerPostFixer( writer => modelChangePostFixer( editor.model, writer ) );

		editing.mapper.registerViewToModelLength( 'li', getViewListItemLength );
		data.mapper.registerViewToModelLength( 'li', getViewListItemLength );

		editing.mapper.on( 'modelToViewPosition', modelToViewPosition( editing.view ) );
		editing.mapper.on( 'viewToModelPosition', viewToModelPosition( editor.model ) );
		data.mapper.on( 'modelToViewPosition', modelToViewPosition( editing.view ) );

		editor.conversion.for( 'editingDowncast' )
			.add( dispatcher => {
				dispatcher.on( 'insert', modelViewSplitOnInsert, { priority: 'high' } );
				dispatcher.on( 'insert:listItem', modelViewInsertion( editor.model ) );
				dispatcher.on( 'attribute:listType:listItem', modelViewChangeType, { priority: 'high' } );
				dispatcher.on( 'attribute:listType:listItem', modelViewMergeAfterChangeType, { priority: 'low' } );
				dispatcher.on( 'attribute:listIndent:listItem', modelViewChangeIndent( editor.model ) );
				dispatcher.on( 'remove:listItem', modelViewRemove( editor.model ) );
				dispatcher.on( 'remove', modelViewMergeAfter, { priority: 'low' } );
			} );

		editor.conversion.for( 'dataDowncast' )
			.add( dispatcher => {
				dispatcher.on( 'insert', modelViewSplitOnInsert, { priority: 'high' } );
				dispatcher.on( 'insert:listItem', modelViewInsertion( editor.model ) );
			} );

		editor.conversion.for( 'upcast' )
			.add( dispatcher => {
				dispatcher.on( 'element:ul', cleanList, { priority: 'high' } );
				dispatcher.on( 'element:ol', cleanList, { priority: 'high' } );
				dispatcher.on( 'element:li', cleanListItem, { priority: 'high' } );
				dispatcher.on( 'element:li', viewModelConverter );
			} );

		// Fix indentation of pasted items.
		editor.model.on( 'insertContent', modelIndentPasteFixer, { priority: 'high' } );

		// Register commands for numbered and bulleted list.
		editor.commands.add( 'numberedList', new ListCommand( editor, 'numbered' ) );
		editor.commands.add( 'bulletedList', new ListCommand( editor, 'bulleted' ) );

		// Register commands for indenting.
		editor.commands.add( 'indentList', new IndentCommand( editor, 'forward' ) );
		editor.commands.add( 'outdentList', new IndentCommand( editor, 'backward' ) );

		const viewDocument = editing.view.document;

		// Overwrite default Enter key behavior.
		// If Enter key is pressed with selection collapsed in empty list item, outdent it instead of breaking it.
		this.listenTo( viewDocument, 'enter', ( evt, data ) => {
			const doc = this.editor.model.document;
			const positionParent = doc.selection.getLastPosition().parent;

			if ( doc.selection.isCollapsed && positionParent.name == 'listItem' && positionParent.isEmpty ) {
				this.editor.execute( 'outdentList' );

				data.preventDefault();
				evt.stop();
			}
		}, { context: 'li' } );

		// Overwrite default Backspace key behavior.
		// If Backspace key is pressed with selection collapsed on first position in first list item, outdent it. #83
		this.listenTo( viewDocument, 'delete', ( evt, data ) => {
			// Check conditions from those that require less computations like those immediately available.
			if ( data.direction !== 'backward' ) {
				return;
			}

			const selection = this.editor.model.document.selection;

			if ( !selection.isCollapsed ) {
				return;
			}

			const firstPosition = selection.getFirstPosition();

			if ( !firstPosition.isAtStart ) {
				return;
			}

			const positionParent = firstPosition.parent;

			if ( positionParent.name !== 'listItem' ) {
				return;
			}

			const previousIsAListItem = positionParent.previousSibling && positionParent.previousSibling.name === 'listItem';

			if ( previousIsAListItem ) {
				return;
			}

			this.editor.execute( 'outdentList' );

			data.preventDefault();
			evt.stop();
		}, { context: 'li' } );

		this.listenTo( editor.editing.view.document, 'tab', ( evt, data ) => {
			const commandName = data.shiftKey ? 'outdentList' : 'indentList';
			const command = this.editor.commands.get( commandName );

			if ( command.isEnabled ) {
				editor.execute( commandName );

				data.stopPropagation();
				data.preventDefault();
				evt.stop();
			}
		}, { context: 'li' } );
	}

	/**
	 * @inheritDoc
	 */
	afterInit() {
		const commands = this.editor.commands;

		const indent = commands.get( 'indent' );
		const outdent = commands.get( 'outdent' );

		if ( indent ) {
			indent.registerChildCommand( commands.get( 'indentList' ) );
		}

		if ( outdent ) {
			outdent.registerChildCommand( commands.get( 'outdentList' ) );
		}
	}
}

function getViewListItemLength( element ) {
	let length = 1;

	for ( const child of element.getChildren() ) {
		if ( child.name == 'ul' || child.name == 'ol' ) {
			for ( const item of child.getChildren() ) {
				length += getViewListItemLength( item );
			}
		}
	}

	return length;
}

;// CONCATENATED MODULE: ./node_modules/@ckeditor/ckeditor5-list/src/list.js
/**
 * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */

/**
 * @module list/list
 */






/**
 * The list feature.
 *
 * This is a "glue" plugin that loads the {@link module:list/list/listediting~ListEditing list editing feature}
 * and {@link module:list/list/listui~ListUI list UI feature}.
 *
 * @extends module:core/plugin~Plugin
 */
class List extends (/* unused pure expression or super */ null && (Plugin)) {
	/**
	 * @inheritDoc
	 */
	static get requires() {
		return [ ListEditing, ListUI ];
	}

	/**
	 * @inheritDoc
	 */
	static get pluginName() {
		return 'List';
	}
}

/**
 * The configuration of the {@link module:list/list~List list} feature
 * and the {@link module:list/documentlist~DocumentList document list} feature.
 *
 *		ClassicEditor
 *			.create( editorElement, {
 *				list:  ... // The list feature configuration.
 *			} )
 *			.then( ... )
 *			.catch( ... );
 *
 * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
 *
 * @interface ListConfig
 */

/**
 * The configuration of the {@link module:list/list~List} feature and the {@link module:list/documentlist~DocumentList} feature.
 *
 * Read more in {@link module:list/list~ListConfig}.
 *
 * @member {module:module:list/list~ListConfig} module:core/editor/editorconfig~EditorConfig#list
 */

;// CONCATENATED MODULE: ./ckeditor5-macros/Macros.js

















class Macros extends plugin_Plugin {
    static get requires() {
        return [
          MacrosEditing,
          MacrosUI ];
    }
}

class MacrosCommand extends command_Command {
    execute( { value } ) {
        const editor = this.editor;
        const selection = editor.model.document.selection;

        editor.model.change( writer => {
            // Create a <macros> elment with the "name" attribute (and all the selection attributes)...
            //const macros = writer.createElement( 'macros', { ...Object.fromEntries( selection.getAttributes() ), name: value } );
            
            //... and insert it into the document.
            //editor.model.insertContent( macros );
           
            //Put the selection on the inserted element.
            //writer.setSelection( macros, 'on' );

            const macros = writer.createText( '[' + value + ']' );
            editor.model.insertContent( macros );
        } );
    }

    refresh() {
        const model = this.editor.model;
        const selection = model.document.selection;
        const isAllowed = model.schema.checkChild(selection.focus.parent, 'macros');

        this.isEnabled = isAllowed;
    }
}

class MacrosUI extends plugin_Plugin {
    init() {
        const editor = this.editor;
        const t = editor.t;
        const macrosNames = editor.config.get( 'macrosConfig.macroses' );

        // The "macros" dropdown must be registered among the UI components of the editor
        // to be displayed in the toolbar.
        editor.ui.componentFactory.add( 'macros', locale => {
            const dropdownView = createDropdown( locale  );
            dropdownView.extendTemplate( { attributes: { class: 'ck-macros-ui-dropdown' } } );

            // Populate the list in the dropdown with items.
            addListToDropdown( dropdownView, getDropdownItemsDefinitions( macrosNames ) );

            dropdownView.buttonView.set( {
                // The t() function helps localize the editor. All strings enclosed in t() can be
                // translated and change when the language of the editor changes.
                label: t( 'Macros' ),
                tooltip: true,
                withText: true
            } );

            // Disable the macros button when the command is disabled.
            const command = editor.commands.get( 'macros' );
            dropdownView.bind( 'isEnabled' ).to( command );

            // Execute the command when the dropdown item is clicked (executed).
            this.listenTo( dropdownView, 'execute', evt => {
                var callback = null;

                const macrosesCallback = editor.config.get( 'macrosConfig.macrosesCallback' );
                if (macrosesCallback)
                {                    
                    callback = macrosesCallback[evt.source.commandParam]; 
                }
                if (callback)
                {
                    callback(function(id){
                        editor.execute( 'macros', { value: evt.source.commandParam + ':' + id } );
                        editor.editing.view.focus();
                    });
                }else{
                    editor.execute( 'macros', { value: evt.source.commandParam } );
                    editor.editing.view.focus();
                }
            } );

            return dropdownView;
        } );
    }
}
function getDropdownItemsDefinitions(macrosNames) {
    const itemDefinitions = new Collection();
    for (let i = 0; i < macrosNames.length; i++) {
        const definition = {
            type: 'button',
            model: new model_Model( {
                commandParam: macrosNames[i].Value,
                label: macrosNames[i].Text,
                withText: true
            } )
        };
        itemDefinitions.add(definition);
    }
    return itemDefinitions;
}


class MacrosEditing extends plugin_Plugin {
    static get requires() { return [ Widget ]; }

    init() {
        console.log( 'MacrosEditing#init() got called' );

        this._defineSchema();
        this._defineConverters();

        this.editor.commands.add( 'macros', new MacrosCommand( this.editor ) );

        this.editor.editing.mapper.on(
            'viewToModelPosition',
            viewToModelPositionOutsideModelElement( this.editor.model, viewElement => viewElement.hasClass( 'macros' ) )
        );
        this.editor.config.define( 'macrosConfig', { macroses: [ /*'date', 'first name', 'surname'*/ ] } );
    }

    _defineSchema() {
        const schema = this.editor.model.schema;

        schema.register( 'macros', {
            // Allow wherever text is allowed:
            allowWhere: '$text',
            // The macros will act as an inline node:
            isInline: true,
            // The inline widget is self-contained so it cannot be split by the caret and it can be selected:
            isObject: true,
            // The inline widget can have the same attributes as text (for example linkHref, bold).
            allowAttributesOf: '$text',
            // The macros can have many macroses, like date, name, surname, etc:
            allowAttributes: [ 'name' ]
        } );
    }

    _defineConverters() {
        const conversion = this.editor.conversion;

        conversion.for( 'upcast' ).elementToElement( {
            view: {
                name: 'span',
                classes: [ 'macros' ]
            },
            model: ( viewElement, { writer: modelWriter } ) => {
                // Extract the "name" from "{name}".
                const name = viewElement.getChild( 0 ).data.slice( 1, -1 );

                return modelWriter.createElement( 'macros', { name } );
            }
        } );

        conversion.for( 'editingDowncast' ).elementToElement( {
            model: 'macros',
            view: ( modelItem, { writer: viewWriter } ) => {
                const widgetElement = createMacrosView( modelItem, viewWriter );

                // Enable widget handling on a macros element inside the editing view.
                return toWidget( widgetElement, viewWriter );
            }
        } );

        conversion.for( 'dataDowncast' ).elementToElement( {
            model: 'macros',
            view: ( modelItem, { writer: viewWriter } ) => createMacrosView( modelItem, viewWriter )
        } );

        // Helper method for both downcast converters.
        function createMacrosView( modelItem, viewWriter ) {
            const name = modelItem.getAttribute( 'name' );
            const macrosView = viewWriter.createContainerElement( 'div', { class: 'macros' }, { isAllowedInsideAttributeElement: true } );
            const innerText = viewWriter.createText( '[' + name + ']' );

            viewWriter.insert( viewWriter.createPositionAt( macrosView, 0 ), innerText );

            return macrosView;
        }
    }

}

;// CONCATENATED MODULE: ./ckeditor5-plugins/SoutronPlugins.js


class SoutronPlugins extends plugin_Plugin {
    init() {
        const editor = this.editor;

        editor.plugins.get('ImageUploadEditing').on('uploadComplete', (evt, p) => {
                            editor.model.change(writer => {
                                writer.setAttribute('dataImageId', p.data.fieldValueId, p.imageElement);

                                //var link = _utilities.getURL(p.data.url);
                                //p.data.url = link;
                                //p.data.urls.default = link;

                                writer.setAttribute('src', p.data.url, p.imageElement);
                            });
                        });

                        editor.model.schema.extend('imageBlock', { allowAttributes: 'dataImageId' });
                        editor.model.schema.extend('imageInline', { allowAttributes: 'dataImageId' });

                        editor.conversion.for('upcast').attributeToAttribute({
                            view: 'data-image_id',
                            model: 'dataImageId'
                        });

                        editor.conversion.for('downcast').add(dispatcher => {
                            dispatcher.on('attribute:dataImageId:imageBlock', (evt, data, conversionApi) => {
                                if (!conversionApi.consumable.consume(data.item, evt.name)) {
                                    return;
                                }

                                var viewWriter = conversionApi.writer;
                                var figure = conversionApi.mapper.toViewElement(data.item);
                                var img = figure.getChild(0);

                                if (data.attributeNewValue !== null) {
                                    viewWriter.setAttribute('data-image_id', data.attributeNewValue, img);

                                    viewWriter.addClass('soutron-ck-image', img);
                                } else {
                                    viewWriter.removeAttribute('data-image_id', img);
                                }
                            });
                        });
                        editor.conversion.for('downcast').add(dispatcher => {
                            dispatcher.on('attribute:dataImageId:imageInline', (evt, data, conversionApi) => {
                                if (!conversionApi.consumable.consume(data.item, evt.name)) {
                                    return;
                                }

                                var viewWriter = conversionApi.writer;
                                var img = conversionApi.mapper.toViewElement(data.item);

                                if (data.attributeNewValue !== null) {
                                    viewWriter.setAttribute('data-image_id', data.attributeNewValue, img);

                                    viewWriter.addClass('soutron-ck-image', img);
                                } else {
                                    viewWriter.removeAttribute('data-image_id', img);
                                }
                            });
                        });




    }
}

;// CONCATENATED MODULE: ./src/ckeditor.js
/**
 * @license Copyright (c) 2014-2022, CKSource Holding sp. z o.o. All rights reserved.
 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
 */








//import Base64UploadAdapter from '@ckeditor/ckeditor5-upload/src/adapters/base64uploadadapter.js';
/*-*/



//import CodeBlock from '@ckeditor/ckeditor5-code-block/src/codeblock.js';


























//import List from '@ckeditor/ckeditor5-list/src/list.js';
//import ListProperties from '@ckeditor/ckeditor5-list/src/listproperties.js';



















//import Style from '@ckeditor/ckeditor5-style/src/style.js';










//import Title from '@ckeditor/ckeditor5-heading/src/title.js';
//import TodoList from '@ckeditor/ckeditor5-list/src/todolist';








class ClassicEditor extends classiceditor_ClassicEditor {}
class InlineEditor extends inlineeditor_InlineEditor {}
class BalloonEditor extends ballooneditor_BalloonEditor {}

// Plugins to include in the build.
const plugins = [
	Alignment,
	Autoformat,
	AutoImage,
	AutoLink,
	Autosave,
	BlockQuote,
	Bold,
	Essentials,
	FindAndReplace,
	FontBackgroundColor,
	FontColor,
	FontFamily,
	FontSize,
	GeneralHtmlSupport,
	Heading,
	HorizontalLine,
	HtmlEmbed,
	Image,
	ImageCaption,
	ImageInsert,
	ImageResize,
	ImageStyle,
	ImageToolbar,
	ImageUpload,
	Indent,
	Italic,
	Link,
	LinkImage,
	//List,
	DocumentList,
	MediaEmbed,
	MediaEmbedToolbar,
	Paragraph,
	PasteFromOffice,
	RemoveFormat,
	SourceEditing,
	SpecialCharacters,
	SpecialCharactersArrows,
	SpecialCharactersCurrency,
	SpecialCharactersEssentials,
	SpecialCharactersLatin,
	SpecialCharactersMathematical,
	SpecialCharactersText,
	Strikethrough,
	Subscript,
	Superscript,
	Table,
	TableCaption,
	TableCellProperties,
	TableProperties,
	TableToolbar,
	TableColumnResize,
	TextTransformation,
	Underline,
	//Base64UploadAdapter,
	/*-*/SimpleUploadAdapter,
	Code,
	DataFilter,
	DataSchema,
	Highlight,
	HtmlComment,
	IndentBlock,
	//ListProperties,
	DocumentListProperties,
	Mention,
	PageBreak,
	StandardEditingMode,
	TextPartLanguage,
	WordCount,

	//CodeBlock,	
	//Essentials,
	//Style,
	//Title,
	//TodoList,

	FullScreen,
  	Macros,
  	SoutronPlugins		
];

ClassicEditor.builtinPlugins = plugins;
InlineEditor.builtinPlugins = plugins;
BalloonEditor.builtinPlugins = plugins;

// Editor configuration.
const config = {
	toolbar: {
		items: [
			'heading',
			'|',
			'bold',
			'italic',
			'link',
			'bulletedList',
			'numberedList',
			'|',
			'outdent',
			'indent',
			'|',
			'imageUpload',
			'blockQuote',
			'insertTable',
			'mediaEmbed',
			'undo',
			'redo'
		]
	},
	language: 'en',
	image: {
		toolbar: [
			'imageTextAlternative',
			'toggleImageCaption',
			'imageStyle:inline',
			'imageStyle:block',
			'imageStyle:side',
			'linkImage'
		]
	},
	table: {
		contentToolbar: [
			'tableColumn',
			'tableRow',
			'mergeTableCells',
			'tableCellProperties',
			'tableProperties'
		]
	}
};

ClassicEditor.defaultConfig = config;
InlineEditor.defaultConfig = config;
BalloonEditor.defaultConfig = config;


/* harmony default export */ const ckeditor = ({  ClassicEditor, InlineEditor, BalloonEditor, EditorWatchdog: EditorWatchdog });
})();

__webpack_exports__ = __webpack_exports__["default"];
/******/ 	return __webpack_exports__;
/******/ })()
;
});
//# sourceMappingURL=ckeditor.js.map
