@ckeditor/ckeditor5-image
Version:
Image feature for CKEditor 5.
109 lines (108 loc) • 5.08 kB
JavaScript
/**
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module image/imagestyle/imagestyleediting
*/
import { Plugin } from 'ckeditor5/src/core';
import ImageStyleCommand from './imagestylecommand';
import ImageUtils from '../imageutils';
import utils from './utils';
import { viewToModelStyleAttribute, modelToViewStyleAttribute } from './converters';
/**
* The image style engine plugin. It sets the default configuration, creates converters and registers
* {@link module:image/imagestyle/imagestylecommand~ImageStyleCommand ImageStyleCommand}.
*/
export default class ImageStyleEditing extends Plugin {
/**
* @inheritDoc
*/
static get pluginName() {
return 'ImageStyleEditing';
}
/**
* @inheritDoc
*/
static get requires() {
return [ImageUtils];
}
/**
* @inheritDoc
*/
init() {
const { normalizeStyles, getDefaultStylesConfiguration } = utils;
const editor = this.editor;
const isBlockPluginLoaded = editor.plugins.has('ImageBlockEditing');
const isInlinePluginLoaded = editor.plugins.has('ImageInlineEditing');
editor.config.define('image.styles', getDefaultStylesConfiguration(isBlockPluginLoaded, isInlinePluginLoaded));
this.normalizedStyles = normalizeStyles({
configuredStyles: editor.config.get('image.styles'),
isBlockPluginLoaded,
isInlinePluginLoaded
});
this._setupConversion(isBlockPluginLoaded, isInlinePluginLoaded);
this._setupPostFixer();
// Register imageStyle command.
editor.commands.add('imageStyle', new ImageStyleCommand(editor, this.normalizedStyles));
}
/**
* Sets the editor conversion taking the presence of
* {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`}
* and {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugins into consideration.
*/
_setupConversion(isBlockPluginLoaded, isInlinePluginLoaded) {
const editor = this.editor;
const schema = editor.model.schema;
const modelToViewConverter = modelToViewStyleAttribute(this.normalizedStyles);
const viewToModelConverter = viewToModelStyleAttribute(this.normalizedStyles);
editor.editing.downcastDispatcher.on('attribute:imageStyle', modelToViewConverter);
editor.data.downcastDispatcher.on('attribute:imageStyle', modelToViewConverter);
// Allow imageStyle attribute in image and imageInline.
// We could call it 'style' but https://github.com/ckeditor/ckeditor5-engine/issues/559.
if (isBlockPluginLoaded) {
schema.extend('imageBlock', { allowAttributes: 'imageStyle' });
// Converter for figure element from view to model.
editor.data.upcastDispatcher.on('element:figure', viewToModelConverter, { priority: 'low' });
}
if (isInlinePluginLoaded) {
schema.extend('imageInline', { allowAttributes: 'imageStyle' });
// Converter for the img element from view to model.
editor.data.upcastDispatcher.on('element:img', viewToModelConverter, { priority: 'low' });
}
}
/**
* Registers a post-fixer that will make sure that the style attribute value is correct for a specific image type (block vs inline).
*/
_setupPostFixer() {
const editor = this.editor;
const document = editor.model.document;
const imageUtils = editor.plugins.get(ImageUtils);
const stylesMap = new Map(this.normalizedStyles.map(style => [style.name, style]));
// Make sure that style attribute is valid for the image type.
document.registerPostFixer(writer => {
let changed = false;
for (const change of document.differ.getChanges()) {
if (change.type == 'insert' || change.type == 'attribute' && change.attributeKey == 'imageStyle') {
let element = change.type == 'insert' ? change.position.nodeAfter : change.range.start.nodeAfter;
if (element && element.is('element', 'paragraph') && element.childCount > 0) {
element = element.getChild(0);
}
if (!imageUtils.isImage(element)) {
continue;
}
const imageStyle = element.getAttribute('imageStyle');
if (!imageStyle) {
continue;
}
const imageStyleDefinition = stylesMap.get(imageStyle);
if (!imageStyleDefinition || !imageStyleDefinition.modelElements.includes(element.name)) {
writer.removeAttribute('imageStyle', element);
changed = true;
}
}
}
return changed;
});
}
}