UNPKG

5.08 kBJavaScriptView Raw
1/**
2 * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3 * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4 */
5/**
6 * @module image/imagestyle/imagestyleediting
7 */
8import { Plugin } from 'ckeditor5/src/core';
9import ImageStyleCommand from './imagestylecommand';
10import ImageUtils from '../imageutils';
11import utils from './utils';
12import { viewToModelStyleAttribute, modelToViewStyleAttribute } from './converters';
13/**
14 * The image style engine plugin. It sets the default configuration, creates converters and registers
15 * {@link module:image/imagestyle/imagestylecommand~ImageStyleCommand ImageStyleCommand}.
16 */
17export default class ImageStyleEditing extends Plugin {
18 /**
19 * @inheritDoc
20 */
21 static get pluginName() {
22 return 'ImageStyleEditing';
23 }
24 /**
25 * @inheritDoc
26 */
27 static get requires() {
28 return [ImageUtils];
29 }
30 /**
31 * @inheritDoc
32 */
33 init() {
34 const { normalizeStyles, getDefaultStylesConfiguration } = utils;
35 const editor = this.editor;
36 const isBlockPluginLoaded = editor.plugins.has('ImageBlockEditing');
37 const isInlinePluginLoaded = editor.plugins.has('ImageInlineEditing');
38 editor.config.define('image.styles', getDefaultStylesConfiguration(isBlockPluginLoaded, isInlinePluginLoaded));
39 this.normalizedStyles = normalizeStyles({
40 configuredStyles: editor.config.get('image.styles'),
41 isBlockPluginLoaded,
42 isInlinePluginLoaded
43 });
44 this._setupConversion(isBlockPluginLoaded, isInlinePluginLoaded);
45 this._setupPostFixer();
46 // Register imageStyle command.
47 editor.commands.add('imageStyle', new ImageStyleCommand(editor, this.normalizedStyles));
48 }
49 /**
50 * Sets the editor conversion taking the presence of
51 * {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`}
52 * and {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugins into consideration.
53 */
54 _setupConversion(isBlockPluginLoaded, isInlinePluginLoaded) {
55 const editor = this.editor;
56 const schema = editor.model.schema;
57 const modelToViewConverter = modelToViewStyleAttribute(this.normalizedStyles);
58 const viewToModelConverter = viewToModelStyleAttribute(this.normalizedStyles);
59 editor.editing.downcastDispatcher.on('attribute:imageStyle', modelToViewConverter);
60 editor.data.downcastDispatcher.on('attribute:imageStyle', modelToViewConverter);
61 // Allow imageStyle attribute in image and imageInline.
62 // We could call it 'style' but https://github.com/ckeditor/ckeditor5-engine/issues/559.
63 if (isBlockPluginLoaded) {
64 schema.extend('imageBlock', { allowAttributes: 'imageStyle' });
65 // Converter for figure element from view to model.
66 editor.data.upcastDispatcher.on('element:figure', viewToModelConverter, { priority: 'low' });
67 }
68 if (isInlinePluginLoaded) {
69 schema.extend('imageInline', { allowAttributes: 'imageStyle' });
70 // Converter for the img element from view to model.
71 editor.data.upcastDispatcher.on('element:img', viewToModelConverter, { priority: 'low' });
72 }
73 }
74 /**
75 * Registers a post-fixer that will make sure that the style attribute value is correct for a specific image type (block vs inline).
76 */
77 _setupPostFixer() {
78 const editor = this.editor;
79 const document = editor.model.document;
80 const imageUtils = editor.plugins.get(ImageUtils);
81 const stylesMap = new Map(this.normalizedStyles.map(style => [style.name, style]));
82 // Make sure that style attribute is valid for the image type.
83 document.registerPostFixer(writer => {
84 let changed = false;
85 for (const change of document.differ.getChanges()) {
86 if (change.type == 'insert' || change.type == 'attribute' && change.attributeKey == 'imageStyle') {
87 let element = change.type == 'insert' ? change.position.nodeAfter : change.range.start.nodeAfter;
88 if (element && element.is('element', 'paragraph') && element.childCount > 0) {
89 element = element.getChild(0);
90 }
91 if (!imageUtils.isImage(element)) {
92 continue;
93 }
94 const imageStyle = element.getAttribute('imageStyle');
95 if (!imageStyle) {
96 continue;
97 }
98 const imageStyleDefinition = stylesMap.get(imageStyle);
99 if (!imageStyleDefinition || !imageStyleDefinition.modelElements.includes(element.name)) {
100 writer.removeAttribute('imageStyle', element);
101 changed = true;
102 }
103 }
104 }
105 return changed;
106 });
107 }
108}