UNPKG

13.6 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/utils
7 */
8import { icons } from 'ckeditor5/src/core';
9import { logWarning } from 'ckeditor5/src/utils';
10const { objectFullWidth, objectInline, objectLeft, objectRight, objectCenter, objectBlockLeft, objectBlockRight } = icons;
11/**
12 * Default image style options provided by the plugin that can be referred in the {@link module:image/imageconfig~ImageConfig#styles}
13 * configuration.
14 *
15 * There are available 5 styles focused on formatting:
16 *
17 * * **`'alignLeft'`** aligns the inline or block image to the left and wraps it with the text using the `image-style-align-left` class,
18 * * **`'alignRight'`** aligns the inline or block image to the right and wraps it with the text using the `image-style-align-right` class,
19 * * **`'alignCenter'`** centers the block image using the `image-style-align-center` class,
20 * * **`'alignBlockLeft'`** aligns the block image to the left using the `image-style-block-align-left` class,
21 * * **`'alignBlockRight'`** aligns the block image to the right using the `image-style-block-align-right` class,
22 *
23 * and 3 semantic styles:
24 *
25 * * **`'inline'`** is an inline image without any CSS class,
26 * * **`'block'`** is a block image without any CSS class,
27 * * **`'side'`** is a block image styled with the `image-style-side` CSS class.
28 */
29export const DEFAULT_OPTIONS = {
30 // This style represents an image placed in the line of text.
31 get inline() {
32 return {
33 name: 'inline',
34 title: 'In line',
35 icon: objectInline,
36 modelElements: ['imageInline'],
37 isDefault: true
38 };
39 },
40 // This style represents an image aligned to the left and wrapped with text.
41 get alignLeft() {
42 return {
43 name: 'alignLeft',
44 title: 'Left aligned image',
45 icon: objectLeft,
46 modelElements: ['imageBlock', 'imageInline'],
47 className: 'image-style-align-left'
48 };
49 },
50 // This style represents an image aligned to the left.
51 get alignBlockLeft() {
52 return {
53 name: 'alignBlockLeft',
54 title: 'Left aligned image',
55 icon: objectBlockLeft,
56 modelElements: ['imageBlock'],
57 className: 'image-style-block-align-left'
58 };
59 },
60 // This style represents a centered image.
61 get alignCenter() {
62 return {
63 name: 'alignCenter',
64 title: 'Centered image',
65 icon: objectCenter,
66 modelElements: ['imageBlock'],
67 className: 'image-style-align-center'
68 };
69 },
70 // This style represents an image aligned to the right and wrapped with text.
71 get alignRight() {
72 return {
73 name: 'alignRight',
74 title: 'Right aligned image',
75 icon: objectRight,
76 modelElements: ['imageBlock', 'imageInline'],
77 className: 'image-style-align-right'
78 };
79 },
80 // This style represents an image aligned to the right.
81 get alignBlockRight() {
82 return {
83 name: 'alignBlockRight',
84 title: 'Right aligned image',
85 icon: objectBlockRight,
86 modelElements: ['imageBlock'],
87 className: 'image-style-block-align-right'
88 };
89 },
90 // This option is equal to the situation when no style is applied.
91 get block() {
92 return {
93 name: 'block',
94 title: 'Centered image',
95 icon: objectCenter,
96 modelElements: ['imageBlock'],
97 isDefault: true
98 };
99 },
100 // This represents a side image.
101 get side() {
102 return {
103 name: 'side',
104 title: 'Side image',
105 icon: objectRight,
106 modelElements: ['imageBlock'],
107 className: 'image-style-side'
108 };
109 }
110};
111/**
112 * Default image style icons provided by the plugin that can be referred in the {@link module:image/imageconfig~ImageConfig#styles}
113 * configuration.
114 *
115 * See {@link module:image/imageconfig~ImageStyleOptionDefinition#icon} to learn more.
116 *
117 * There are 7 default icons available: `'full'`, `'left'`, `'inlineLeft'`, `'center'`, `'right'`, `'inlineRight'`, and `'inline'`.
118 */
119export const DEFAULT_ICONS = {
120 full: objectFullWidth,
121 left: objectBlockLeft,
122 right: objectBlockRight,
123 center: objectCenter,
124 inlineLeft: objectLeft,
125 inlineRight: objectRight,
126 inline: objectInline
127};
128/**
129 * Default drop-downs provided by the plugin that can be referred in the {@link module:image/imageconfig~ImageConfig#toolbar}
130 * configuration. The drop-downs are containers for the {@link module:image/imageconfig~ImageStyleConfig#options image style options}.
131 *
132 * If both of the `ImageEditing` plugins are loaded, there are 2 predefined drop-downs available:
133 *
134 * * **`'imageStyle:wrapText'`**, which contains the `alignLeft` and `alignRight` options, that is,
135 * those that wraps the text around the image,
136 * * **`'imageStyle:breakText'`**, which contains the `alignBlockLeft`, `alignCenter` and `alignBlockRight` options, that is,
137 * those that breaks the text around the image.
138 */
139export const DEFAULT_DROPDOWN_DEFINITIONS = [{
140 name: 'imageStyle:wrapText',
141 title: 'Wrap text',
142 defaultItem: 'imageStyle:alignLeft',
143 items: ['imageStyle:alignLeft', 'imageStyle:alignRight']
144 }, {
145 name: 'imageStyle:breakText',
146 title: 'Break text',
147 defaultItem: 'imageStyle:block',
148 items: ['imageStyle:alignBlockLeft', 'imageStyle:block', 'imageStyle:alignBlockRight']
149 }];
150/**
151 * Returns a list of the normalized and validated image style options.
152 *
153 * @param config
154 * @param config.isInlinePluginLoaded
155 * Determines whether the {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin has been loaded.
156 * @param config.isBlockPluginLoaded
157 * Determines whether the {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin has been loaded.
158 * @param config.configuredStyles
159 * The image styles configuration provided in the image styles {@link module:image/imageconfig~ImageConfig#styles configuration}
160 * as a default or custom value.
161 * @returns
162 * * Each of options contains a complete icon markup.
163 * * The image style options not supported by any of the loaded plugins are filtered out.
164 */
165function normalizeStyles(config) {
166 const configuredStyles = config.configuredStyles.options || [];
167 const styles = configuredStyles
168 .map(arrangement => normalizeDefinition(arrangement))
169 .filter(arrangement => isValidOption(arrangement, config));
170 return styles;
171}
172/**
173 * Returns the default image styles configuration depending on the loaded image editing plugins.
174 *
175 * @param isInlinePluginLoaded
176 * Determines whether the {@link module:image/image/imageblockediting~ImageBlockEditing `ImageBlockEditing`} plugin has been loaded.
177 *
178 * @param isBlockPluginLoaded
179 * Determines whether the {@link module:image/image/imageinlineediting~ImageInlineEditing `ImageInlineEditing`} plugin has been loaded.
180 *
181 * @returns
182 * It returns an object with the lists of the image style options and groups defined as strings related to the
183 * {@link module:image/imagestyle/utils#DEFAULT_OPTIONS default options}
184 */
185function getDefaultStylesConfiguration(isBlockPluginLoaded, isInlinePluginLoaded) {
186 if (isBlockPluginLoaded && isInlinePluginLoaded) {
187 return {
188 options: [
189 'inline', 'alignLeft', 'alignRight',
190 'alignCenter', 'alignBlockLeft', 'alignBlockRight',
191 'block', 'side'
192 ]
193 };
194 }
195 else if (isBlockPluginLoaded) {
196 return {
197 options: ['block', 'side']
198 };
199 }
200 else if (isInlinePluginLoaded) {
201 return {
202 options: ['inline', 'alignLeft', 'alignRight']
203 };
204 }
205 return {};
206}
207/**
208 * Returns a list of the available predefined drop-downs' definitions depending on the loaded image editing plugins.
209 */
210function getDefaultDropdownDefinitions(pluginCollection) {
211 if (pluginCollection.has('ImageBlockEditing') && pluginCollection.has('ImageInlineEditing')) {
212 return [...DEFAULT_DROPDOWN_DEFINITIONS];
213 }
214 else {
215 return [];
216 }
217}
218/**
219 * Normalizes an image style option or group provided in the {@link module:image/imageconfig~ImageConfig#styles}
220 * and returns it in a {@link module:image/imageconfig~ImageStyleOptionDefinition}/
221 */
222function normalizeDefinition(definition) {
223 if (typeof definition === 'string') {
224 // Just the name of the style has been passed, but none of the defaults.
225 if (!DEFAULT_OPTIONS[definition]) {
226 // Normalize the style anyway to prevent errors.
227 definition = { name: definition };
228 }
229 // Just the name of the style has been passed and it's one of the defaults, just use it.
230 // Clone the style to avoid overriding defaults.
231 else {
232 definition = { ...DEFAULT_OPTIONS[definition] };
233 }
234 }
235 else {
236 // If an object style has been passed and if the name matches one of the defaults,
237 // extend it with defaults – the user wants to customize a default style.
238 // Note: Don't override the user–defined style object, clone it instead.
239 definition = extendStyle(DEFAULT_OPTIONS[definition.name], definition);
240 }
241 // If an icon is defined as a string and correspond with a name
242 // in default icons, use the default icon provided by the plugin.
243 if (typeof definition.icon === 'string') {
244 definition.icon = DEFAULT_ICONS[definition.icon] || definition.icon;
245 }
246 return definition;
247}
248/**
249 * Checks if the image style option is valid:
250 * * if it has the modelElements fields defined and filled,
251 * * if the defined modelElements are supported by any of the loaded image editing plugins.
252 * It also displays a console warning these conditions are not met.
253 *
254 * @param option image style option
255 */
256function isValidOption(option, { isBlockPluginLoaded, isInlinePluginLoaded }) {
257 const { modelElements, name } = option;
258 if (!modelElements || !modelElements.length || !name) {
259 warnInvalidStyle({ style: option });
260 return false;
261 }
262 else {
263 const supportedElements = [isBlockPluginLoaded ? 'imageBlock' : null, isInlinePluginLoaded ? 'imageInline' : null];
264 // Check if the option is supported by any of the loaded plugins.
265 if (!modelElements.some(elementName => supportedElements.includes(elementName))) {
266 /**
267 * In order to work correctly, each image style {@link module:image/imageconfig~ImageStyleOptionDefinition option}
268 * requires specific model elements (also: types of images) to be supported by the editor.
269 *
270 * Model element names to which the image style option can be applied are defined in the
271 * {@link module:image/imageconfig~ImageStyleOptionDefinition#modelElements} property of the style option
272 * definition.
273 *
274 * Explore the warning in the console to find out precisely which option is not supported and which editor plugins
275 * are missing. Make sure these plugins are loaded in your editor to get this image style option working.
276 *
277 * @error image-style-missing-dependency
278 * @param {String} [option] The name of the unsupported option.
279 * @param {String} [missingPlugins] The names of the plugins one of which has to be loaded for the particular option.
280 */
281 logWarning('image-style-missing-dependency', {
282 style: option,
283 missingPlugins: modelElements.map(name => name === 'imageBlock' ? 'ImageBlockEditing' : 'ImageInlineEditing')
284 });
285 return false;
286 }
287 }
288 return true;
289}
290/**
291 * Extends the default style with a style provided by the developer.
292 * Note: Don't override the custom–defined style object, clone it instead.
293 */
294function extendStyle(source, style) {
295 const extendedStyle = { ...style };
296 for (const prop in source) {
297 if (!Object.prototype.hasOwnProperty.call(style, prop)) {
298 extendedStyle[prop] = source[prop];
299 }
300 }
301 return extendedStyle;
302}
303/**
304 * Displays a console warning with the 'image-style-configuration-definition-invalid' error.
305 */
306function warnInvalidStyle(info) {
307 /**
308 * The image style definition provided in the configuration is invalid.
309 *
310 * Please make sure the definition implements properly one of the following:
311 *
312 * * {@link module:image/imageconfig~ImageStyleOptionDefinition image style option definition},
313 * * {@link module:image/imageconfig~ImageStyleDropdownDefinition image style dropdown definition}
314 *
315 * @error image-style-configuration-definition-invalid
316 * @param {String} [dropdown] The name of the invalid drop-down
317 * @param {String} [style] The name of the invalid image style option
318 */
319 logWarning('image-style-configuration-definition-invalid', info);
320}
321export default {
322 normalizeStyles,
323 getDefaultStylesConfiguration,
324 getDefaultDropdownDefinitions,
325 warnInvalidStyle,
326 DEFAULT_OPTIONS,
327 DEFAULT_ICONS,
328 DEFAULT_DROPDOWN_DEFINITIONS
329};