UNPKG

114 kBJavaScriptView Raw
1/**
2 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4 */
5import { ObservableMixin, insertToPriorityArray, EmitterMixin, CKEditorError, Config, Locale, Collection, KeystrokeHandler, setDataInElement } from '@ckeditor/ckeditor5-utils/dist/index.js';
6import { Model, StylesProcessor, DataController, EditingController, Conversion } from '@ckeditor/ckeditor5-engine/dist/index.js';
7import { isFunction } from 'lodash-es';
8
9class Plugin extends ObservableMixin() {
10 /**
11 * Disables the plugin.
12 *
13 * Plugin may be disabled by multiple features or algorithms (at once). When disabling a plugin, unique id should be passed
14 * (e.g. feature name). The same identifier should be used when {@link #clearForceDisabled enabling back} the plugin.
15 * The plugin becomes enabled only after all features {@link #clearForceDisabled enabled it back}.
16 *
17 * Disabling and enabling a plugin:
18 *
19 * ```ts
20 * plugin.isEnabled; // -> true
21 * plugin.forceDisabled( 'MyFeature' );
22 * plugin.isEnabled; // -> false
23 * plugin.clearForceDisabled( 'MyFeature' );
24 * plugin.isEnabled; // -> true
25 * ```
26 *
27 * Plugin disabled by multiple features:
28 *
29 * ```ts
30 * plugin.forceDisabled( 'MyFeature' );
31 * plugin.forceDisabled( 'OtherFeature' );
32 * plugin.clearForceDisabled( 'MyFeature' );
33 * plugin.isEnabled; // -> false
34 * plugin.clearForceDisabled( 'OtherFeature' );
35 * plugin.isEnabled; // -> true
36 * ```
37 *
38 * Multiple disabling with the same identifier is redundant:
39 *
40 * ```ts
41 * plugin.forceDisabled( 'MyFeature' );
42 * plugin.forceDisabled( 'MyFeature' );
43 * plugin.clearForceDisabled( 'MyFeature' );
44 * plugin.isEnabled; // -> true
45 * ```
46 *
47 * **Note:** some plugins or algorithms may have more complex logic when it comes to enabling or disabling certain plugins,
48 * so the plugin might be still disabled after {@link #clearForceDisabled} was used.
49 *
50 * @param id Unique identifier for disabling. Use the same id when {@link #clearForceDisabled enabling back} the plugin.
51 */ forceDisabled(id) {
52 this._disableStack.add(id);
53 if (this._disableStack.size == 1) {
54 this.on('set:isEnabled', forceDisable$1, {
55 priority: 'highest'
56 });
57 this.isEnabled = false;
58 }
59 }
60 /**
61 * Clears forced disable previously set through {@link #forceDisabled}. See {@link #forceDisabled}.
62 *
63 * @param id Unique identifier, equal to the one passed in {@link #forceDisabled} call.
64 */ clearForceDisabled(id) {
65 this._disableStack.delete(id);
66 if (this._disableStack.size == 0) {
67 this.off('set:isEnabled', forceDisable$1);
68 this.isEnabled = true;
69 }
70 }
71 /**
72 * @inheritDoc
73 */ destroy() {
74 this.stopListening();
75 }
76 /**
77 * @inheritDoc
78 */ static get isContextPlugin() {
79 return false;
80 }
81 /**
82 * @inheritDoc
83 */ constructor(editor){
84 super();
85 /**
86 * Holds identifiers for {@link #forceDisabled} mechanism.
87 */ this._disableStack = new Set();
88 this.editor = editor;
89 this.set('isEnabled', true);
90 }
91}
92/**
93 * Helper function that forces plugin to be disabled.
94 */ function forceDisable$1(evt) {
95 evt.return = false;
96 evt.stop();
97}
98
99class Command extends ObservableMixin() {
100 /**
101 * A flag indicating whether a command execution changes the editor data or not.
102 *
103 * Commands with `affectsData` set to `false` will not be automatically disabled in
104 * the {@link module:core/editor/editor~Editor#isReadOnly read-only mode} and
105 * {@glink features/read-only#related-features other editor modes} with restricted user write permissions.
106 *
107 * **Note:** You do not have to set it for your every command. It is `true` by default.
108 *
109 * @default true
110 */ get affectsData() {
111 return this._affectsData;
112 }
113 set affectsData(affectsData) {
114 this._affectsData = affectsData;
115 }
116 /**
117 * Refreshes the command. The command should update its {@link #isEnabled} and {@link #value} properties
118 * in this method.
119 *
120 * This method is automatically called when
121 * {@link module:engine/model/document~Document#event:change any changes are applied to the document}.
122 */ refresh() {
123 this.isEnabled = true;
124 }
125 /**
126 * Disables the command.
127 *
128 * Command may be disabled by multiple features or algorithms (at once). When disabling a command, unique id should be passed
129 * (e.g. the feature name). The same identifier should be used when {@link #clearForceDisabled enabling back} the command.
130 * The command becomes enabled only after all features {@link #clearForceDisabled enabled it back}.
131 *
132 * Disabling and enabling a command:
133 *
134 * ```ts
135 * command.isEnabled; // -> true
136 * command.forceDisabled( 'MyFeature' );
137 * command.isEnabled; // -> false
138 * command.clearForceDisabled( 'MyFeature' );
139 * command.isEnabled; // -> true
140 * ```
141 *
142 * Command disabled by multiple features:
143 *
144 * ```ts
145 * command.forceDisabled( 'MyFeature' );
146 * command.forceDisabled( 'OtherFeature' );
147 * command.clearForceDisabled( 'MyFeature' );
148 * command.isEnabled; // -> false
149 * command.clearForceDisabled( 'OtherFeature' );
150 * command.isEnabled; // -> true
151 * ```
152 *
153 * Multiple disabling with the same identifier is redundant:
154 *
155 * ```ts
156 * command.forceDisabled( 'MyFeature' );
157 * command.forceDisabled( 'MyFeature' );
158 * command.clearForceDisabled( 'MyFeature' );
159 * command.isEnabled; // -> true
160 * ```
161 *
162 * **Note:** some commands or algorithms may have more complex logic when it comes to enabling or disabling certain commands,
163 * so the command might be still disabled after {@link #clearForceDisabled} was used.
164 *
165 * @param id Unique identifier for disabling. Use the same id when {@link #clearForceDisabled enabling back} the command.
166 */ forceDisabled(id) {
167 this._disableStack.add(id);
168 if (this._disableStack.size == 1) {
169 this.on('set:isEnabled', forceDisable, {
170 priority: 'highest'
171 });
172 this.isEnabled = false;
173 }
174 }
175 /**
176 * Clears forced disable previously set through {@link #forceDisabled}. See {@link #forceDisabled}.
177 *
178 * @param id Unique identifier, equal to the one passed in {@link #forceDisabled} call.
179 */ clearForceDisabled(id) {
180 this._disableStack.delete(id);
181 if (this._disableStack.size == 0) {
182 this.off('set:isEnabled', forceDisable);
183 this.refresh();
184 }
185 }
186 /**
187 * Executes the command.
188 *
189 * A command may accept parameters. They will be passed from {@link module:core/editor/editor~Editor#execute `editor.execute()`}
190 * to the command.
191 *
192 * The `execute()` method will automatically abort when the command is disabled ({@link #isEnabled} is `false`).
193 * This behavior is implemented by a high priority listener to the {@link #event:execute} event.
194 *
195 * In order to see how to disable a command from "outside" see the {@link #isEnabled} documentation.
196 *
197 * This method may return a value, which would be forwarded all the way down to the
198 * {@link module:core/editor/editor~Editor#execute `editor.execute()`}.
199 *
200 * @fires execute
201 */ execute(...args) {
202 return undefined;
203 }
204 /**
205 * Destroys the command.
206 */ destroy() {
207 this.stopListening();
208 }
209 /**
210 * Creates a new `Command` instance.
211 *
212 * @param editor The editor on which this command will be used.
213 */ constructor(editor){
214 super();
215 this.editor = editor;
216 this.set('value', undefined);
217 this.set('isEnabled', false);
218 this._affectsData = true;
219 this._isEnabledBasedOnSelection = true;
220 this._disableStack = new Set();
221 this.decorate('execute');
222 // By default, every command is refreshed when changes are applied to the model.
223 this.listenTo(this.editor.model.document, 'change', ()=>{
224 this.refresh();
225 });
226 this.listenTo(editor, 'change:isReadOnly', ()=>{
227 this.refresh();
228 });
229 // By default, commands are disabled if the selection is in non-editable place or editor is in read-only mode.
230 this.on('set:isEnabled', (evt)=>{
231 if (!this.affectsData) {
232 return;
233 }
234 const selection = editor.model.document.selection;
235 const selectionInGraveyard = selection.getFirstPosition().root.rootName == '$graveyard';
236 const canEditAtSelection = !selectionInGraveyard && editor.model.canEditAt(selection);
237 // Disable if editor is read only, or when selection is in a place which cannot be edited.
238 //
239 // Checking `editor.isReadOnly` is needed for all commands that have `_isEnabledBasedOnSelection == false`.
240 // E.g. undo does not base on selection, but affects data and should be disabled when the editor is in read-only mode.
241 if (editor.isReadOnly || this._isEnabledBasedOnSelection && !canEditAtSelection) {
242 evt.return = false;
243 evt.stop();
244 }
245 }, {
246 priority: 'highest'
247 });
248 this.on('execute', (evt)=>{
249 if (!this.isEnabled) {
250 evt.stop();
251 }
252 }, {
253 priority: 'high'
254 });
255 }
256}
257/**
258 * Helper function that forces command to be disabled.
259 */ function forceDisable(evt) {
260 evt.return = false;
261 evt.stop();
262}
263
264class MultiCommand extends Command {
265 /**
266 * @inheritDoc
267 */ refresh() {
268 // Override base command refresh(): the command's state is changed when one of child commands changes states.
269 }
270 /**
271 * Executes the first enabled command which has the highest priority of all registered child commands.
272 *
273 * @returns The value returned by the {@link module:core/command~Command#execute `command.execute()`}.
274 */ execute(...args) {
275 const command = this._getFirstEnabledCommand();
276 return !!command && command.execute(args);
277 }
278 /**
279 * Registers a child command.
280 *
281 * @param options An object with configuration options.
282 * @param options.priority Priority of a command to register.
283 */ registerChildCommand(command, options = {}) {
284 insertToPriorityArray(this._childCommandsDefinitions, {
285 command,
286 priority: options.priority || 'normal'
287 });
288 // Change multi-command enabled state when one of registered commands changes state.
289 command.on('change:isEnabled', ()=>this._checkEnabled());
290 this._checkEnabled();
291 }
292 /**
293 * Checks if any of child commands is enabled.
294 */ _checkEnabled() {
295 this.isEnabled = !!this._getFirstEnabledCommand();
296 }
297 /**
298 * Returns a first enabled command with the highest priority or `undefined` if none of them is enabled.
299 */ _getFirstEnabledCommand() {
300 const commandDefinition = this._childCommandsDefinitions.find(({ command })=>command.isEnabled);
301 return commandDefinition && commandDefinition.command;
302 }
303 constructor(){
304 super(...arguments);
305 /**
306 * Registered child commands definitions.
307 */ this._childCommandsDefinitions = [];
308 }
309}
310
311class PluginCollection extends EmitterMixin() {
312 /**
313 * Iterable interface.
314 *
315 * Returns `[ PluginConstructor, pluginInstance ]` pairs.
316 */ *[Symbol.iterator]() {
317 for (const entry of this._plugins){
318 if (typeof entry[0] == 'function') {
319 yield entry;
320 }
321 }
322 }
323 /**
324 * Gets the plugin instance by its constructor or name.
325 *
326 * ```ts
327 * // Check if 'Clipboard' plugin was loaded.
328 * if ( editor.plugins.has( 'ClipboardPipeline' ) ) {
329 * // Get clipboard plugin instance
330 * const clipboard = editor.plugins.get( 'ClipboardPipeline' );
331 *
332 * this.listenTo( clipboard, 'inputTransformation', ( evt, data ) => {
333 * // Do something on clipboard input.
334 * } );
335 * }
336 * ```
337 *
338 * **Note**: This method will throw an error if a plugin is not loaded. Use `{@link #has editor.plugins.has()}`
339 * to check if a plugin is available.
340 *
341 * @param key The plugin constructor or {@link module:core/plugin~PluginStaticMembers#pluginName name}.
342 */ get(key) {
343 const plugin = this._plugins.get(key);
344 if (!plugin) {
345 let pluginName = key;
346 if (typeof key == 'function') {
347 pluginName = key.pluginName || key.name;
348 }
349 /**
350 * The plugin is not loaded and could not be obtained.
351 *
352 * Plugin classes (constructors) need to be provided to the editor and must be loaded before they can be obtained from
353 * the plugin collection.
354 * This is usually done in CKEditor 5 builds by setting the {@link module:core/editor/editor~Editor.builtinPlugins}
355 * property.
356 *
357 * **Note**: You can use `{@link module:core/plugincollection~PluginCollection#has editor.plugins.has()}`
358 * to check if a plugin was loaded.
359 *
360 * @error plugincollection-plugin-not-loaded
361 * @param plugin The name of the plugin which is not loaded.
362 */ throw new CKEditorError('plugincollection-plugin-not-loaded', this._context, {
363 plugin: pluginName
364 });
365 }
366 return plugin;
367 }
368 /**
369 * Checks if a plugin is loaded.
370 *
371 * ```ts
372 * // Check if the 'Clipboard' plugin was loaded.
373 * if ( editor.plugins.has( 'ClipboardPipeline' ) ) {
374 * // Now use the clipboard plugin instance:
375 * const clipboard = editor.plugins.get( 'ClipboardPipeline' );
376 *
377 * // ...
378 * }
379 * ```
380 *
381 * @param key The plugin constructor or {@link module:core/plugin~PluginStaticMembers#pluginName name}.
382 */ has(key) {
383 return this._plugins.has(key);
384 }
385 /**
386 * Initializes a set of plugins and adds them to the collection.
387 *
388 * @param plugins An array of {@link module:core/plugin~PluginInterface plugin constructors}
389 * or {@link module:core/plugin~PluginStaticMembers#pluginName plugin names}.
390 * @param pluginsToRemove Names of the plugins or plugin constructors
391 * that should not be loaded (despite being specified in the `plugins` array).
392 * @param pluginsSubstitutions An array of {@link module:core/plugin~PluginInterface plugin constructors}
393 * that will be used to replace plugins of the same names that were passed in `plugins` or that are in their dependency tree.
394 * A useful option for replacing built-in plugins while creating tests (for mocking their APIs). Plugins that will be replaced
395 * must follow these rules:
396 * * The new plugin must be a class.
397 * * The new plugin must be named.
398 * * Both plugins must not depend on other plugins.
399 * @returns A promise which gets resolved once all plugins are loaded and available in the collection.
400 */ init(plugins, pluginsToRemove = [], pluginsSubstitutions = []) {
401 // Plugin initialization procedure consists of 2 main steps:
402 // 1) collecting all available plugin constructors,
403 // 2) verification whether all required plugins can be instantiated.
404 //
405 // In the first step, all plugin constructors, available in the provided `plugins` array and inside
406 // plugin's dependencies (from the `Plugin.requires` array), are recursively collected and added to the existing
407 // `this._availablePlugins` map, but without any verification at the given moment. Performing the verification
408 // at this point (during the plugin constructor searching) would cause false errors to occur, that some plugin
409 // is missing but in fact it may be defined further in the array as the dependency of other plugin. After
410 // traversing the entire dependency tree, it will be checked if all required "top level" plugins are available.
411 //
412 // In the second step, the list of plugins that have not been explicitly removed is traversed to get all the
413 // plugin constructors to be instantiated in the correct order and to validate against some rules. Finally, if
414 // no plugin is missing and no other error has been found, they all will be instantiated.
415 // eslint-disable-next-line @typescript-eslint/no-this-alias
416 const that = this;
417 const context = this._context;
418 findAvailablePluginConstructors(plugins);
419 validatePlugins(plugins);
420 const pluginsToLoad = plugins.filter((plugin)=>!isPluginRemoved(plugin, pluginsToRemove));
421 const pluginConstructors = [
422 ...getPluginConstructors(pluginsToLoad)
423 ];
424 substitutePlugins(pluginConstructors, pluginsSubstitutions);
425 const pluginInstances = loadPlugins(pluginConstructors);
426 return initPlugins(pluginInstances, 'init').then(()=>initPlugins(pluginInstances, 'afterInit')).then(()=>pluginInstances);
427 function isPluginConstructor(plugin) {
428 return typeof plugin === 'function';
429 }
430 function isContextPlugin(plugin) {
431 return isPluginConstructor(plugin) && !!plugin.isContextPlugin;
432 }
433 function isPluginRemoved(plugin, pluginsToRemove) {
434 return pluginsToRemove.some((removedPlugin)=>{
435 if (removedPlugin === plugin) {
436 return true;
437 }
438 if (getPluginName(plugin) === removedPlugin) {
439 return true;
440 }
441 if (getPluginName(removedPlugin) === plugin) {
442 return true;
443 }
444 return false;
445 });
446 }
447 function getPluginName(plugin) {
448 return isPluginConstructor(plugin) ? plugin.pluginName || plugin.name : plugin;
449 }
450 function findAvailablePluginConstructors(plugins, processed = new Set()) {
451 plugins.forEach((plugin)=>{
452 if (!isPluginConstructor(plugin)) {
453 return;
454 }
455 if (processed.has(plugin)) {
456 return;
457 }
458 processed.add(plugin);
459 if (plugin.pluginName && !that._availablePlugins.has(plugin.pluginName)) {
460 that._availablePlugins.set(plugin.pluginName, plugin);
461 }
462 if (plugin.requires) {
463 findAvailablePluginConstructors(plugin.requires, processed);
464 }
465 });
466 }
467 function getPluginConstructors(plugins, processed = new Set()) {
468 return plugins.map((plugin)=>{
469 return isPluginConstructor(plugin) ? plugin : that._availablePlugins.get(plugin);
470 }).reduce((result, plugin)=>{
471 if (processed.has(plugin)) {
472 return result;
473 }
474 processed.add(plugin);
475 if (plugin.requires) {
476 validatePlugins(plugin.requires, plugin);
477 getPluginConstructors(plugin.requires, processed).forEach((plugin)=>result.add(plugin));
478 }
479 return result.add(plugin);
480 }, new Set());
481 }
482 function validatePlugins(plugins, parentPluginConstructor = null) {
483 plugins.map((plugin)=>{
484 return isPluginConstructor(plugin) ? plugin : that._availablePlugins.get(plugin) || plugin;
485 }).forEach((plugin)=>{
486 checkMissingPlugin(plugin, parentPluginConstructor);
487 checkContextPlugin(plugin, parentPluginConstructor);
488 checkRemovedPlugin(plugin, parentPluginConstructor);
489 });
490 }
491 function checkMissingPlugin(plugin, parentPluginConstructor) {
492 if (isPluginConstructor(plugin)) {
493 return;
494 }
495 if (parentPluginConstructor) {
496 /**
497 * A required "soft" dependency was not found on the plugin list.
498 *
499 * When configuring the editor, either prior to building (via
500 * {@link module:core/editor/editor~Editor.builtinPlugins `Editor.builtinPlugins`}) or when
501 * creating a new instance of the editor (e.g. via
502 * {@link module:core/editor/editorconfig~EditorConfig#plugins `config.plugins`}), you need to provide
503 * some of the dependencies for other plugins that you used.
504 *
505 * This error is thrown when one of these dependencies was not provided. The name of the missing plugin
506 * can be found in `missingPlugin` and the plugin that required it in `requiredBy`.
507 *
508 * In order to resolve it, you need to import the missing plugin and add it to the
509 * current list of plugins (`Editor.builtinPlugins` or `config.plugins`/`config.extraPlugins`).
510 *
511 * Soft requirements were introduced in version 26.0.0. If you happen to stumble upon this error
512 * when upgrading to version 26.0.0, read also the
513 * {@glink updating/guides/update-to-26 Migration to 26.0.0} guide.
514 *
515 * @error plugincollection-soft-required
516 * @param missingPlugin The name of the required plugin.
517 * @param requiredBy The name of the plugin that requires the other plugin.
518 */ throw new CKEditorError('plugincollection-soft-required', context, {
519 missingPlugin: plugin,
520 requiredBy: getPluginName(parentPluginConstructor)
521 });
522 }
523 /**
524 * A plugin is not available and could not be loaded.
525 *
526 * Plugin classes (constructors) need to be provided to the editor before they can be loaded by name.
527 * This is usually done in CKEditor 5 builds by setting the {@link module:core/editor/editor~Editor.builtinPlugins}
528 * property.
529 *
530 * **If you see this warning when using one of the {@glink installation/getting-started/predefined-builds
531 * CKEditor 5 Builds}**,
532 * it means that you try to enable a plugin which was not included in that build. This may be due to a typo
533 * in the plugin name or simply because that plugin is not a part of this build. In the latter scenario,
534 * read more about {@glink installation/getting-started/quick-start custom builds}.
535 *
536 * **If you see this warning when using one of the editor creators directly** (not a build), then it means
537 * that you tried loading plugins by name. However, unlike CKEditor 4, CKEditor 5 does not implement a "plugin loader".
538 * This means that CKEditor 5 does not know where to load the plugin modules from. Therefore, you need to
539 * provide each plugin through a reference (as a constructor function). Check out the examples in
540 * {@glink installation/advanced/alternative-setups/integrating-from-source-webpack "Building from source"}.
541 *
542 * @error plugincollection-plugin-not-found
543 * @param plugin The name of the plugin which could not be loaded.
544 */ throw new CKEditorError('plugincollection-plugin-not-found', context, {
545 plugin
546 });
547 }
548 function checkContextPlugin(plugin, parentPluginConstructor) {
549 if (!isContextPlugin(parentPluginConstructor)) {
550 return;
551 }
552 if (isContextPlugin(plugin)) {
553 return;
554 }
555 /**
556 * If a plugin is a context plugin, all plugins it requires should also be context plugins
557 * instead of plugins. In other words, if one plugin can be used in the context,
558 * all its requirements should also be ready to be used in the context. Note that the context
559 * provides only a part of the API provided by the editor. If one plugin needs a full
560 * editor API, all plugins which require it are considered as plugins that need a full
561 * editor API.
562 *
563 * @error plugincollection-context-required
564 * @param plugin The name of the required plugin.
565 * @param requiredBy The name of the parent plugin.
566 */ throw new CKEditorError('plugincollection-context-required', context, {
567 plugin: getPluginName(plugin),
568 requiredBy: getPluginName(parentPluginConstructor)
569 });
570 }
571 function checkRemovedPlugin(plugin, parentPluginConstructor) {
572 if (!parentPluginConstructor) {
573 return;
574 }
575 if (!isPluginRemoved(plugin, pluginsToRemove)) {
576 return;
577 }
578 /**
579 * Cannot load a plugin because one of its dependencies is listed in the `removePlugins` option.
580 *
581 * @error plugincollection-required
582 * @param plugin The name of the required plugin.
583 * @param requiredBy The name of the parent plugin.
584 */ throw new CKEditorError('plugincollection-required', context, {
585 plugin: getPluginName(plugin),
586 requiredBy: getPluginName(parentPluginConstructor)
587 });
588 }
589 function loadPlugins(pluginConstructors) {
590 return pluginConstructors.map((PluginConstructor)=>{
591 let pluginInstance = that._contextPlugins.get(PluginConstructor);
592 pluginInstance = pluginInstance || new PluginConstructor(context);
593 that._add(PluginConstructor, pluginInstance);
594 return pluginInstance;
595 });
596 }
597 function initPlugins(pluginInstances, method) {
598 return pluginInstances.reduce((promise, plugin)=>{
599 if (!plugin[method]) {
600 return promise;
601 }
602 if (that._contextPlugins.has(plugin)) {
603 return promise;
604 }
605 return promise.then(plugin[method].bind(plugin));
606 }, Promise.resolve());
607 }
608 /**
609 * Replaces plugin constructors with the specified set of plugins.
610 */ function substitutePlugins(pluginConstructors, pluginsSubstitutions) {
611 for (const pluginItem of pluginsSubstitutions){
612 if (typeof pluginItem != 'function') {
613 /**
614 * The plugin replacing an existing plugin must be a function.
615 *
616 * @error plugincollection-replace-plugin-invalid-type
617 */ throw new CKEditorError('plugincollection-replace-plugin-invalid-type', null, {
618 pluginItem
619 });
620 }
621 const pluginName = pluginItem.pluginName;
622 if (!pluginName) {
623 /**
624 * The plugin replacing an existing plugin must have a name.
625 *
626 * @error plugincollection-replace-plugin-missing-name
627 */ throw new CKEditorError('plugincollection-replace-plugin-missing-name', null, {
628 pluginItem
629 });
630 }
631 if (pluginItem.requires && pluginItem.requires.length) {
632 /**
633 * The plugin replacing an existing plugin cannot depend on other plugins.
634 *
635 * @error plugincollection-plugin-for-replacing-cannot-have-dependencies
636 */ throw new CKEditorError('plugincollection-plugin-for-replacing-cannot-have-dependencies', null, {
637 pluginName
638 });
639 }
640 const pluginToReplace = that._availablePlugins.get(pluginName);
641 if (!pluginToReplace) {
642 /**
643 * The replaced plugin does not exist in the
644 * {@link module:core/plugincollection~PluginCollection available plugins} collection.
645 *
646 * @error plugincollection-plugin-for-replacing-not-exist
647 */ throw new CKEditorError('plugincollection-plugin-for-replacing-not-exist', null, {
648 pluginName
649 });
650 }
651 const indexInPluginConstructors = pluginConstructors.indexOf(pluginToReplace);
652 if (indexInPluginConstructors === -1) {
653 // The Context feature can substitute plugins as well.
654 // It may happen that the editor will be created with the given context, where the plugin for substitute
655 // was already replaced. In such a case, we don't want to do it again.
656 if (that._contextPlugins.has(pluginToReplace)) {
657 return;
658 }
659 /**
660 * The replaced plugin will not be loaded so it cannot be replaced.
661 *
662 * @error plugincollection-plugin-for-replacing-not-loaded
663 */ throw new CKEditorError('plugincollection-plugin-for-replacing-not-loaded', null, {
664 pluginName
665 });
666 }
667 if (pluginToReplace.requires && pluginToReplace.requires.length) {
668 /**
669 * The replaced plugin cannot depend on other plugins.
670 *
671 * @error plugincollection-replaced-plugin-cannot-have-dependencies
672 */ throw new CKEditorError('plugincollection-replaced-plugin-cannot-have-dependencies', null, {
673 pluginName
674 });
675 }
676 pluginConstructors.splice(indexInPluginConstructors, 1, pluginItem);
677 that._availablePlugins.set(pluginName, pluginItem);
678 }
679 }
680 }
681 /**
682 * Destroys all loaded plugins.
683 */ destroy() {
684 const promises = [];
685 for (const [, pluginInstance] of this){
686 if (typeof pluginInstance.destroy == 'function' && !this._contextPlugins.has(pluginInstance)) {
687 promises.push(pluginInstance.destroy());
688 }
689 }
690 return Promise.all(promises);
691 }
692 /**
693 * Adds the plugin to the collection. Exposed mainly for testing purposes.
694 *
695 * @param PluginConstructor The plugin constructor.
696 * @param plugin The instance of the plugin.
697 */ _add(PluginConstructor, plugin) {
698 this._plugins.set(PluginConstructor, plugin);
699 const pluginName = PluginConstructor.pluginName;
700 if (!pluginName) {
701 return;
702 }
703 if (this._plugins.has(pluginName)) {
704 /**
705 * Two plugins with the same {@link module:core/plugin~PluginStaticMembers#pluginName} were loaded.
706 * This will lead to runtime conflicts between these plugins.
707 *
708 * In practice, this warning usually means that new plugins were added to an existing CKEditor 5 build.
709 * Plugins should always be added to a source version of the editor (`@ckeditor/ckeditor5-editor-*`),
710 * not to an editor imported from one of the `@ckeditor/ckeditor5-build-*` packages.
711 *
712 * Check your import paths and the list of plugins passed to
713 * {@link module:core/editor/editor~Editor.create `Editor.create()`}
714 * or specified in {@link module:core/editor/editor~Editor.builtinPlugins `Editor.builtinPlugins`}.
715 *
716 * The second option is that your `node_modules/` directory contains duplicated versions of the same
717 * CKEditor 5 packages. Normally, on clean installations, npm deduplicates packages in `node_modules/`, so
718 * it may be enough to call `rm -rf node_modules && npm i`. However, if you installed conflicting versions
719 * of some packages, their dependencies may need to be installed in more than one version which may lead to this
720 * warning.
721 *
722 * Technically speaking, this error occurs because after adding a plugin to an existing editor build
723 * the dependencies of this plugin are being duplicated.
724 * They are already built into that editor build and now get added for the second time as dependencies
725 * of the plugin you are installing.
726 *
727 * Read more about {@glink installation/plugins/installing-plugins Installing plugins}.
728 *
729 * @error plugincollection-plugin-name-conflict
730 * @param pluginName The duplicated plugin name.
731 * @param plugin1 The first plugin constructor.
732 * @param plugin2 The second plugin constructor.
733 */ throw new CKEditorError('plugincollection-plugin-name-conflict', null, {
734 pluginName,
735 plugin1: this._plugins.get(pluginName).constructor,
736 plugin2: PluginConstructor
737 });
738 }
739 this._plugins.set(pluginName, plugin);
740 }
741 /**
742 * Creates an instance of the plugin collection class.
743 * Allows loading and initializing plugins and their dependencies.
744 * Allows providing a list of already loaded plugins. These plugins will not be destroyed along with this collection.
745 *
746 * @param availablePlugins Plugins (constructors) which the collection will be able to use
747 * when {@link module:core/plugincollection~PluginCollection#init} is used with the plugin names (strings, instead of constructors).
748 * Usually, the editor will pass its built-in plugins to the collection so they can later be
749 * used in `config.plugins` or `config.removePlugins` by names.
750 * @param contextPlugins A list of already initialized plugins represented by a `[ PluginConstructor, pluginInstance ]` pair.
751 */ constructor(context, availablePlugins = [], contextPlugins = []){
752 super();
753 this._plugins = new Map();
754 this._context = context;
755 this._availablePlugins = new Map();
756 for (const PluginConstructor of availablePlugins){
757 if (PluginConstructor.pluginName) {
758 this._availablePlugins.set(PluginConstructor.pluginName, PluginConstructor);
759 }
760 }
761 this._contextPlugins = new Map();
762 for (const [PluginConstructor, pluginInstance] of contextPlugins){
763 this._contextPlugins.set(PluginConstructor, pluginInstance);
764 this._contextPlugins.set(pluginInstance, PluginConstructor);
765 // To make it possible to require a plugin by its name.
766 if (PluginConstructor.pluginName) {
767 this._availablePlugins.set(PluginConstructor.pluginName, PluginConstructor);
768 }
769 }
770 }
771}
772
773class Context {
774 /**
775 * Loads and initializes plugins specified in the configuration.
776 *
777 * @returns A promise which resolves once the initialization is completed, providing an array of loaded plugins.
778 */ initPlugins() {
779 const plugins = this.config.get('plugins') || [];
780 const substitutePlugins = this.config.get('substitutePlugins') || [];
781 // Plugins for substitution should be checked as well.
782 for (const Plugin of plugins.concat(substitutePlugins)){
783 if (typeof Plugin != 'function') {
784 /**
785 * Only a constructor function is allowed as a {@link module:core/contextplugin~ContextPlugin context plugin}.
786 *
787 * @error context-initplugins-constructor-only
788 */ throw new CKEditorError('context-initplugins-constructor-only', null, {
789 Plugin
790 });
791 }
792 if (Plugin.isContextPlugin !== true) {
793 /**
794 * Only a plugin marked as a {@link module:core/contextplugin~ContextPlugin.isContextPlugin context plugin}
795 * is allowed to be used with a context.
796 *
797 * @error context-initplugins-invalid-plugin
798 */ throw new CKEditorError('context-initplugins-invalid-plugin', null, {
799 Plugin
800 });
801 }
802 }
803 return this.plugins.init(plugins, [], substitutePlugins);
804 }
805 /**
806 * Destroys the context instance and all editors used with the context,
807 * releasing all resources used by the context.
808 *
809 * @returns A promise that resolves once the context instance is fully destroyed.
810 */ destroy() {
811 return Promise.all(Array.from(this.editors, (editor)=>editor.destroy())).then(()=>this.plugins.destroy());
812 }
813 /**
814 * Adds a reference to the editor which is used with this context.
815 *
816 * When the given editor has created the context, the reference to this editor will be stored
817 * as a {@link ~Context#_contextOwner}.
818 *
819 * This method should only be used by the editor.
820 *
821 * @internal
822 * @param isContextOwner Stores the given editor as a context owner.
823 */ _addEditor(editor, isContextOwner) {
824 if (this._contextOwner) {
825 /**
826 * Cannot add multiple editors to the context which is created by the editor.
827 *
828 * @error context-addeditor-private-context
829 */ throw new CKEditorError('context-addeditor-private-context');
830 }
831 this.editors.add(editor);
832 if (isContextOwner) {
833 this._contextOwner = editor;
834 }
835 }
836 /**
837 * Removes a reference to the editor which was used with this context.
838 * When the context was created by the given editor, the context will be destroyed.
839 *
840 * This method should only be used by the editor.
841 *
842 * @internal
843 * @return A promise that resolves once the editor is removed from the context or when the context was destroyed.
844 */ _removeEditor(editor) {
845 if (this.editors.has(editor)) {
846 this.editors.remove(editor);
847 }
848 if (this._contextOwner === editor) {
849 return this.destroy();
850 }
851 return Promise.resolve();
852 }
853 /**
854 * Returns the context configuration which will be copied to the editors created using this context.
855 *
856 * The configuration returned by this method has the plugins configuration removed – plugins are shared with all editors
857 * through another mechanism.
858 *
859 * This method should only be used by the editor.
860 *
861 * @internal
862 * @returns Configuration as a plain object.
863 */ _getEditorConfig() {
864 const result = {};
865 for (const name of this.config.names()){
866 if (![
867 'plugins',
868 'removePlugins',
869 'extraPlugins'
870 ].includes(name)) {
871 result[name] = this.config.get(name);
872 }
873 }
874 return result;
875 }
876 /**
877 * Creates and initializes a new context instance.
878 *
879 * ```ts
880 * const commonConfig = { ... }; // Configuration for all the plugins and editors.
881 * const editorPlugins = [ ... ]; // Regular plugins here.
882 *
883 * Context
884 * .create( {
885 * // Only context plugins here.
886 * plugins: [ ... ],
887 *
888 * // Configure the language for all the editors (it cannot be overwritten).
889 * language: { ... },
890 *
891 * // Configuration for context plugins.
892 * comments: { ... },
893 * ...
894 *
895 * // Default configuration for editor plugins.
896 * toolbar: { ... },
897 * image: { ... },
898 * ...
899 * } )
900 * .then( context => {
901 * const promises = [];
902 *
903 * promises.push( ClassicEditor.create(
904 * document.getElementById( 'editor1' ),
905 * {
906 * editorPlugins,
907 * context
908 * }
909 * ) );
910 *
911 * promises.push( ClassicEditor.create(
912 * document.getElementById( 'editor2' ),
913 * {
914 * editorPlugins,
915 * context,
916 * toolbar: { ... } // You can overwrite the configuration of the context.
917 * }
918 * ) );
919 *
920 * return Promise.all( promises );
921 * } );
922 * ```
923 *
924 * @param config The context configuration.
925 * @returns A promise resolved once the context is ready. The promise resolves with the created context instance.
926 */ static create(config) {
927 return new Promise((resolve)=>{
928 const context = new this(config);
929 resolve(context.initPlugins().then(()=>context));
930 });
931 }
932 /**
933 * Creates a context instance with a given configuration.
934 *
935 * Usually not to be used directly. See the static {@link module:core/context~Context.create `create()`} method.
936 *
937 * @param config The context configuration.
938 */ constructor(config){
939 /**
940 * Reference to the editor which created the context.
941 * Null when the context was created outside of the editor.
942 *
943 * It is used to destroy the context when removing the editor that has created the context.
944 */ this._contextOwner = null;
945 // We don't pass translations to the config, because its behavior of splitting keys
946 // with dots (e.g. `resize.width` => `resize: { width }`) breaks the translations.
947 const { translations, ...rest } = config || {};
948 this.config = new Config(rest, this.constructor.defaultConfig);
949 const availablePlugins = this.constructor.builtinPlugins;
950 this.config.define('plugins', availablePlugins);
951 this.plugins = new PluginCollection(this, availablePlugins);
952 const languageConfig = this.config.get('language') || {};
953 this.locale = new Locale({
954 uiLanguage: typeof languageConfig === 'string' ? languageConfig : languageConfig.ui,
955 contentLanguage: this.config.get('language.content'),
956 translations
957 });
958 this.t = this.locale.t;
959 this.editors = new Collection();
960 }
961}
962
963class ContextPlugin extends ObservableMixin() {
964 /**
965 * @inheritDoc
966 */ destroy() {
967 this.stopListening();
968 }
969 /**
970 * @inheritDoc
971 */ static get isContextPlugin() {
972 return true;
973 }
974 /**
975 * Creates a new plugin instance.
976 */ constructor(context){
977 super();
978 this.context = context;
979 }
980}
981
982class CommandCollection {
983 /**
984 * Registers a new command.
985 *
986 * @param commandName The name of the command.
987 */ add(commandName, command) {
988 this._commands.set(commandName, command);
989 }
990 /**
991 * Retrieves a command from the collection.
992 *
993 * @param commandName The name of the command.
994 */ get(commandName) {
995 return this._commands.get(commandName);
996 }
997 /**
998 * Executes a command.
999 *
1000 * @param commandName The name of the command.
1001 * @param commandParams Command parameters.
1002 * @returns The value returned by the {@link module:core/command~Command#execute `command.execute()`}.
1003 */ execute(commandName, ...commandParams) {
1004 const command = this.get(commandName);
1005 if (!command) {
1006 /**
1007 * Command does not exist.
1008 *
1009 * @error commandcollection-command-not-found
1010 * @param commandName Name of the command.
1011 */ throw new CKEditorError('commandcollection-command-not-found', this, {
1012 commandName
1013 });
1014 }
1015 return command.execute(...commandParams);
1016 }
1017 /**
1018 * Returns iterator of command names.
1019 */ *names() {
1020 yield* this._commands.keys();
1021 }
1022 /**
1023 * Returns iterator of command instances.
1024 */ *commands() {
1025 yield* this._commands.values();
1026 }
1027 /**
1028 * Iterable interface.
1029 *
1030 * Returns `[ commandName, commandInstance ]` pairs.
1031 */ [Symbol.iterator]() {
1032 return this._commands[Symbol.iterator]();
1033 }
1034 /**
1035 * Destroys all collection commands.
1036 */ destroy() {
1037 for (const command of this.commands()){
1038 command.destroy();
1039 }
1040 }
1041 /**
1042 * Creates collection instance.
1043 */ constructor(){
1044 this._commands = new Map();
1045 }
1046}
1047
1048class EditingKeystrokeHandler extends KeystrokeHandler {
1049 /**
1050 * Registers a handler for the specified keystroke.
1051 *
1052 * The handler can be specified as a command name or a callback.
1053 *
1054 * @param keystroke Keystroke defined in a format accepted by
1055 * the {@link module:utils/keyboard~parseKeystroke} function.
1056 * @param callback If a string is passed, then the keystroke will
1057 * {@link module:core/editor/editor~Editor#execute execute a command}.
1058 * If a function, then it will be called with the
1059 * {@link module:engine/view/observer/keyobserver~KeyEventData key event data} object and
1060 * a `cancel()` helper to both `preventDefault()` and `stopPropagation()` of the event.
1061 * @param options Additional options.
1062 * @param options.priority The priority of the keystroke callback. The higher the priority value
1063 * the sooner the callback will be executed. Keystrokes having the same priority
1064 * are called in the order they were added.
1065 */ set(keystroke, callback, options = {}) {
1066 if (typeof callback == 'string') {
1067 const commandName = callback;
1068 callback = (evtData, cancel)=>{
1069 this.editor.execute(commandName);
1070 cancel();
1071 };
1072 }
1073 super.set(keystroke, callback, options);
1074 }
1075 /**
1076 * Creates an instance of the keystroke handler.
1077 */ constructor(editor){
1078 super();
1079 this.editor = editor;
1080 }
1081}
1082
1083const DEFAULT_CATEGORY_ID = 'contentEditing';
1084const DEFAULT_GROUP_ID = 'common';
1085class Accessibility {
1086 /**
1087 * Adds a top-level category in the {@link #keystrokeInfos keystroke information database} with a label and optional description.
1088 *
1089 * Categories organize keystrokes and help users to find the right keystroke. Each category can have multiple groups
1090 * of keystrokes that narrow down the context in which the keystrokes are available. Every keystroke category comes
1091 * with a `'common'` group by default.
1092 *
1093 * By default, two categories are available:
1094 * * `'contentEditing'` for keystrokes related to content creation,
1095 * * `'navigation'` for keystrokes related to navigation in the UI and the content.
1096 *
1097 * To create a new keystroke category with new groups, use the following code:
1098 *
1099 * ```js
1100 * class MyPlugin extends Plugin {
1101 * // ...
1102 * init() {
1103 * const editor = this.editor;
1104 * const t = editor.t;
1105 *
1106 * // ...
1107 *
1108 * editor.accessibility.addKeystrokeInfoCategory( {
1109 * id: 'myCategory',
1110 * label: t( 'My category' ),
1111 * description: t( 'My category description.' ),
1112 * groups: [
1113 * {
1114 * id: 'myGroup',
1115 * label: t( 'My keystroke group' ),
1116 * keystrokes: [
1117 * {
1118 * label: t( 'Keystroke label 1' ),
1119 * keystroke: 'Ctrl+Shift+N'
1120 * },
1121 * {
1122 * label: t( 'Keystroke label 2' ),
1123 * keystroke: 'Ctrl+Shift+M'
1124 * }
1125 * ]
1126 * }
1127 * ]
1128 * };
1129 * }
1130 * }
1131 * ```
1132 *
1133 * See {@link #keystrokeInfos}, {@link #addKeystrokeInfoGroup}, and {@link #addKeystrokeInfos}.
1134 */ addKeystrokeInfoCategory({ id, label, description, groups }) {
1135 this.keystrokeInfos.set(id, {
1136 id,
1137 label,
1138 description,
1139 groups: new Map()
1140 });
1141 this.addKeystrokeInfoGroup({
1142 categoryId: id,
1143 id: DEFAULT_GROUP_ID
1144 });
1145 if (groups) {
1146 groups.forEach((group)=>{
1147 this.addKeystrokeInfoGroup({
1148 categoryId: id,
1149 ...group
1150 });
1151 });
1152 }
1153 }
1154 /**
1155 * Adds a group of keystrokes in a specific category to the {@link #keystrokeInfos keystroke information database}.
1156 *
1157 * Groups narrow down the context in which the keystrokes are available. When `categoryId` is not specified,
1158 * the group goes to the `'contentEditing'` category (default).
1159 *
1160 * To create a new group within an existing category, use the following code:
1161 *
1162 * ```js
1163 * class MyPlugin extends Plugin {
1164 * // ...
1165 * init() {
1166 * const editor = this.editor;
1167 * const t = editor.t;
1168 *
1169 * // ...
1170 *
1171 * editor.accessibility.addKeystrokeInfoGroup( {
1172 * id: 'myGroup',
1173 * categoryId: 'navigation',
1174 * label: t( 'My keystroke group' ),
1175 * keystrokes: [
1176 * {
1177 * label: t( 'Keystroke label 1' ),
1178 * keystroke: 'Ctrl+Shift+N'
1179 * },
1180 * {
1181 * label: t( 'Keystroke label 2' ),
1182 * keystroke: 'Ctrl+Shift+M'
1183 * }
1184 * ]
1185 * } );
1186 * }
1187 * }
1188 * ```
1189 *
1190 * See {@link #keystrokeInfos}, {@link #addKeystrokeInfoCategory}, and {@link #addKeystrokeInfos}.
1191 */ addKeystrokeInfoGroup({ categoryId = DEFAULT_CATEGORY_ID, id, label, keystrokes }) {
1192 const category = this.keystrokeInfos.get(categoryId);
1193 if (!category) {
1194 throw new CKEditorError('accessibility-unknown-keystroke-info-category', this._editor, {
1195 groupId: id,
1196 categoryId
1197 });
1198 }
1199 category.groups.set(id, {
1200 id,
1201 label,
1202 keystrokes: keystrokes || []
1203 });
1204 }
1205 /**
1206 * Adds information about keystrokes to the {@link #keystrokeInfos keystroke information database}.
1207 *
1208 * Keystrokes without specified `groupId` or `categoryId` go to the `'common'` group in the `'contentEditing'` category (default).
1209 *
1210 * To add a keystroke brought by your plugin (using default group and category), use the following code:
1211 *
1212 * ```js
1213 * class MyPlugin extends Plugin {
1214 * // ...
1215 * init() {
1216 * const editor = this.editor;
1217 * const t = editor.t;
1218 *
1219 * // ...
1220 *
1221 * editor.accessibility.addKeystrokeInfos( {
1222 * keystrokes: [
1223 * {
1224 * label: t( 'Keystroke label' ),
1225 * keystroke: 'CTRL+B'
1226 * }
1227 * ]
1228 * } );
1229 * }
1230 * }
1231 * ```
1232 * To add a keystroke in a specific existing `'widget'` group in the default `'contentEditing'` category:
1233 *
1234 * ```js
1235 * class MyPlugin extends Plugin {
1236 * // ...
1237 * init() {
1238 * const editor = this.editor;
1239 * const t = editor.t;
1240 *
1241 * // ...
1242 *
1243 * editor.accessibility.addKeystrokeInfos( {
1244 * // Add a keystroke to the existing "widget" group.
1245 * groupId: 'widget',
1246 * keystrokes: [
1247 * {
1248 * label: t( 'A an action on a selected widget' ),
1249 * keystroke: 'Ctrl+D',
1250 * }
1251 * ]
1252 * } );
1253 * }
1254 * }
1255 * ```
1256 *
1257 * To add a keystroke to another existing category (using default group):
1258 *
1259 * ```js
1260 * class MyPlugin extends Plugin {
1261 * // ...
1262 * init() {
1263 * const editor = this.editor;
1264 * const t = editor.t;
1265 *
1266 * // ...
1267 *
1268 * editor.accessibility.addKeystrokeInfos( {
1269 * // Add keystrokes to the "navigation" category (one of defaults).
1270 * categoryId: 'navigation',
1271 * keystrokes: [
1272 * {
1273 * label: t( 'Keystroke label' ),
1274 * keystroke: 'CTRL+B'
1275 * }
1276 * ]
1277 * } );
1278 * }
1279 * }
1280 * ```
1281 *
1282 * See {@link #keystrokeInfos}, {@link #addKeystrokeInfoGroup}, and {@link #addKeystrokeInfoCategory}.
1283 */ addKeystrokeInfos({ categoryId = DEFAULT_CATEGORY_ID, groupId = DEFAULT_GROUP_ID, keystrokes }) {
1284 if (!this.keystrokeInfos.has(categoryId)) {
1285 /**
1286 * Cannot add keystrokes in an unknown category. Use
1287 * {@link module:core/accessibility~Accessibility#addKeystrokeInfoCategory}
1288 * to add a new category or make sure the specified category exists.
1289 *
1290 * @error accessibility-unknown-keystroke-info-category
1291 * @param categoryId The id of the unknown keystroke category.
1292 * @param keystrokes Keystroke definitions about to be added.
1293 */ throw new CKEditorError('accessibility-unknown-keystroke-info-category', this._editor, {
1294 categoryId,
1295 keystrokes
1296 });
1297 }
1298 const category = this.keystrokeInfos.get(categoryId);
1299 if (!category.groups.has(groupId)) {
1300 /**
1301 * Cannot add keystrokes to an unknown group.
1302 *
1303 * Use {@link module:core/accessibility~Accessibility#addKeystrokeInfoGroup}
1304 * to add a new group or make sure the specified group exists.
1305 *
1306 * @error accessibility-unknown-keystroke-info-group
1307 * @param groupId The id of the unknown keystroke group.
1308 * @param categoryId The id of category the unknown group should belong to.
1309 * @param keystrokes Keystroke definitions about to be added.
1310 */ throw new CKEditorError('accessibility-unknown-keystroke-info-group', this._editor, {
1311 groupId,
1312 categoryId,
1313 keystrokes
1314 });
1315 }
1316 category.groups.get(groupId).keystrokes.push(...keystrokes);
1317 }
1318 /**
1319 * @inheritDoc
1320 */ constructor(editor){
1321 /**
1322 * Stores information about keystrokes brought by editor features for the users to interact with the editor, mainly
1323 * keystroke combinations and their accessible labels.
1324 *
1325 * This information is particularly useful for screen reader and other assistive technology users. It gets displayed
1326 * by the {@link module:ui/editorui/accessibilityhelp/accessibilityhelp~AccessibilityHelp Accessibility help} dialog.
1327 *
1328 * Keystrokes are organized in categories and groups. They can be added using ({@link #addKeystrokeInfoCategory},
1329 * {@link #addKeystrokeInfoGroup}, and {@link #addKeystrokeInfos}) methods.
1330 *
1331 * Please note that:
1332 * * two categories are always available:
1333 * * `'contentEditing'` for keystrokes related to content creation,
1334 * * `'navigation'` for keystrokes related to navigation in the UI and the content.
1335 * * unless specified otherwise, new keystrokes are added into the `'contentEditing'` category and the `'common'`
1336 * keystroke group within that category while using the {@link #addKeystrokeInfos} method.
1337 */ this.keystrokeInfos = new Map();
1338 this._editor = editor;
1339 const isMenuBarVisible = editor.config.get('menuBar.isVisible');
1340 const t = editor.locale.t;
1341 this.addKeystrokeInfoCategory({
1342 id: DEFAULT_CATEGORY_ID,
1343 label: t('Content editing keystrokes'),
1344 description: t('These keyboard shortcuts allow for quick access to content editing features.')
1345 });
1346 const navigationKeystrokes = [
1347 {
1348 label: t('Close contextual balloons, dropdowns, and dialogs'),
1349 keystroke: 'Esc'
1350 },
1351 {
1352 label: t('Open the accessibility help dialog'),
1353 keystroke: 'Alt+0'
1354 },
1355 {
1356 label: t('Move focus between form fields (inputs, buttons, etc.)'),
1357 keystroke: [
1358 [
1359 'Tab'
1360 ],
1361 [
1362 'Shift+Tab'
1363 ]
1364 ]
1365 },
1366 {
1367 label: t('Move focus to the toolbar, navigate between toolbars'),
1368 keystroke: 'Alt+F10',
1369 mayRequireFn: true
1370 },
1371 {
1372 label: t('Navigate through the toolbar or menu bar'),
1373 keystroke: [
1374 [
1375 'arrowup'
1376 ],
1377 [
1378 'arrowright'
1379 ],
1380 [
1381 'arrowdown'
1382 ],
1383 [
1384 'arrowleft'
1385 ]
1386 ]
1387 },
1388 {
1389 // eslint-disable-next-line max-len
1390 label: t('Execute the currently focused button. Executing buttons that interact with the editor content moves the focus back to the content.'),
1391 keystroke: [
1392 [
1393 'Enter'
1394 ],
1395 [
1396 'Space'
1397 ]
1398 ]
1399 }
1400 ];
1401 if (isMenuBarVisible) {
1402 navigationKeystrokes.push({
1403 label: t('Move focus to the menu bar, navigate between menu bars'),
1404 keystroke: 'Alt+F9',
1405 mayRequireFn: true
1406 });
1407 }
1408 this.addKeystrokeInfoCategory({
1409 id: 'navigation',
1410 label: t('User interface and content navigation keystrokes'),
1411 description: t('Use the following keystrokes for more efficient navigation in the CKEditor 5 user interface.'),
1412 groups: [
1413 {
1414 id: 'common',
1415 keystrokes: navigationKeystrokes
1416 }
1417 ]
1418 });
1419 }
1420}
1421
1422class Editor extends ObservableMixin() {
1423 /**
1424 * Defines whether the editor is in the read-only mode.
1425 *
1426 * In read-only mode the editor {@link #commands commands} are disabled so it is not possible
1427 * to modify the document by using them. Also, the editable element(s) become non-editable.
1428 *
1429 * In order to make the editor read-only, you need to call the {@link #enableReadOnlyMode} method:
1430 *
1431 * ```ts
1432 * editor.enableReadOnlyMode( 'feature-id' );
1433 * ```
1434 *
1435 * Later, to turn off the read-only mode, call {@link #disableReadOnlyMode}:
1436 *
1437 * ```ts
1438 * editor.disableReadOnlyMode( 'feature-id' );
1439 * ```
1440 *
1441 * @readonly
1442 * @observable
1443 */ get isReadOnly() {
1444 return this._readOnlyLocks.size > 0;
1445 }
1446 set isReadOnly(value) {
1447 /**
1448 * The {@link module:core/editor/editor~Editor#isReadOnly Editor#isReadOnly} property is read-only since version `34.0.0`
1449 * and can be set only using {@link module:core/editor/editor~Editor#enableReadOnlyMode `Editor#enableReadOnlyMode( lockId )`} and
1450 * {@link module:core/editor/editor~Editor#disableReadOnlyMode `Editor#disableReadOnlyMode( lockId )`}.
1451 *
1452 * Usage before version `34.0.0`:
1453 *
1454 * ```ts
1455 * editor.isReadOnly = true;
1456 * editor.isReadOnly = false;
1457 * ```
1458 *
1459 * Usage since version `34.0.0`:
1460 *
1461 * ```ts
1462 * editor.enableReadOnlyMode( 'my-feature-id' );
1463 * editor.disableReadOnlyMode( 'my-feature-id' );
1464 * ```
1465 *
1466 * @error editor-isreadonly-has-no-setter
1467 */ throw new CKEditorError('editor-isreadonly-has-no-setter');
1468 }
1469 /**
1470 * Turns on the read-only mode in the editor.
1471 *
1472 * Editor can be switched to or out of the read-only mode by many features, under various circumstances. The editor supports locking
1473 * 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
1474 * the same time, without conflicting with each other. It guarantees that you will not make the editor editable accidentally (which
1475 * could lead to errors).
1476 *
1477 * Each read-only mode request is identified by a unique id (also called "lock"). If multiple plugins requested to turn on the
1478 * read-only mode, then, the editor will become editable only after all these plugins turn the read-only mode off (using the same ids).
1479 *
1480 * Note, that you cannot force the editor to disable the read-only mode if other plugins set it.
1481 *
1482 * After the first `enableReadOnlyMode()` call, the {@link #isReadOnly `isReadOnly` property} will be set to `true`:
1483 *
1484 * ```ts
1485 * editor.isReadOnly; // `false`.
1486 * editor.enableReadOnlyMode( 'my-feature-id' );
1487 * editor.isReadOnly; // `true`.
1488 * ```
1489 *
1490 * You can turn off the read-only mode ("clear the lock") using the {@link #disableReadOnlyMode `disableReadOnlyMode()`} method:
1491 *
1492 * ```ts
1493 * editor.enableReadOnlyMode( 'my-feature-id' );
1494 * // ...
1495 * editor.disableReadOnlyMode( 'my-feature-id' );
1496 * editor.isReadOnly; // `false`.
1497 * ```
1498 *
1499 * All "locks" need to be removed to enable editing:
1500 *
1501 * ```ts
1502 * editor.enableReadOnlyMode( 'my-feature-id' );
1503 * editor.enableReadOnlyMode( 'my-other-feature-id' );
1504 * // ...
1505 * editor.disableReadOnlyMode( 'my-feature-id' );
1506 * editor.isReadOnly; // `true`.
1507 * editor.disableReadOnlyMode( 'my-other-feature-id' );
1508 * editor.isReadOnly; // `false`.
1509 * ```
1510 *
1511 * @param lockId A unique ID for setting the editor to the read-only state.
1512 */ enableReadOnlyMode(lockId) {
1513 if (typeof lockId !== 'string' && typeof lockId !== 'symbol') {
1514 /**
1515 * The lock ID is missing or it is not a string or symbol.
1516 *
1517 * @error editor-read-only-lock-id-invalid
1518 */ throw new CKEditorError('editor-read-only-lock-id-invalid', null, {
1519 lockId
1520 });
1521 }
1522 if (this._readOnlyLocks.has(lockId)) {
1523 return;
1524 }
1525 this._readOnlyLocks.add(lockId);
1526 if (this._readOnlyLocks.size === 1) {
1527 // Manually fire the `change:isReadOnly` event as only getter is provided.
1528 this.fire('change:isReadOnly', 'isReadOnly', true, false);
1529 }
1530 }
1531 /**
1532 * Removes the read-only lock from the editor with given lock ID.
1533 *
1534 * When no lock is present on the editor anymore, then the {@link #isReadOnly `isReadOnly` property} will be set to `false`.
1535 *
1536 * @param lockId The lock ID for setting the editor to the read-only state.
1537 */ disableReadOnlyMode(lockId) {
1538 if (typeof lockId !== 'string' && typeof lockId !== 'symbol') {
1539 throw new CKEditorError('editor-read-only-lock-id-invalid', null, {
1540 lockId
1541 });
1542 }
1543 if (!this._readOnlyLocks.has(lockId)) {
1544 return;
1545 }
1546 this._readOnlyLocks.delete(lockId);
1547 if (this._readOnlyLocks.size === 0) {
1548 // Manually fire the `change:isReadOnly` event as only getter is provided.
1549 this.fire('change:isReadOnly', 'isReadOnly', false, true);
1550 }
1551 }
1552 /**
1553 * Sets the data in the editor.
1554 *
1555 * ```ts
1556 * editor.setData( '<p>This is editor!</p>' );
1557 * ```
1558 *
1559 * If your editor implementation uses multiple roots, you should pass an object with keys corresponding
1560 * to the editor root names and values equal to the data that should be set in each root:
1561 *
1562 * ```ts
1563 * editor.setData( {
1564 * header: '<p>Content for header part.</p>',
1565 * content: '<p>Content for main part.</p>',
1566 * footer: '<p>Content for footer part.</p>'
1567 * } );
1568 * ```
1569 *
1570 * By default the editor accepts HTML. This can be controlled by injecting a different data processor.
1571 * See the {@glink features/markdown Markdown output} guide for more details.
1572 *
1573 * @param data Input data.
1574 */ setData(data) {
1575 this.data.set(data);
1576 }
1577 /**
1578 * Gets the data from the editor.
1579 *
1580 * ```ts
1581 * editor.getData(); // -> '<p>This is editor!</p>'
1582 * ```
1583 *
1584 * If your editor implementation uses multiple roots, you should pass root name as one of the options:
1585 *
1586 * ```ts
1587 * editor.getData( { rootName: 'header' } ); // -> '<p>Content for header part.</p>'
1588 * ```
1589 *
1590 * By default, the editor outputs HTML. This can be controlled by injecting a different data processor.
1591 * See the {@glink features/markdown Markdown output} guide for more details.
1592 *
1593 * A warning is logged when you try to retrieve data for a detached root, as most probably this is a mistake. A detached root should
1594 * be treated like it is removed, and you should not save its data. Note, that the detached root data is always an empty string.
1595 *
1596 * @param options Additional configuration for the retrieved data.
1597 * Editor features may introduce more configuration options that can be set through this parameter.
1598 * @param options.rootName Root name. Defaults to `'main'`.
1599 * @param options.trim Whether returned data should be trimmed. This option is set to `'empty'` by default,
1600 * which means that whenever editor content is considered empty, an empty string is returned. To turn off trimming
1601 * use `'none'`. In such cases exact content will be returned (for example `'<p>&nbsp;</p>'` for an empty editor).
1602 * @returns Output data.
1603 */ getData(options) {
1604 return this.data.get(options);
1605 }
1606 /**
1607 * Loads and initializes plugins specified in the configuration.
1608 *
1609 * @returns A promise which resolves once the initialization is completed, providing an array of loaded plugins.
1610 */ initPlugins() {
1611 const config = this.config;
1612 const plugins = config.get('plugins');
1613 const removePlugins = config.get('removePlugins') || [];
1614 const extraPlugins = config.get('extraPlugins') || [];
1615 const substitutePlugins = config.get('substitutePlugins') || [];
1616 return this.plugins.init(plugins.concat(extraPlugins), removePlugins, substitutePlugins);
1617 }
1618 /**
1619 * Destroys the editor instance, releasing all resources used by it.
1620 *
1621 * **Note** The editor cannot be destroyed during the initialization phase so if it is called
1622 * while the editor {@link #state is being initialized}, it will wait for the editor initialization before destroying it.
1623 *
1624 * @fires destroy
1625 * @returns A promise that resolves once the editor instance is fully destroyed.
1626 */ destroy() {
1627 let readyPromise = Promise.resolve();
1628 if (this.state == 'initializing') {
1629 readyPromise = new Promise((resolve)=>this.once('ready', resolve));
1630 }
1631 return readyPromise.then(()=>{
1632 this.fire('destroy');
1633 this.stopListening();
1634 this.commands.destroy();
1635 }).then(()=>this.plugins.destroy()).then(()=>{
1636 this.model.destroy();
1637 this.data.destroy();
1638 this.editing.destroy();
1639 this.keystrokes.destroy();
1640 })// Remove the editor from the context.
1641 // When the context was created by this editor, the context will be destroyed.
1642 .then(()=>this._context._removeEditor(this));
1643 }
1644 /**
1645 * Executes the specified command with given parameters.
1646 *
1647 * Shorthand for:
1648 *
1649 * ```ts
1650 * editor.commands.get( commandName ).execute( ... );
1651 * ```
1652 *
1653 * @param commandName The name of the command to execute.
1654 * @param commandParams Command parameters.
1655 * @returns The value returned by the {@link module:core/commandcollection~CommandCollection#execute `commands.execute()`}.
1656 */ execute(commandName, ...commandParams) {
1657 try {
1658 return this.commands.execute(commandName, ...commandParams);
1659 } catch (err) {
1660 // @if CK_DEBUG // throw err;
1661 /* istanbul ignore next -- @preserve */ CKEditorError.rethrowUnexpectedError(err, this);
1662 }
1663 }
1664 /**
1665 * Focuses the editor.
1666 *
1667 * **Note** To explicitly focus the editing area of the editor, use the
1668 * {@link module:engine/view/view~View#focus `editor.editing.view.focus()`} method of the editing view.
1669 *
1670 * Check out the {@glink framework/deep-dive/ui/focus-tracking#focus-in-the-editor-ui Focus in the editor UI} section
1671 * of the {@glink framework/deep-dive/ui/focus-tracking Deep dive into focus tracking} guide to learn more.
1672 */ focus() {
1673 this.editing.view.focus();
1674 }
1675 /* istanbul ignore next -- @preserve */ /**
1676 * Creates and initializes a new editor instance.
1677 *
1678 * This is an abstract method. Every editor type needs to implement its own initialization logic.
1679 *
1680 * See the `create()` methods of the existing editor types to learn how to use them:
1681 *
1682 * * {@link module:editor-classic/classiceditor~ClassicEditor.create `ClassicEditor.create()`}
1683 * * {@link module:editor-balloon/ballooneditor~BalloonEditor.create `BalloonEditor.create()`}
1684 * * {@link module:editor-decoupled/decouplededitor~DecoupledEditor.create `DecoupledEditor.create()`}
1685 * * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`}
1686 */ static create(...args) {
1687 throw new Error('This is an abstract method.');
1688 }
1689 /**
1690 * Creates a new instance of the editor class.
1691 *
1692 * Usually, not to be used directly. See the static {@link module:core/editor/editor~Editor.create `create()`} method.
1693 *
1694 * @param config The editor configuration.
1695 */ constructor(config = {}){
1696 super();
1697 const constructor = this.constructor;
1698 // We don't pass translations to the config, because its behavior of splitting keys
1699 // with dots (e.g. `resize.width` => `resize: { width }`) breaks the translations.
1700 const { translations: defaultTranslations, ...defaultConfig } = constructor.defaultConfig || {};
1701 const { translations = defaultTranslations, ...rest } = config;
1702 // Prefer the language passed as the argument to the constructor instead of the constructor's `defaultConfig`, if both are set.
1703 const language = config.language || defaultConfig.language;
1704 this._context = config.context || new Context({
1705 language,
1706 translations
1707 });
1708 this._context._addEditor(this, !config.context);
1709 // Clone the plugins to make sure that the plugin array will not be shared
1710 // between editors and make the watchdog feature work correctly.
1711 const availablePlugins = Array.from(constructor.builtinPlugins || []);
1712 this.config = new Config(rest, defaultConfig);
1713 this.config.define('plugins', availablePlugins);
1714 this.config.define(this._context._getEditorConfig());
1715 this.plugins = new PluginCollection(this, availablePlugins, this._context.plugins);
1716 this.locale = this._context.locale;
1717 this.t = this.locale.t;
1718 this._readOnlyLocks = new Set();
1719 this.commands = new CommandCollection();
1720 this.set('state', 'initializing');
1721 this.once('ready', ()=>this.state = 'ready', {
1722 priority: 'high'
1723 });
1724 this.once('destroy', ()=>this.state = 'destroyed', {
1725 priority: 'high'
1726 });
1727 this.model = new Model();
1728 this.on('change:isReadOnly', ()=>{
1729 this.model.document.isReadOnly = this.isReadOnly;
1730 });
1731 const stylesProcessor = new StylesProcessor();
1732 this.data = new DataController(this.model, stylesProcessor);
1733 this.editing = new EditingController(this.model, stylesProcessor);
1734 this.editing.view.document.bind('isReadOnly').to(this);
1735 this.conversion = new Conversion([
1736 this.editing.downcastDispatcher,
1737 this.data.downcastDispatcher
1738 ], this.data.upcastDispatcher);
1739 this.conversion.addAlias('dataDowncast', this.data.downcastDispatcher);
1740 this.conversion.addAlias('editingDowncast', this.editing.downcastDispatcher);
1741 this.keystrokes = new EditingKeystrokeHandler(this);
1742 this.keystrokes.listenTo(this.editing.view.document);
1743 this.accessibility = new Accessibility(this);
1744 }
1745} /**
1746 * This error is thrown when trying to pass a `<textarea>` element to a `create()` function of an editor class.
1747 *
1748 * The only editor type which can be initialized on `<textarea>` elements is
1749 * the {@glink installation/getting-started/predefined-builds#classic-editor classic editor}.
1750 * 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
1751 * editable element and therefore `<textarea>` is not appropriate for them. Use a `<div>` or another text container instead:
1752 *
1753 * ```html
1754 * <div id="editor">
1755 * <p>Initial content.</p>
1756 * </div>
1757 * ```
1758 *
1759 * @error editor-wrong-element
1760 */
1761
1762/**
1763 * Checks if the editor is initialized on a `<textarea>` element that belongs to a form. If yes, it updates the editor's element
1764 * content before submitting the form.
1765 *
1766 * This helper requires the {@link module:core/editor/utils/elementapimixin~ElementApi ElementApi interface}.
1767 *
1768 * @param editor Editor instance.
1769 */ function attachToForm(editor) {
1770 if (!isFunction(editor.updateSourceElement)) {
1771 /**
1772 * The editor passed to `attachToForm()` must implement the
1773 * {@link module:core/editor/utils/elementapimixin~ElementApi} interface.
1774 *
1775 * @error attachtoform-missing-elementapi-interface
1776 */ throw new CKEditorError('attachtoform-missing-elementapi-interface', editor);
1777 }
1778 const sourceElement = editor.sourceElement;
1779 // Only when replacing a textarea which is inside of a form element.
1780 if (isTextArea(sourceElement) && sourceElement.form) {
1781 let originalSubmit;
1782 const form = sourceElement.form;
1783 const onSubmit = ()=>editor.updateSourceElement();
1784 // Replace the original form#submit() to call a custom submit function first.
1785 // Check if #submit is a function because the form might have an input named "submit".
1786 if (isFunction(form.submit)) {
1787 originalSubmit = form.submit;
1788 form.submit = ()=>{
1789 onSubmit();
1790 originalSubmit.apply(form);
1791 };
1792 }
1793 // Update the replaced textarea with data before each form#submit event.
1794 form.addEventListener('submit', onSubmit);
1795 // Remove the submit listener and revert the original submit method on
1796 // editor#destroy.
1797 editor.on('destroy', ()=>{
1798 form.removeEventListener('submit', onSubmit);
1799 if (originalSubmit) {
1800 form.submit = originalSubmit;
1801 }
1802 });
1803 }
1804}
1805function isTextArea(sourceElement) {
1806 return !!sourceElement && sourceElement.tagName.toLowerCase() === 'textarea';
1807}
1808
1809/**
1810 * @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
1811 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
1812 */ /**
1813 * Implementation of the {@link module:core/editor/utils/dataapimixin~DataApi}.
1814 *
1815 * @deprecated This functionality is already implemented by the `Editor` class.
1816 */ function DataApiMixin(base) {
1817 return base;
1818}
1819
1820/**
1821 * Implementation of the {@link module:core/editor/utils/elementapimixin~ElementApi}.
1822 */ function ElementApiMixin(base) {
1823 class Mixin extends base {
1824 updateSourceElement(data) {
1825 if (!this.sourceElement) {
1826 /**
1827 * Cannot update the source element of a detached editor.
1828 *
1829 * The {@link module:core/editor/utils/elementapimixin~ElementApi#updateSourceElement `updateSourceElement()`}
1830 * method cannot be called if you did not pass an element to `Editor.create()`.
1831 *
1832 * @error editor-missing-sourceelement
1833 */ throw new CKEditorError('editor-missing-sourceelement', this);
1834 }
1835 const shouldUpdateSourceElement = this.config.get('updateSourceElementOnDestroy');
1836 const isSourceElementTextArea = this.sourceElement instanceof HTMLTextAreaElement;
1837 // The data returned by the editor might be unsafe, so we want to prevent rendering
1838 // unsafe content inside the source element different than <textarea>, which is considered
1839 // secure. This behavior could be changed by setting the `updateSourceElementOnDestroy`
1840 // configuration option to `true`.
1841 if (!shouldUpdateSourceElement && !isSourceElementTextArea) {
1842 setDataInElement(this.sourceElement, '');
1843 return;
1844 }
1845 const dataToSet = typeof data === 'string' ? data : this.data.get();
1846 setDataInElement(this.sourceElement, dataToSet);
1847 }
1848 }
1849 return Mixin;
1850}
1851// Backward compatibility with `mix`.
1852ElementApiMixin.updateSourceElement = ElementApiMixin(Object).prototype.updateSourceElement;
1853
1854/**
1855 * Marks the source element on which the editor was initialized. This prevents other editor instances from using this element.
1856 *
1857 * Running multiple editor instances on the same source element causes various issues and it is
1858 * crucial this helper is called as soon as the source element is known to prevent collisions.
1859 *
1860 * @param editor Editor instance.
1861 * @param sourceElement Element to bind with the editor instance.
1862 */ function secureSourceElement(editor, sourceElement) {
1863 if (sourceElement.ckeditorInstance) {
1864 /**
1865 * A DOM element used to create the editor (e.g.
1866 * {@link module:editor-inline/inlineeditor~InlineEditor.create `InlineEditor.create()`})
1867 * has already been used to create another editor instance. Make sure each editor is
1868 * created with an unique DOM element.
1869 *
1870 * @error editor-source-element-already-used
1871 * @param element DOM element that caused the collision.
1872 */ throw new CKEditorError('editor-source-element-already-used', editor);
1873 }
1874 sourceElement.ckeditorInstance = editor;
1875 editor.once('destroy', ()=>{
1876 delete sourceElement.ckeditorInstance;
1877 });
1878}
1879
1880class PendingActions extends ContextPlugin {
1881 /**
1882 * @inheritDoc
1883 */ static get pluginName() {
1884 return 'PendingActions';
1885 }
1886 /**
1887 * @inheritDoc
1888 */ init() {
1889 this.set('hasAny', false);
1890 this._actions = new Collection({
1891 idProperty: '_id'
1892 });
1893 this._actions.delegate('add', 'remove').to(this);
1894 }
1895 /**
1896 * Adds an action to the list of pending actions.
1897 *
1898 * This method returns an action object with an observable message property.
1899 * The action object can be later used in the {@link #remove} method. It also allows you to change the message.
1900 *
1901 * @param message The action message.
1902 * @returns An observable object that represents a pending action.
1903 */ add(message) {
1904 if (typeof message !== 'string') {
1905 /**
1906 * The message must be a string.
1907 *
1908 * @error pendingactions-add-invalid-message
1909 */ throw new CKEditorError('pendingactions-add-invalid-message', this);
1910 }
1911 const action = new (ObservableMixin())();
1912 action.set('message', message);
1913 this._actions.add(action);
1914 this.hasAny = true;
1915 return action;
1916 }
1917 /**
1918 * Removes an action from the list of pending actions.
1919 *
1920 * @param action An action object.
1921 */ remove(action) {
1922 this._actions.remove(action);
1923 this.hasAny = !!this._actions.length;
1924 }
1925 /**
1926 * Returns the first action from the list or null if the list is empty
1927 *
1928 * @returns The pending action object.
1929 */ get first() {
1930 return this._actions.get(0);
1931 }
1932 /**
1933 * Iterable interface.
1934 */ [Symbol.iterator]() {
1935 return this._actions[Symbol.iterator]();
1936 }
1937}
1938
1939var 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>";
1940
1941var 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>";
1942
1943var 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>";
1944
1945var 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>";
1946
1947var colorPalette = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M10.209 18.717A8.5 8.5 0 1 1 18.686 9.6h-.008l.002.12a3 3 0 0 1-2.866 2.997h-.268l-.046-.002v.002h-4.791a2 2 0 1 0 0 4 1 1 0 1 1-.128 1.992 8.665 8.665 0 0 1-.372.008Zm-3.918-7.01a1.25 1.25 0 1 0-2.415-.648 1.25 1.25 0 0 0 2.415.647ZM5.723 8.18a1.25 1.25 0 1 0 .647-2.414 1.25 1.25 0 0 0-.647 2.414ZM9.76 6.155a1.25 1.25 0 1 0 .647-2.415 1.25 1.25 0 0 0-.647 2.415Zm4.028 1.759a1.25 1.25 0 1 0 .647-2.415 1.25 1.25 0 0 0-.647 2.415Z\"/></svg>";
1948
1949var 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>";
1950
1951var history = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M11 1a9 9 0 1 1-8.027 13.075l1.128-1.129A7.502 7.502 0 0 0 18.5 10a7.5 7.5 0 1 0-14.962.759l-.745-.746-.76.76A9 9 0 0 1 11 1z\"/><path d=\"M.475 8.17a.75.75 0 0 1 .978.047l.075.082 1.284 1.643 1.681-1.284a.75.75 0 0 1 .978.057l.073.083a.75.75 0 0 1-.057.978l-.083.073-2.27 1.737a.75.75 0 0 1-.973-.052l-.074-.082-1.741-2.23a.75.75 0 0 1 .13-1.052z\"/><path d=\"M11.5 5v4.999l3.196 3.196-1.06 1.06L10.1 10.72l-.1-.113V5z\"/></svg>";
1952
1953var lowVision = "<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>";
1954
1955var textAlternative = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3.035 1C2.446 1 2 1.54 2 2.098V10.5h1.5v-8h13v8H18V2.098C18 1.539 17.48 1 16.9 1H3.035Zm10.453 2.61a1.885 1.885 0 0 0-1.442.736 1.89 1.89 0 0 0 1.011 2.976 1.903 1.903 0 0 0 2.253-1.114 1.887 1.887 0 0 0-1.822-2.598ZM7.463 8.163a.611.611 0 0 0-.432.154L5.071 10.5h5.119L7.88 8.348a.628.628 0 0 0-.417-.185Zm6.236 1.059a.62.62 0 0 0-.42.164L12.07 10.5h2.969l-.92-1.113a.618.618 0 0 0-.42-.165ZM.91 11.5a.91.91 0 0 0-.91.912v6.877c0 .505.405.91.91.91h18.178a.91.91 0 0 0 .912-.91v-6.877a.908.908 0 0 0-.912-.912H.91ZM3.668 13h1.947l2.135 5.7H5.898l-.28-.946H3.601l-.278.945H1.516L3.668 13Zm4.947 0h1.801v4.3h2.7v1.4h-4.5V13h-.001Zm4.5 0h5.4v1.4h-1.798v4.3h-1.701v-4.3h-1.9V13h-.001Zm-8.517 1.457-.614 2.059h1.262l-.648-2.059Z\"/></svg>";
1956
1957var loupe = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.68 13.74h-.001l4.209 4.208a1 1 0 1 0 1.414-1.414l-4.267-4.268a6 6 0 1 0-1.355 1.474ZM13 9a4 4 0 1 1-8 0 4 4 0 0 1 8 0Z\"/></svg>";
1958
1959var previousArrow = "<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>";
1960
1961var nextArrow = "<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>";
1962
1963var image = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M6.66 9.118a.693.693 0 0 1 .956.032l3.65 3.411 2.422-2.238a.695.695 0 0 1 .945 0L17.5 13.6V2.5h-15v11.1l4.16-4.482ZM17.8 1c.652 0 1.2.47 1.2 1.1v14.362c0 .64-.532 1.038-1.184 1.038H2.184C1.532 17.5 1 17.103 1 16.462V2.1C1 1.47 1.537 1 2.2 1h15.6Zm-5.655 6a2.128 2.128 0 0 1 .157-2.364A2.133 2.133 0 1 1 12.145 7Z\"/></svg>";
1964
1965var imageUpload = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M1.201 1C.538 1 0 1.47 0 2.1v14.363c0 .64.534 1.037 1.186 1.037h9.494a2.97 2.97 0 0 1-.414-.287 2.998 2.998 0 0 1-1.055-2.03 3.003 3.003 0 0 1 .693-2.185l.383-.455-.02.018-3.65-3.41a.695.695 0 0 0-.957-.034L1.5 13.6V2.5h15v5.535a2.97 2.97 0 0 1 1.412.932l.088.105V2.1c0-.63-.547-1.1-1.2-1.1H1.202Zm11.713 2.803a2.146 2.146 0 0 0-2.049 1.992 2.14 2.14 0 0 0 1.28 2.096 2.13 2.13 0 0 0 2.644-3.11 2.134 2.134 0 0 0-1.875-.978Z\"/><path d=\"M15.522 19.1a.79.79 0 0 0 .79-.79v-5.373l2.059 2.455a.79.79 0 1 0 1.211-1.015l-3.352-3.995a.79.79 0 0 0-.995-.179.784.784 0 0 0-.299.221l-3.35 3.99a.79.79 0 1 0 1.21 1.017l1.936-2.306v5.185c0 .436.353.79.79.79Z\"/><path d=\"M15.522 19.1a.79.79 0 0 0 .79-.79v-5.373l2.059 2.455a.79.79 0 1 0 1.211-1.015l-3.352-3.995a.79.79 0 0 0-.995-.179.784.784 0 0 0-.299.221l-3.35 3.99a.79.79 0 1 0 1.21 1.017l1.936-2.306v5.185c0 .436.353.79.79.79Z\"/></svg>";
1966
1967var imageAssetManager = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M1.201 1c-.662 0-1.2.47-1.2 1.1v14.248c0 .64.533 1.152 1.185 1.152h6.623v-7.236L6.617 9.15a.694.694 0 0 0-.957-.033L1.602 13.55V2.553l14.798.003V9.7H18V2.1c0-.63-.547-1.1-1.2-1.1H1.202Zm11.723 2.805a2.094 2.094 0 0 0-1.621.832 2.127 2.127 0 0 0 1.136 3.357 2.13 2.13 0 0 0 2.611-1.506 2.133 2.133 0 0 0-.76-2.244 2.13 2.13 0 0 0-1.366-.44Z\"/><path clip-rule=\"evenodd\" d=\"M19.898 12.369v6.187a.844.844 0 0 1-.844.844h-8.719a.844.844 0 0 1-.843-.844v-7.312a.844.844 0 0 1 .843-.844h2.531a.843.843 0 0 1 .597.248l.838.852h4.75c.223 0 .441.114.6.272a.844.844 0 0 1 .247.597Zm-1.52.654-4.377.02-1.1-1.143H11v6h7.4l-.023-4.877Z\"/></svg>";
1968
1969var imageUrl = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M1.201 1C.538 1 0 1.47 0 2.1v14.363c0 .64.534 1.037 1.186 1.037h7.029a5.401 5.401 0 0 1 .615-4.338l.762-1.232-2.975-2.78a.696.696 0 0 0-.957-.033L1.5 13.6V2.5h15v6.023c.449.131.887.32 1.307.573l.058.033c.046.028.09.057.135.086V2.1c0-.63-.547-1.1-1.2-1.1H1.202Zm11.713 2.803a2.15 2.15 0 0 0-1.611.834 2.118 2.118 0 0 0-.438 1.158 2.14 2.14 0 0 0 1.277 2.096 2.132 2.132 0 0 0 2.645-3.11 2.13 2.13 0 0 0-1.873-.978Z\"/><path d=\"M16.63 10.294a3.003 3.003 0 0 0-4.142.887l-.117.177a.647.647 0 0 0-.096.492.664.664 0 0 0 .278.418.7.7 0 0 0 .944-.234 1.741 1.741 0 0 1 2.478-.463 1.869 1.869 0 0 1 .476 2.55.637.637 0 0 0-.071.5.646.646 0 0 0 .309.396.627.627 0 0 0 .869-.19l.027-.041a3.226 3.226 0 0 0-.956-4.492Zm-6.061 3.78-.044.066a3.228 3.228 0 0 0 .82 4.403 3.005 3.005 0 0 0 4.275-.798l.13-.197a.626.626 0 0 0 .092-.475.638.638 0 0 0-.268-.402.713.713 0 0 0-.99.26l-.018.029a1.741 1.741 0 0 1-2.477.461 1.87 1.87 0 0 1-.475-2.55l.029-.047a.647.647 0 0 0 .086-.485.66.66 0 0 0-.275-.408l-.04-.027a.609.609 0 0 0-.845.17Z\"/><path d=\"M15.312 13.925c.24-.36.154-.838-.19-1.067-.346-.23-.82-.124-1.059.236l-1.268 1.907c-.239.36-.153.838.192 1.067.345.23.818.123 1.057-.236l1.268-1.907Z\"/></svg>";
1970
1971var alignBottom = "<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>";
1972
1973var alignMiddle = "<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>";
1974
1975var alignTop = "<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>";
1976
1977var alignLeft = "<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>";
1978
1979var alignCenter = "<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>";
1980
1981var alignRight = "<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>";
1982
1983var alignJustify = "<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>";
1984
1985var objectBlockLeft = "<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>";
1986
1987var objectCenter = "<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>";
1988
1989var objectBlockRight = "<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>";
1990
1991var objectFullWidth = "<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>";
1992
1993var objectInline = "<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>";
1994
1995var objectLeft = "<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>";
1996
1997var objectRight = "<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>";
1998
1999var objectSizeFull = "<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>";
2000
2001var objectSizeCustom = "<svg xmlns=\"http://www.w3.org/2000/svg\" xmlns:v=\"https://vecta.io/nano\" viewBox=\"0 0 20 20\"><path d=\"M.95 1.43a.95.95 0 0 0-.95.95v3.1a.95.95 0 0 0 .95.95h.75v6.3H.95a.95.95 0 0 0-.95.95v3.1a.95.95 0 0 0 .95.95h3.1a.95.95 0 0 0 .95-.95v-.65h1.932l1.539-1.5H5v-.95a.95.95 0 0 0-.95-.95H3.2v-6.3h.85A.95.95 0 0 0 5 5.48v-.55h10v.55a.95.95 0 0 0 .95.95h3.1a.95.95 0 0 0 .95-.95v-3.1a.95.95 0 0 0-.95-.95h-3.1a.95.95 0 0 0-.95.95v1.05H5V2.38a.95.95 0 0 0-.95-.95H.95zm.55 3.5v-2h2v2h-2zm0 9.3v2h2v-2h-2zm15-11.3v2h2v-2h-2z\"/><path d=\"M8.139 20.004v-2.388l7.045-7.048 2.391 2.391-7.046 7.046h-2.39zm11.421-9.101a.64.64 0 0 1-.138.206l-1.165 1.168-2.391-2.391 1.167-1.163a.63.63 0 0 1 .206-.138.635.635 0 0 1 .243-.049.63.63 0 0 1 .449.187l1.491 1.488c.059.059.108.129.138.206s.049.16.049.243a.6.6 0 0 1-.049.243z\"/></svg>";
2002
2003var objectSizeLarge = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2.5 16.5v1h-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 15v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 13v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 11v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 9v1H0V9h1Zm19 0v1h-1V9h1ZM1 7v1H0V7h1Zm19 0v1h-1V7h1ZM1 5v1H0V5h1Zm19 0v1h-1V5h1Zm0-2v1h-1V3h1ZM1 3v1H0V3h1Zm13.5-1.5v1h-1v-1h1Zm2 0v1h-1v-1h1Zm2 0v1h-1v-1h1Zm-8 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm8 0v1h-1v-1h1Zm-10 0v1h-1v-1h1Z\"/><path d=\"M13 5.5H2a2 2 0 0 0-2 2v8a2 2 0 0 0 2 2h11a2 2 0 0 0 2-2v-8a2 2 0 0 0-2-2ZM13 7a.5.5 0 0 1 .5.5v8a.5.5 0 0 1-.5.5H2a.5.5 0 0 1-.5-.5v-8A.5.5 0 0 1 2 7h11Z\"/></svg>";
2004
2005var objectSizeSmall = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2.5 16.5v1h-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 15v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 13v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 11v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 9v1H0V9h1Zm19 0v1h-1V9h1ZM1 7v1H0V7h1Zm19 0v1h-1V7h1ZM1 5v1H0V5h1Zm19 0v1h-1V5h1Zm0-2v1h-1V3h1ZM1 3v1H0V3h1Zm13.5-1.5v1h-1v-1h1Zm2 0v1h-1v-1h1Zm2 0v1h-1v-1h1Zm-8 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm8 0v1h-1v-1h1Zm-10 0v1h-1v-1h1Z\"/><path d=\"M7 9.5H2a2 2 0 0 0-2 2v4a2 2 0 0 0 2 2h5a2 2 0 0 0 2-2v-4a2 2 0 0 0-2-2ZM7 11a.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 2 11h5Z\"/></svg>";
2006
2007var objectSizeMedium = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2.5 16.5v1h-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 15v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 13v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 11v1H0v-1h1Zm19 0v1h-1v-1h1ZM1 9v1H0V9h1Zm19 0v1h-1V9h1ZM1 7v1H0V7h1Zm19 0v1h-1V7h1ZM1 5v1H0V5h1Zm19 0v1h-1V5h1Zm0-2v1h-1V3h1ZM1 3v1H0V3h1Zm13.5-1.5v1h-1v-1h1Zm2 0v1h-1v-1h1Zm2 0v1h-1v-1h1Zm-8 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm-2 0v1h-1v-1h1Zm8 0v1h-1v-1h1Zm-10 0v1h-1v-1h1Z\"/><path d=\"M10 7.5H2a2 2 0 0 0-2 2v6a2 2 0 0 0 2 2h8a2 2 0 0 0 2-2v-6a2 2 0 0 0-2-2ZM10 9a.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 2 9h8Z\"/></svg>";
2008
2009var 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>";
2010
2011var 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>";
2012
2013var 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>";
2014
2015var threeVerticalDots = "<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>";
2016
2017var dragIndicator = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M5 3.25a1.5 1.5 0 1 0 3 0 1.5 1.5 0 1 0-3 0\"/><path d=\"M12 3.25a1.5 1.5 0 1 0 3 0 1.5 1.5 0 1 0-3 0\"/><path d=\"M5 10a1.5 1.5 0 1 0 3 0 1.5 1.5 0 1 0-3 0\"/><path d=\"M12 10a1.5 1.5 0 1 0 3 0 1.5 1.5 0 1 0-3 0\"/><path d=\"M5 16.75a1.5 1.5 0 1 0 3 0 1.5 1.5 0 1 0-3 0\"/><path d=\"M12 16.75a1.5 1.5 0 1 0 3 0 1.5 1.5 0 1 0-3 0\"/></svg>";
2018
2019var 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>";
2020
2021var 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>";
2022
2023var 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>";
2024
2025var text = "<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 13Z\"/><path 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\"/></svg>";
2026
2027var importExport = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path 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\"/></svg>";
2028
2029var 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>";
2030
2031var 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>";
2032
2033var 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>";
2034
2035var 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>";
2036
2037var todoList = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"m2.315 14.705 2.224-2.24a.689.689 0 0 1 .963 0 .664.664 0 0 1 0 .949L2.865 16.07a.682.682 0 0 1-.112.089.647.647 0 0 1-.852-.051L.688 14.886a.635.635 0 0 1 0-.903.647.647 0 0 1 .91 0l.717.722zm5.185.045a.75.75 0 0 1 .75-.75h9.5a.75.75 0 1 1 0 1.5h-9.5a.75.75 0 0 1-.75-.75zM2.329 5.745l2.21-2.226a.689.689 0 0 1 .963 0 .664.664 0 0 1 0 .95L2.865 7.125a.685.685 0 0 1-.496.196.644.644 0 0 1-.468-.187L.688 5.912a.635.635 0 0 1 0-.903.647.647 0 0 1 .91 0l.73.736zM7.5 5.75A.75.75 0 0 1 8.25 5h9.5a.75.75 0 1 1 0 1.5h-9.5a.75.75 0 0 1-.75-.75z\"/></svg>";
2038
2039var codeBlock = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M12.87 12.61a.75.75 0 0 1-.089.976l-.085.07-3.154 2.254 3.412 2.414a.75.75 0 0 1 .237.95l-.057.095a.75.75 0 0 1-.95.237l-.096-.058-4.272-3.022-.003-1.223 4.01-2.867a.75.75 0 0 1 1.047.174zm2.795-.231.095.057 4.011 2.867-.003 1.223-4.272 3.022-.095.058a.75.75 0 0 1-.88-.151l-.07-.086-.058-.095a.75.75 0 0 1 .15-.88l.087-.07 3.412-2.414-3.154-2.253-.085-.071a.75.75 0 0 1 .862-1.207zM16 0a2 2 0 0 1 2 2v9.354l-.663-.492-.837-.001V2a.5.5 0 0 0-.5-.5H2a.5.5 0 0 0-.5.5v15a.5.5 0 0 0 .5.5h3.118L7.156 19H2a2 2 0 0 1-2-2V2a2 2 0 0 1 2-2h14zM5.009 15l.003 1H3v-1h2.009zm2.188-2-1.471 1H5v-1h2.197zM10 11v.095L8.668 12H7v-1h3zm4-2v1H7V9h7zm0-2v1H7V7h7zm-4-2v1H5V5h5zM6 3v1H3V3h3z\"/></svg>";
2040
2041var browseFiles = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M11.627 16.5zm5.873-.196zm0-7.001V8h-13v8.5h4.341c.191.54.457 1.044.785 1.5H2a1.5 1.5 0 0 1-1.5-1.5v-13A1.5 1.5 0 0 1 2 2h4.5a1.5 1.5 0 0 1 1.06.44L9.122 4H16a1.5 1.5 0 0 1 1.5 1.5v1A1.5 1.5 0 0 1 19 8v2.531a6.027 6.027 0 0 0-1.5-1.228zM16 6.5v-1H8.5l-2-2H2v13h1V8a1.5 1.5 0 0 1 1.5-1.5H16z\"/><path d=\"M14.5 19.5a5 5 0 1 1 0-10 5 5 0 0 1 0 10zM15 14v-2h-1v2h-2v1h2v2h1v-2h2v-1h-2z\"/></svg>";
2042
2043var heading1 = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M19 9v10h-2v-8h-2V9h4zM4 8.5h5V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v11.5a1 1 0 0 1-1 1H10a1 1 0 0 1-1-1V11H4v4.5a1 1 0 0 1-1 1h-.5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1H3a1 1 0 0 1 1 1v4.5z\"/></svg>";
2044
2045var heading2 = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3 8.5h5V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v11.5a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V11H3v4.5a1 1 0 0 1-1 1h-.5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1H2a1 1 0 0 1 1 1v4.5zm16.076 8.343V18.5h-6.252c.067-.626.27-1.22.61-1.78.338-.561 1.006-1.305 2.005-2.232.804-.749 1.297-1.257 1.479-1.523.245-.368.368-.732.368-1.092 0-.398-.107-.703-.32-.917-.214-.214-.51-.32-.886-.32-.372 0-.669.111-.889.336-.22.224-.347.596-.38 1.117l-1.778-.178c.106-.982.438-1.686.997-2.114.558-.427 1.257-.64 2.095-.64.918 0 1.64.247 2.164.742.525.495.787 1.11.787 1.847 0 .419-.075.818-.225 1.197-.15.378-.388.775-.714 1.19-.216.275-.605.67-1.168 1.187-.563.516-.92.859-1.07 1.028a3.11 3.11 0 0 0-.365.495h3.542z\"/></svg>";
2046
2047var heading3 = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3 8.5h5V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v11.5a1 1 0 0 1-1 1H9a1 1 0 0 1-1-1V11H3v4.5a1 1 0 0 1-1 1h-.5a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1H2a1 1 0 0 1 1 1v4.5zm9.989 7.53 1.726-.209c.055.44.203.777.445 1.01.24.232.533.349.876.349.368 0 .678-.14.93-.42.251-.279.377-.655.377-1.13 0-.448-.12-.803-.362-1.066a1.153 1.153 0 0 0-.882-.393c-.228 0-.501.044-.819.133l.197-1.453c.482.012.85-.092 1.105-.315.253-.222.38-.517.38-.885 0-.313-.093-.563-.279-.75-.186-.185-.434-.278-.743-.278a1.07 1.07 0 0 0-.78.317c-.216.212-.347.52-.394.927l-1.644-.28c.114-.562.287-1.012.517-1.348.231-.337.553-.601.965-.794a3.24 3.24 0 0 1 1.387-.289c.876 0 1.579.28 2.108.838.436.457.653.973.653 1.549 0 .817-.446 1.468-1.339 1.955.533.114.96.37 1.28.768.319.398.478.878.478 1.441 0 .817-.298 1.513-.895 2.088-.596.576-1.339.864-2.228.864-.842 0-1.54-.243-2.094-.727-.555-.485-.876-1.118-.965-1.901z\"/></svg>";
2048
2049var heading4 = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3.5 8.5h5V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v11.5a1 1 0 0 1-1 1h-.5a1 1 0 0 1-1-1V11h-5v4.5a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v4.5zm13.55 10v-1.873h-3.81v-1.561l4.037-5.91h1.498v5.904h1.156v1.567h-1.156V18.5H17.05zm0-3.44v-3.18l-2.14 3.18h2.14z\"/></svg>";
2050
2051var heading5 = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3.5 8.5h5V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v11.5a1 1 0 0 1-1 1h-.5a1 1 0 0 1-1-1V11h-5v4.5a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v4.5zm9.578 7.607 1.777-.184c.05.402.201.72.45.955a1.223 1.223 0 0 0 1.81-.101c.258-.303.387-.759.387-1.368 0-.572-.128-1-.384-1.286-.256-.285-.59-.428-1-.428-.512 0-.971.226-1.377.679l-1.448-.21.915-4.843h4.716v1.67H15.56l-.28 1.58a2.697 2.697 0 0 1 1.219-.298 2.68 2.68 0 0 1 2.012.863c.55.576.825 1.323.825 2.241a3.36 3.36 0 0 1-.666 2.05c-.605.821-1.445 1.232-2.52 1.232-.86 0-1.56-.23-2.101-.692-.542-.461-.866-1.081-.971-1.86z\"/></svg>";
2052
2053var heading6 = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3.5 8.5h5V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v11.5a1 1 0 0 1-1 1h-.5a1 1 0 0 1-1-1V11h-5v4.5a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1h.5a1 1 0 0 1 1 1v4.5zm15.595 2.973-1.726.19c-.043-.355-.153-.617-.33-.787-.178-.169-.409-.253-.692-.253-.377 0-.695.169-.956.507-.26.339-.424 1.043-.492 2.114.445-.525.997-.787 1.657-.787.745 0 1.383.284 1.914.85.531.568.797 1.3.797 2.197 0 .952-.28 1.716-.838 2.291-.559.576-1.276.864-2.152.864-.94 0-1.712-.365-2.317-1.095-.605-.73-.908-1.927-.908-3.59 0-1.705.316-2.935.946-3.688.63-.753 1.45-1.13 2.457-1.13.706 0 1.291.198 1.755.594.463.395.758.97.885 1.723zm-4.043 3.891c0 .58.133 1.028.4 1.343.266.315.57.473.914.473.33 0 .605-.13.825-.388.22-.258.33-.68.33-1.27 0-.604-.118-1.047-.355-1.329a1.115 1.115 0 0 0-.89-.422c-.342 0-.632.134-.869.403s-.355.666-.355 1.19z\"/></svg>";
2054
2055var horizontalLine = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M2 9h16v2H2z\"/></svg>";
2056
2057var 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>";
2058
2059var 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>";
2060
2061var 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>";
2062
2063var table = "<svg viewBox=\"0 0 20 20\" xmlns=\"http://www.w3.org/2000/svg\"><path d=\"M3 5.5v3h4v-3H3Zm0 4v3h4v-3H3Zm0 4v3h4v-3H3Zm5 3h4v-3H8v3Zm5 0h4v-3h-4v3Zm4-4v-3h-4v3h4Zm0-4v-3h-4v3h4Zm1.5 8A1.5 1.5 0 0 1 17 18H3a1.5 1.5 0 0 1-1.5-1.5V3c.222-.863 1.068-1.5 2-1.5h13c.932 0 1.778.637 2 1.5v13.5Zm-6.5-4v-3H8v3h4Zm0-4v-3H8v3h4Z\"/></svg>";
2064
2065const icons = {
2066 bold,
2067 cancel,
2068 caption,
2069 check,
2070 cog,
2071 colorPalette,
2072 eraser,
2073 history,
2074 image,
2075 imageUpload,
2076 imageAssetManager,
2077 imageUrl,
2078 lowVision,
2079 textAlternative,
2080 loupe,
2081 previousArrow,
2082 nextArrow,
2083 importExport,
2084 paragraph,
2085 plus,
2086 text,
2087 alignBottom,
2088 alignMiddle,
2089 alignTop,
2090 alignLeft,
2091 alignCenter,
2092 alignRight,
2093 alignJustify,
2094 objectLeft,
2095 objectCenter,
2096 objectRight,
2097 objectFullWidth,
2098 objectInline,
2099 objectBlockLeft,
2100 objectBlockRight,
2101 objectSizeCustom,
2102 objectSizeFull,
2103 objectSizeLarge,
2104 objectSizeSmall,
2105 objectSizeMedium,
2106 pencil,
2107 pilcrow,
2108 quote,
2109 threeVerticalDots,
2110 dragIndicator,
2111 redo,
2112 undo,
2113 bulletedList,
2114 numberedList,
2115 todoList,
2116 codeBlock,
2117 browseFiles,
2118 heading1,
2119 heading2,
2120 heading3,
2121 heading4,
2122 heading5,
2123 heading6,
2124 horizontalLine,
2125 html,
2126 indent,
2127 outdent,
2128 table
2129};
2130
2131export { Command, Context, ContextPlugin, DataApiMixin, Editor, ElementApiMixin, MultiCommand, PendingActions, Plugin, attachToForm, icons, secureSourceElement };
2132//# sourceMappingURL=index.js.map
2133
\No newline at end of file