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 | */
|
5 | import { first } from 'ckeditor5/src/utils.js';
|
6 | /**
|
7 | * Creates a view element representing the inline image.
|
8 | *
|
9 | * ```html
|
10 | * <span class="image-inline"><img></img></span>
|
11 | * ```
|
12 | *
|
13 | * Note that `alt` and `src` attributes are converted separately, so they are not included.
|
14 | *
|
15 | * @internal
|
16 | */
|
17 | export function createInlineImageViewElement(writer) {
|
18 | return writer.createContainerElement('span', { class: 'image-inline' }, writer.createEmptyElement('img'));
|
19 | }
|
20 | /**
|
21 | * Creates a view element representing the block image.
|
22 | *
|
23 | * ```html
|
24 | * <figure class="image"><img></img></figure>
|
25 | * ```
|
26 | *
|
27 | * Note that `alt` and `src` attributes are converted separately, so they are not included.
|
28 | *
|
29 | * @internal
|
30 | */
|
31 | export function createBlockImageViewElement(writer) {
|
32 | return writer.createContainerElement('figure', { class: 'image' }, [
|
33 | writer.createEmptyElement('img'),
|
34 | writer.createSlot('children')
|
35 | ]);
|
36 | }
|
37 | /**
|
38 | * A function returning a `MatcherPattern` for a particular type of View images.
|
39 | *
|
40 | * @internal
|
41 | * @param matchImageType The type of created image.
|
42 | */
|
43 | export function getImgViewElementMatcher(editor, matchImageType) {
|
44 | const imageUtils = editor.plugins.get('ImageUtils');
|
45 | const areBothImagePluginsLoaded = editor.plugins.has('ImageInlineEditing') && editor.plugins.has('ImageBlockEditing');
|
46 | return element => {
|
47 | // Check if the matched view element is an <img>.
|
48 | if (!imageUtils.isInlineImageView(element)) {
|
49 | return null;
|
50 | }
|
51 | // If just one of the plugins is loaded (block or inline), it will match all kinds of images.
|
52 | if (!areBothImagePluginsLoaded) {
|
53 | return getPositiveMatchPattern(element);
|
54 | }
|
55 | // The <img> can be standalone, wrapped in <figure>...</figure> (ImageBlock plugin) or
|
56 | // wrapped in <figure><a>...</a></figure> (LinkImage plugin).
|
57 | const imageType = element.getStyle('display') == 'block' || element.findAncestor(imageUtils.isBlockImageView) ?
|
58 | 'imageBlock' :
|
59 | 'imageInline';
|
60 | if (imageType !== matchImageType) {
|
61 | return null;
|
62 | }
|
63 | return getPositiveMatchPattern(element);
|
64 | };
|
65 | function getPositiveMatchPattern(element) {
|
66 | const pattern = {
|
67 | name: true
|
68 | };
|
69 | // This will trigger src consumption (See https://github.com/ckeditor/ckeditor5/issues/11530).
|
70 | if (element.hasAttribute('src')) {
|
71 | pattern.attributes = ['src'];
|
72 | }
|
73 | return pattern;
|
74 | }
|
75 | }
|
76 | /**
|
77 | * Considering the current model selection, it returns the name of the model image element
|
78 | * (`'imageBlock'` or `'imageInline'`) that will make most sense from the UX perspective if a new
|
79 | * image was inserted (also: uploaded, dropped, pasted) at that selection.
|
80 | *
|
81 | * The assumption is that inserting images into empty blocks or on other block widgets should
|
82 | * produce block images. Inline images should be inserted in other cases, e.g. in paragraphs
|
83 | * that already contain some text.
|
84 | *
|
85 | * @internal
|
86 | */
|
87 | export function determineImageTypeForInsertionAtSelection(schema, selection) {
|
88 | const firstBlock = first(selection.getSelectedBlocks());
|
89 | // Insert a block image if the selection is not in/on block elements or it's on a block widget.
|
90 | if (!firstBlock || schema.isObject(firstBlock)) {
|
91 | return 'imageBlock';
|
92 | }
|
93 | // A block image should also be inserted into an empty block element
|
94 | // (that is not an empty list item so the list won't get split).
|
95 | if (firstBlock.isEmpty && firstBlock.name != 'listItem') {
|
96 | return 'imageBlock';
|
97 | }
|
98 | // Otherwise insert an inline image.
|
99 | return 'imageInline';
|
100 | }
|
101 | /**
|
102 | * Returns parsed value of the size, but only if it contains unit: px.
|
103 | */
|
104 | export function getSizeValueIfInPx(size) {
|
105 | if (size && size.endsWith('px')) {
|
106 | return parseInt(size);
|
107 | }
|
108 | return null;
|
109 | }
|
110 | /**
|
111 | * Returns true if both styles (width and height) are set.
|
112 | *
|
113 | * If both image styles: width & height are set, they will override the image width & height attributes in the
|
114 | * browser. In this case, the image looks the same as if these styles were applied to attributes instead of styles.
|
115 | * That's why we can upcast these styles to width & height attributes instead of resizedWidth and resizedHeight.
|
116 | */
|
117 | export function widthAndHeightStylesAreBothSet(viewElement) {
|
118 | const widthStyle = getSizeValueIfInPx(viewElement.getStyle('width'));
|
119 | const heightStyle = getSizeValueIfInPx(viewElement.getStyle('height'));
|
120 | return !!(widthStyle && heightStyle);
|
121 | }
|