@ckeditor/ckeditor5-image
Version:
Image feature for CKEditor 5.
81 lines (80 loc) • 3.65 kB
JavaScript
/**
* @license Copyright (c) 2003-2024, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
import { Command } from 'ckeditor5/src/core.js';
/**
* The image type command. It changes the type of a selected image, depending on the configuration.
*/
export default class ImageTypeCommand extends Command {
/**
* @inheritDoc
*
* @param modelElementName Model element name the command converts to.
*/
constructor(editor, modelElementName) {
super(editor);
this._modelElementName = modelElementName;
}
/**
* @inheritDoc
*/
refresh() {
const editor = this.editor;
const imageUtils = editor.plugins.get('ImageUtils');
const element = imageUtils.getClosestSelectedImageElement(this.editor.model.document.selection);
if (this._modelElementName === 'imageBlock') {
this.isEnabled = imageUtils.isInlineImage(element);
}
else {
this.isEnabled = imageUtils.isBlockImage(element);
}
}
/**
* Executes the command and changes the type of a selected image.
*
* @fires execute
* @param options.setImageSizes Specifies whether the image `width` and `height` attributes should be set automatically.
* The default is `true`.
* @returns An object containing references to old and new model image elements
* (for before and after the change) so external integrations can hook into the decorated
* `execute` event and handle this change. `null` if the type change failed.
*/
execute(options = {}) {
const editor = this.editor;
const model = this.editor.model;
const imageUtils = editor.plugins.get('ImageUtils');
const oldElement = imageUtils.getClosestSelectedImageElement(model.document.selection);
const attributes = Object.fromEntries(oldElement.getAttributes());
// Don't change image type if "src" is missing (a broken image), unless there's "uploadId" set.
// This state may happen during image upload (before it finishes) and it should be possible to change type
// of the image in the meantime.
if (!attributes.src && !attributes.uploadId) {
return null;
}
return model.change(writer => {
const { setImageSizes = true } = options;
// Get all markers that contain the old image element.
const markers = Array.from(model.markers)
.filter(marker => marker.getRange().containsItem(oldElement));
const newElement = imageUtils.insertImage(attributes, model.createSelection(oldElement, 'on'), this._modelElementName, { setImageSizes });
if (!newElement) {
return null;
}
const newElementRange = writer.createRangeOn(newElement);
// Expand the previously intersecting markers' ranges to include the new image element.
for (const marker of markers) {
const markerRange = marker.getRange();
// Join the survived part of the old marker range with the new element range
// (loosely because there could be some new paragraph or the existing one might got split).
const range = markerRange.root.rootName != '$graveyard' ?
markerRange.getJoined(newElementRange, true) : newElementRange;
writer.updateMarker(marker, { range });
}
return {
oldElement,
newElement
};
});
}
}