@ckeditor/ckeditor5-html-support 36.0.1 → 37.0.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (50) hide show
  1. package/README.md +2 -2
  2. package/build/html-support.js +1 -1
  3. package/ckeditor5-metadata.json +2 -2
  4. package/package.json +42 -36
  5. package/src/augmentation.d.ts +33 -0
  6. package/src/augmentation.js +5 -0
  7. package/src/conversionutils.d.ts +42 -0
  8. package/src/conversionutils.js +57 -77
  9. package/src/converters.d.ts +56 -0
  10. package/src/converters.js +104 -156
  11. package/src/datafilter.d.ts +250 -0
  12. package/src/datafilter.js +566 -782
  13. package/src/dataschema.d.ts +169 -0
  14. package/src/dataschema.js +143 -229
  15. package/src/fullpage.d.ts +21 -0
  16. package/src/fullpage.js +65 -86
  17. package/src/generalhtmlsupport.d.ts +88 -0
  18. package/src/generalhtmlsupport.js +244 -327
  19. package/src/generalhtmlsupportconfig.d.ts +67 -0
  20. package/src/generalhtmlsupportconfig.js +5 -0
  21. package/src/htmlcomment.d.ts +72 -0
  22. package/src/htmlcomment.js +175 -239
  23. package/src/htmlpagedataprocessor.d.ts +22 -0
  24. package/src/htmlpagedataprocessor.js +53 -76
  25. package/src/index.d.ts +25 -0
  26. package/src/index.js +1 -2
  27. package/src/integrations/codeblock.d.ts +22 -0
  28. package/src/integrations/codeblock.js +87 -115
  29. package/src/integrations/customelement.d.ts +25 -0
  30. package/src/integrations/customelement.js +127 -160
  31. package/src/integrations/documentlist.d.ts +26 -0
  32. package/src/integrations/documentlist.js +154 -191
  33. package/src/integrations/dualcontent.d.ts +44 -0
  34. package/src/integrations/dualcontent.js +92 -128
  35. package/src/integrations/heading.d.ts +25 -0
  36. package/src/integrations/heading.js +41 -54
  37. package/src/integrations/image.d.ts +25 -0
  38. package/src/integrations/image.js +154 -212
  39. package/src/integrations/integrationutils.d.ts +15 -0
  40. package/src/integrations/integrationutils.js +21 -0
  41. package/src/integrations/mediaembed.d.ts +25 -0
  42. package/src/integrations/mediaembed.js +101 -147
  43. package/src/integrations/script.d.ts +25 -0
  44. package/src/integrations/script.js +45 -67
  45. package/src/integrations/style.d.ts +25 -0
  46. package/src/integrations/style.js +45 -67
  47. package/src/integrations/table.d.ts +22 -0
  48. package/src/integrations/table.js +113 -160
  49. package/src/schemadefinitions.d.ts +13 -0
  50. package/src/schemadefinitions.js +846 -835
@@ -2,14 +2,11 @@
2
2
  * @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
-
6
5
  /**
7
6
  * @module html-support/generalhtmlsupport
8
7
  */
9
-
10
8
  import { Plugin } from 'ckeditor5/src/core';
11
9
  import { toArray } from 'ckeditor5/src/utils';
12
-
13
10
  import DataFilter from './datafilter';
14
11
  import CodeBlockElementSupport from './integrations/codeblock';
15
12
  import DualContentModelElementSupport from './integrations/dualcontent';
@@ -21,341 +18,261 @@ import TableElementSupport from './integrations/table';
21
18
  import StyleElementSupport from './integrations/style';
22
19
  import DocumentListElementSupport from './integrations/documentlist';
23
20
  import CustomElementSupport from './integrations/customelement';
24
-
25
21
  /**
26
22
  * The General HTML Support feature.
27
23
  *
28
24
  * This is a "glue" plugin which initializes the {@link module:html-support/datafilter~DataFilter data filter} configuration
29
25
  * and features integration with the General HTML Support.
30
- *
31
- * @extends module:core/plugin~Plugin
32
26
  */
33
27
  export default class GeneralHtmlSupport extends Plugin {
34
- /**
35
- * @inheritDoc
36
- */
37
- static get pluginName() {
38
- return 'GeneralHtmlSupport';
39
- }
40
-
41
- /**
42
- * @inheritDoc
43
- */
44
- static get requires() {
45
- return [
46
- DataFilter,
47
- CodeBlockElementSupport,
48
- DualContentModelElementSupport,
49
- HeadingElementSupport,
50
- ImageElementSupport,
51
- MediaEmbedElementSupport,
52
- ScriptElementSupport,
53
- TableElementSupport,
54
- StyleElementSupport,
55
- DocumentListElementSupport,
56
- CustomElementSupport
57
- ];
58
- }
59
-
60
- /**
61
- * @inheritDoc
62
- */
63
- init() {
64
- const editor = this.editor;
65
- const dataFilter = editor.plugins.get( DataFilter );
66
-
67
- // Load the filtering configuration.
68
- dataFilter.loadAllowedConfig( editor.config.get( 'htmlSupport.allow' ) || [] );
69
- dataFilter.loadDisallowedConfig( editor.config.get( 'htmlSupport.disallow' ) || [] );
70
- }
71
-
72
- /**
73
- * Returns a GHS model attribute name related to a given view element name.
74
- *
75
- * @protected
76
- * @param {String} viewElementName A view element name.
77
- * @returns {String}
78
- */
79
- getGhsAttributeNameForElement( viewElementName ) {
80
- const dataSchema = this.editor.plugins.get( 'DataSchema' );
81
- const definitions = Array.from( dataSchema.getDefinitionsForView( viewElementName, false ) );
82
-
83
- if ( definitions && definitions.length && definitions[ 0 ].isInline && !definitions[ 0 ].isObject ) {
84
- return definitions[ 0 ].model;
85
- }
86
-
87
- return 'htmlAttributes';
88
- }
89
-
90
- /**
91
- * Updates GHS model attribute for a specified view element name, so it includes the given class name.
92
- *
93
- * @protected
94
- * @param {String} viewElementName A view element name.
95
- * @param {String|Array.<String>} className The css class to add.
96
- * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
97
- */
98
- addModelHtmlClass( viewElementName, className, selectable ) {
99
- const model = this.editor.model;
100
- const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );
101
-
102
- model.change( writer => {
103
- for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
104
- modifyGhsAttribute( writer, item, ghsAttributeName, 'classes', classes => {
105
- for ( const value of toArray( className ) ) {
106
- classes.add( value );
107
- }
108
- } );
109
- }
110
- } );
111
- }
112
-
113
- /**
114
- * Updates GHS model attribute for a specified view element name, so it does not include the given class name.
115
- *
116
- * @protected
117
- * @param {String} viewElementName A view element name.
118
- * @param {String|Array.<String>} className The css class to remove.
119
- * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
120
- */
121
- removeModelHtmlClass( viewElementName, className, selectable ) {
122
- const model = this.editor.model;
123
- const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );
124
-
125
- model.change( writer => {
126
- for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
127
- modifyGhsAttribute( writer, item, ghsAttributeName, 'classes', classes => {
128
- for ( const value of toArray( className ) ) {
129
- classes.delete( value );
130
- }
131
- } );
132
- }
133
- } );
134
- }
135
-
136
- /**
137
- * Updates GHS model attribute for a specified view element name, so it includes the given attribute.
138
- *
139
- * @protected
140
- * @param {String} viewElementName A view element name.
141
- * @param {Object} attributes The object with attributes to set.
142
- * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
143
- */
144
- setModelHtmlAttributes( viewElementName, attributes, selectable ) {
145
- const model = this.editor.model;
146
- const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );
147
-
148
- model.change( writer => {
149
- for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
150
- modifyGhsAttribute( writer, item, ghsAttributeName, 'attributes', attributesMap => {
151
- for ( const [ key, value ] of Object.entries( attributes ) ) {
152
- attributesMap.set( key, value );
153
- }
154
- } );
155
- }
156
- } );
157
- }
158
-
159
- /**
160
- * Updates GHS model attribute for a specified view element name, so it does not include the given attribute.
161
- *
162
- * @protected
163
- * @param {String} viewElementName A view element name.
164
- * @param {String|Array.<String>} attributeName The attribute name (or names) to remove.
165
- * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
166
- */
167
- removeModelHtmlAttributes( viewElementName, attributeName, selectable ) {
168
- const model = this.editor.model;
169
- const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );
170
-
171
- model.change( writer => {
172
- for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
173
- modifyGhsAttribute( writer, item, ghsAttributeName, 'attributes', attributesMap => {
174
- for ( const key of toArray( attributeName ) ) {
175
- attributesMap.delete( key );
176
- }
177
- } );
178
- }
179
- } );
180
- }
181
-
182
- /**
183
- * Updates GHS model attribute for a specified view element name, so it includes a given style.
184
- *
185
- * @protected
186
- * @param {String} viewElementName A view element name.
187
- * @param {Object} styles The object with styles to set.
188
- * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
189
- */
190
- setModelHtmlStyles( viewElementName, styles, selectable ) {
191
- const model = this.editor.model;
192
- const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );
193
-
194
- model.change( writer => {
195
- for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
196
- modifyGhsAttribute( writer, item, ghsAttributeName, 'styles', stylesMap => {
197
- for ( const [ key, value ] of Object.entries( styles ) ) {
198
- stylesMap.set( key, value );
199
- }
200
- } );
201
- }
202
- } );
203
- }
204
-
205
- /**
206
- * Updates GHS model attribute for a specified view element name, so it does not include a given style.
207
- *
208
- * @protected
209
- * @param {String} viewElementName A view element name.
210
- * @param {String|Array.<String>} properties The style (or styles list) to remove.
211
- * @param {module:engine/model/selection~Selectable} selectable The selection or element to update.
212
- */
213
- removeModelHtmlStyles( viewElementName, properties, selectable ) {
214
- const model = this.editor.model;
215
- const ghsAttributeName = this.getGhsAttributeNameForElement( viewElementName );
216
-
217
- model.change( writer => {
218
- for ( const item of getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) ) {
219
- modifyGhsAttribute( writer, item, ghsAttributeName, 'styles', stylesMap => {
220
- for ( const key of toArray( properties ) ) {
221
- stylesMap.delete( key );
222
- }
223
- } );
224
- }
225
- } );
226
- }
227
- }
228
-
229
- // Returns an iterator over an items in the selectable that accept given GHS attribute.
230
- function* getItemsToUpdateGhsAttribute( model, selectable, ghsAttributeName ) {
231
- if ( selectable.is( 'documentSelection' ) && selectable.isCollapsed ) {
232
- if ( model.schema.checkAttributeInSelection( selectable, ghsAttributeName ) ) {
233
- yield selectable;
234
- }
235
- } else {
236
- for ( const range of getValidRangesForSelectable( model, selectable, ghsAttributeName ) ) {
237
- yield* range.getItems( { shallow: true } );
238
- }
239
- }
240
- }
241
-
242
- // Translates a given selectable to an iterable of ranges.
243
- function getValidRangesForSelectable( model, selectable, ghsAttributeName ) {
244
- if ( selectable.is( 'node' ) || selectable.is( '$text' ) || selectable.is( '$textProxy' ) ) {
245
- if ( model.schema.checkAttribute( selectable, ghsAttributeName ) ) {
246
- return [ model.createRangeOn( selectable ) ];
247
- } else {
248
- return [];
249
- }
250
- } else {
251
- return model.schema.getValidRanges( model.createSelection( selectable ).getRanges(), ghsAttributeName );
252
- }
28
+ /**
29
+ * @inheritDoc
30
+ */
31
+ static get pluginName() {
32
+ return 'GeneralHtmlSupport';
33
+ }
34
+ /**
35
+ * @inheritDoc
36
+ */
37
+ static get requires() {
38
+ return [
39
+ DataFilter,
40
+ CodeBlockElementSupport,
41
+ DualContentModelElementSupport,
42
+ HeadingElementSupport,
43
+ ImageElementSupport,
44
+ MediaEmbedElementSupport,
45
+ ScriptElementSupport,
46
+ TableElementSupport,
47
+ StyleElementSupport,
48
+ DocumentListElementSupport,
49
+ CustomElementSupport
50
+ ];
51
+ }
52
+ /**
53
+ * @inheritDoc
54
+ */
55
+ init() {
56
+ const editor = this.editor;
57
+ const dataFilter = editor.plugins.get(DataFilter);
58
+ // Load the filtering configuration.
59
+ dataFilter.loadAllowedConfig(editor.config.get('htmlSupport.allow') || []);
60
+ dataFilter.loadDisallowedConfig(editor.config.get('htmlSupport.disallow') || []);
61
+ }
62
+ /**
63
+ * Returns a GHS model attribute name related to a given view element name.
64
+ *
65
+ * @param viewElementName A view element name.
66
+ */
67
+ getGhsAttributeNameForElement(viewElementName) {
68
+ const dataSchema = this.editor.plugins.get('DataSchema');
69
+ const definitions = Array.from(dataSchema.getDefinitionsForView(viewElementName, false));
70
+ if (definitions &&
71
+ definitions.length &&
72
+ definitions[0].isInline &&
73
+ !definitions[0].isObject) {
74
+ return definitions[0].model;
75
+ }
76
+ return 'htmlAttributes';
77
+ }
78
+ /**
79
+ * Updates GHS model attribute for a specified view element name, so it includes the given class name.
80
+ *
81
+ * @internal
82
+ * @param viewElementName A view element name.
83
+ * @param className The css class to add.
84
+ * @param selectable The selection or element to update.
85
+ */
86
+ addModelHtmlClass(viewElementName, className, selectable) {
87
+ const model = this.editor.model;
88
+ const ghsAttributeName = this.getGhsAttributeNameForElement(viewElementName);
89
+ model.change(writer => {
90
+ for (const item of getItemsToUpdateGhsAttribute(model, selectable, ghsAttributeName)) {
91
+ modifyGhsAttribute(writer, item, ghsAttributeName, 'classes', classes => {
92
+ for (const value of toArray(className)) {
93
+ classes.add(value);
94
+ }
95
+ });
96
+ }
97
+ });
98
+ }
99
+ /**
100
+ * Updates GHS model attribute for a specified view element name, so it does not include the given class name.
101
+ *
102
+ * @internal
103
+ * @param viewElementName A view element name.
104
+ * @param className The css class to remove.
105
+ * @param selectable The selection or element to update.
106
+ */
107
+ removeModelHtmlClass(viewElementName, className, selectable) {
108
+ const model = this.editor.model;
109
+ const ghsAttributeName = this.getGhsAttributeNameForElement(viewElementName);
110
+ model.change(writer => {
111
+ for (const item of getItemsToUpdateGhsAttribute(model, selectable, ghsAttributeName)) {
112
+ modifyGhsAttribute(writer, item, ghsAttributeName, 'classes', classes => {
113
+ for (const value of toArray(className)) {
114
+ classes.delete(value);
115
+ }
116
+ });
117
+ }
118
+ });
119
+ }
120
+ /**
121
+ * Updates GHS model attribute for a specified view element name, so it includes the given attribute.
122
+ *
123
+ * @param viewElementName A view element name.
124
+ * @param attributes The object with attributes to set.
125
+ * @param selectable The selection or element to update.
126
+ */
127
+ setModelHtmlAttributes(viewElementName, attributes, selectable) {
128
+ const model = this.editor.model;
129
+ const ghsAttributeName = this.getGhsAttributeNameForElement(viewElementName);
130
+ model.change(writer => {
131
+ for (const item of getItemsToUpdateGhsAttribute(model, selectable, ghsAttributeName)) {
132
+ modifyGhsAttribute(writer, item, ghsAttributeName, 'attributes', attributesMap => {
133
+ for (const [key, value] of Object.entries(attributes)) {
134
+ attributesMap.set(key, value);
135
+ }
136
+ });
137
+ }
138
+ });
139
+ }
140
+ /**
141
+ * Updates GHS model attribute for a specified view element name, so it does not include the given attribute.
142
+ *
143
+ * @param viewElementName A view element name.
144
+ * @param attributeName The attribute name (or names) to remove.
145
+ * @param selectable The selection or element to update.
146
+ */
147
+ removeModelHtmlAttributes(viewElementName, attributeName, selectable) {
148
+ const model = this.editor.model;
149
+ const ghsAttributeName = this.getGhsAttributeNameForElement(viewElementName);
150
+ model.change(writer => {
151
+ for (const item of getItemsToUpdateGhsAttribute(model, selectable, ghsAttributeName)) {
152
+ modifyGhsAttribute(writer, item, ghsAttributeName, 'attributes', attributesMap => {
153
+ for (const key of toArray(attributeName)) {
154
+ attributesMap.delete(key);
155
+ }
156
+ });
157
+ }
158
+ });
159
+ }
160
+ /**
161
+ * Updates GHS model attribute for a specified view element name, so it includes a given style.
162
+ *
163
+ * @param viewElementName A view element name.
164
+ * @param styles The object with styles to set.
165
+ * @param selectable The selection or element to update.
166
+ */
167
+ setModelHtmlStyles(viewElementName, styles, selectable) {
168
+ const model = this.editor.model;
169
+ const ghsAttributeName = this.getGhsAttributeNameForElement(viewElementName);
170
+ model.change(writer => {
171
+ for (const item of getItemsToUpdateGhsAttribute(model, selectable, ghsAttributeName)) {
172
+ modifyGhsAttribute(writer, item, ghsAttributeName, 'styles', stylesMap => {
173
+ for (const [key, value] of Object.entries(styles)) {
174
+ stylesMap.set(key, value);
175
+ }
176
+ });
177
+ }
178
+ });
179
+ }
180
+ /**
181
+ * Updates GHS model attribute for a specified view element name, so it does not include a given style.
182
+ *
183
+ * @param viewElementName A view element name.
184
+ * @param properties The style (or styles list) to remove.
185
+ * @param selectable The selection or element to update.
186
+ */
187
+ removeModelHtmlStyles(viewElementName, properties, selectable) {
188
+ const model = this.editor.model;
189
+ const ghsAttributeName = this.getGhsAttributeNameForElement(viewElementName);
190
+ model.change(writer => {
191
+ for (const item of getItemsToUpdateGhsAttribute(model, selectable, ghsAttributeName)) {
192
+ modifyGhsAttribute(writer, item, ghsAttributeName, 'styles', stylesMap => {
193
+ for (const key of toArray(properties)) {
194
+ stylesMap.delete(key);
195
+ }
196
+ });
197
+ }
198
+ });
199
+ }
253
200
  }
254
-
255
- // Updates a GHS attribute on a specified item.
256
- // @param {module:engine/model/writer~Writer} writer
257
- // @param {module:engine/model/item~Item|module:engine/model/documentselection~DocumentSelection} item
258
- // @param {String} ghsAttributeName
259
- // @param {'classes'|'attributes'|'styles'} subject
260
- // @param {Function} callback That receives a map or set as an argument and should modify it (add or remove entries).
261
- function modifyGhsAttribute( writer, item, ghsAttributeName, subject, callback ) {
262
- const oldValue = item.getAttribute( ghsAttributeName );
263
- const newValue = {};
264
-
265
- for ( const kind of [ 'attributes', 'styles', 'classes' ] ) {
266
- if ( kind != subject ) {
267
- if ( oldValue && oldValue[ kind ] ) {
268
- newValue[ kind ] = oldValue[ kind ];
269
- }
270
- } else {
271
- const values = kind == 'classes' ?
272
- new Set( oldValue && oldValue[ kind ] || [] ) :
273
- new Map( Object.entries( oldValue && oldValue[ kind ] || {} ) );
274
-
275
- callback( values );
276
-
277
- if ( values.size ) {
278
- newValue[ kind ] = kind == 'classes' ? Array.from( values ) : Object.fromEntries( values );
279
- }
280
- }
281
- }
282
-
283
- if ( Object.keys( newValue ).length ) {
284
- if ( item.is( 'documentSelection' ) ) {
285
- writer.setSelectionAttribute( ghsAttributeName, newValue );
286
- } else {
287
- writer.setAttribute( ghsAttributeName, newValue, item );
288
- }
289
- } else if ( oldValue ) {
290
- if ( item.is( 'documentSelection' ) ) {
291
- writer.removeSelectionAttribute( ghsAttributeName );
292
- } else {
293
- writer.removeAttribute( ghsAttributeName, item );
294
- }
295
- }
296
- }
297
-
298
201
  /**
299
- * The configuration of the General HTML Support feature.
300
- * Introduced by the {@link module:html-support/generalhtmlsupport~GeneralHtmlSupport} feature.
301
- *
302
- * Read more in {@link module:html-support/generalhtmlsupport~GeneralHtmlSupportConfig}.
303
- *
304
- * @member {module:htmlsupport/generalhtmlsupport~GeneralHtmlSupportConfig} module:core/editor/editorconfig~EditorConfig#htmlSupport
202
+ * Returns an iterator over an items in the selectable that accept given GHS attribute.
305
203
  */
306
-
307
- /**
308
- * The configuration of the General HTML Support feature.
309
- * The option is used by the {@link module:html-support/generalhtmlsupport~GeneralHtmlSupport} feature.
310
- *
311
- * ClassicEditor
312
- * .create( {
313
- * htmlSupport: ... // General HTML Support feature config.
314
- * } )
315
- * .then( ... )
316
- * .catch( ... );
317
- *
318
- * See {@link module:core/editor/editorconfig~EditorConfig all editor options}.
319
- *
320
- * @interface GeneralHtmlSupportConfig
321
- */
322
-
204
+ function* getItemsToUpdateGhsAttribute(model, selectable, ghsAttributeName) {
205
+ if (selectable.is('documentSelection') && selectable.isCollapsed) {
206
+ if (model.schema.checkAttributeInSelection(selectable, ghsAttributeName)) {
207
+ yield selectable;
208
+ }
209
+ }
210
+ else {
211
+ for (const range of getValidRangesForSelectable(model, selectable, ghsAttributeName)) {
212
+ yield* range.getItems({ shallow: true });
213
+ }
214
+ }
215
+ }
323
216
  /**
324
- * The configuration of allowed content rules used by General HTML Support.
325
- *
326
- * Setting this configuration option will enable HTML features that are not explicitly supported by any other dedicated CKEditor 5 features.
327
- *
328
- * const htmlSupportConfig.allow = [
329
- * {
330
- * name: 'div', // Enable 'div' element support,
331
- * classes: [ 'special-container' ], // allow 'special-container' class,
332
- * styles: 'background', // allow 'background' style,
333
- * attributes: true // allow any attribute (can be empty).
334
- * },
335
- * {
336
- * name: 'p', // Extend existing Paragraph feature,
337
- * classes: 'highlighted' // with 'highlighted' class,
338
- * attributes: [
339
- * { key: 'data-i18n-context, value: true } // and i18n attribute.
340
- * ]
341
- * }
342
- * ];
343
- *
344
- * @member {Array.<module:engine/view/matcher~MatcherPattern>} module:html-support/generalhtmlsupport~GeneralHtmlSupportConfig#allow
217
+ * Translates a given selectable to an iterable of ranges.
345
218
  */
346
-
219
+ function getValidRangesForSelectable(model, selectable, ghsAttributeName) {
220
+ if (selectable.is('node') || selectable.is('$text') || selectable.is('$textProxy')) {
221
+ if (model.schema.checkAttribute(selectable, ghsAttributeName)) {
222
+ return [model.createRangeOn(selectable)];
223
+ }
224
+ else {
225
+ return [];
226
+ }
227
+ }
228
+ else {
229
+ return model.schema.getValidRanges(model.createSelection(selectable).getRanges(), ghsAttributeName);
230
+ }
231
+ }
347
232
  /**
348
- * The configuration of disallowed content rules used by General HTML Support.
349
- *
350
- * Setting this configuration option will disable listed HTML features.
351
- *
352
- * const htmlSupportConfig.disallow = [
353
- * {
354
- * name: /[\s\S]+/ // For every HTML feature,
355
- * attributes: {
356
- * key: /^on.*$/ // disable 'on*' attributes, like 'onClick', 'onError' etc.
357
- * }
358
- * }
359
- * ];
360
- * @member {Array.<module:engine/view/matcher~MatcherPattern>} module:html-support/generalhtmlsupport~GeneralHtmlSupportConfig#disallow
233
+ * Updates a GHS attribute on a specified item.
234
+ * @param callback That receives a map or set as an argument and should modify it (add or remove entries).
361
235
  */
236
+ function modifyGhsAttribute(writer, item, ghsAttributeName, subject, callback) {
237
+ const oldValue = item.getAttribute(ghsAttributeName);
238
+ const newValue = {};
239
+ for (const kind of ['attributes', 'styles', 'classes']) {
240
+ // Properties other than `subject` should be assigned from `oldValue`.
241
+ if (kind != subject) {
242
+ if (oldValue && oldValue[kind]) {
243
+ newValue[kind] = oldValue[kind];
244
+ }
245
+ continue;
246
+ }
247
+ // `callback` should be applied on property [`subject`].
248
+ if (subject == 'classes') {
249
+ const values = new Set(oldValue && oldValue.classes || []);
250
+ callback(values);
251
+ if (values.size) {
252
+ newValue[kind] = Array.from(values);
253
+ }
254
+ continue;
255
+ }
256
+ const values = new Map(Object.entries(oldValue && oldValue[kind] || {}));
257
+ callback(values);
258
+ if (values.size) {
259
+ newValue[kind] = Object.fromEntries(values);
260
+ }
261
+ }
262
+ if (Object.keys(newValue).length) {
263
+ if (item.is('documentSelection')) {
264
+ writer.setSelectionAttribute(ghsAttributeName, newValue);
265
+ }
266
+ else {
267
+ writer.setAttribute(ghsAttributeName, newValue, item);
268
+ }
269
+ }
270
+ else if (oldValue) {
271
+ if (item.is('documentSelection')) {
272
+ writer.removeSelectionAttribute(ghsAttributeName);
273
+ }
274
+ else {
275
+ writer.removeAttribute(ghsAttributeName, item);
276
+ }
277
+ }
278
+ }