@ckeditor/ckeditor5-html-support 29.2.0 → 32.0.0

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 (45) hide show
  1. package/LICENSE.md +2 -2
  2. package/build/html-support.js +3 -3
  3. package/build/translations/cs.js +1 -0
  4. package/build/translations/es.js +1 -0
  5. package/build/translations/id.js +1 -0
  6. package/build/translations/nl.js +1 -0
  7. package/build/translations/pl.js +1 -0
  8. package/build/translations/pt-br.js +1 -0
  9. package/build/translations/sk.js +1 -0
  10. package/build/translations/sr-latn.js +1 -0
  11. package/build/translations/sr.js +1 -0
  12. package/build/translations/zh.js +1 -0
  13. package/lang/translations/cs.po +21 -0
  14. package/lang/translations/de.po +1 -1
  15. package/lang/translations/en.po +1 -1
  16. package/lang/translations/es.po +21 -0
  17. package/lang/translations/gl.po +1 -1
  18. package/lang/translations/hu.po +1 -1
  19. package/lang/translations/id.po +21 -0
  20. package/lang/translations/it.po +1 -1
  21. package/lang/translations/nl.po +21 -0
  22. package/lang/translations/pl.po +21 -0
  23. package/lang/translations/pt-br.po +21 -0
  24. package/lang/translations/ru.po +1 -1
  25. package/lang/translations/sk.po +21 -0
  26. package/lang/translations/sr-latn.po +21 -0
  27. package/lang/translations/sr.po +21 -0
  28. package/lang/translations/zh.po +21 -0
  29. package/package.json +34 -34
  30. package/src/conversionutils.js +1 -1
  31. package/src/converters.js +3 -21
  32. package/src/datafilter.js +6 -22
  33. package/src/dataschema.js +46 -2
  34. package/src/generalhtmlsupport.js +11 -5
  35. package/src/htmlcomment.js +1 -1
  36. package/src/index.js +1 -1
  37. package/src/integrations/codeblock.js +1 -3
  38. package/src/integrations/dualcontent.js +1 -3
  39. package/src/integrations/heading.js +61 -0
  40. package/src/integrations/image.js +193 -0
  41. package/src/integrations/mediaembed.js +11 -43
  42. package/src/integrations/script.js +73 -0
  43. package/src/integrations/table.js +5 -29
  44. package/src/schemadefinitions.js +26 -26
  45. package/CHANGELOG.md +0 -4
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-html-support",
3
- "version": "29.2.0",
3
+ "version": "32.0.0",
4
4
  "description": "HTML Support feature for CKEditor 5.",
5
5
  "keywords": [
6
6
  "ckeditor",
@@ -16,44 +16,44 @@
16
16
  ],
17
17
  "main": "src/index.js",
18
18
  "dependencies": {
19
- "ckeditor5": "^29.2.0",
19
+ "ckeditor5": "^32.0.0",
20
20
  "lodash-es": "^4.17.15"
21
21
  },
22
22
  "devDependencies": {
23
- "@ckeditor/ckeditor5-alignment": "^29.2.0",
24
- "@ckeditor/ckeditor5-basic-styles": "^29.2.0",
25
- "@ckeditor/ckeditor5-block-quote": "^29.2.0",
26
- "@ckeditor/ckeditor5-cloud-services": "^29.2.0",
27
- "@ckeditor/ckeditor5-code-block": "^29.2.0",
28
- "@ckeditor/ckeditor5-core": "^29.2.0",
29
- "@ckeditor/ckeditor5-dev-utils": "^25.4.0",
30
- "@ckeditor/ckeditor5-easy-image": "^29.2.0",
31
- "@ckeditor/ckeditor5-editor-classic": "^29.2.0",
32
- "@ckeditor/ckeditor5-engine": "^29.2.0",
33
- "@ckeditor/ckeditor5-enter": "^29.2.0",
34
- "@ckeditor/ckeditor5-essentials": "^29.2.0",
35
- "@ckeditor/ckeditor5-font": "^29.2.0",
36
- "@ckeditor/ckeditor5-heading": "^29.2.0",
37
- "@ckeditor/ckeditor5-highlight": "^29.2.0",
38
- "@ckeditor/ckeditor5-horizontal-line": "^29.2.0",
39
- "@ckeditor/ckeditor5-html-embed": "^29.2.0",
40
- "@ckeditor/ckeditor5-image": "^29.2.0",
41
- "@ckeditor/ckeditor5-indent": "^29.2.0",
42
- "@ckeditor/ckeditor5-link": "^29.2.0",
43
- "@ckeditor/ckeditor5-list": "^29.2.0",
44
- "@ckeditor/ckeditor5-media-embed": "^29.2.0",
45
- "@ckeditor/ckeditor5-page-break": "^29.2.0",
46
- "@ckeditor/ckeditor5-paragraph": "^29.2.0",
47
- "@ckeditor/ckeditor5-paste-from-office": "^29.2.0",
48
- "@ckeditor/ckeditor5-source-editing": "^29.2.0",
49
- "@ckeditor/ckeditor5-table": "^29.2.0",
50
- "@ckeditor/ckeditor5-theme-lark": "^29.2.0",
51
- "@ckeditor/ckeditor5-utils": "^29.2.0",
52
- "webpack": "^4.43.0",
53
- "webpack-cli": "^3.3.11"
23
+ "@ckeditor/ckeditor5-alignment": "^32.0.0",
24
+ "@ckeditor/ckeditor5-basic-styles": "^32.0.0",
25
+ "@ckeditor/ckeditor5-block-quote": "^32.0.0",
26
+ "@ckeditor/ckeditor5-cloud-services": "^32.0.0",
27
+ "@ckeditor/ckeditor5-code-block": "^32.0.0",
28
+ "@ckeditor/ckeditor5-core": "^32.0.0",
29
+ "@ckeditor/ckeditor5-dev-utils": "^27.1.0",
30
+ "@ckeditor/ckeditor5-easy-image": "^32.0.0",
31
+ "@ckeditor/ckeditor5-editor-classic": "^32.0.0",
32
+ "@ckeditor/ckeditor5-engine": "^32.0.0",
33
+ "@ckeditor/ckeditor5-enter": "^32.0.0",
34
+ "@ckeditor/ckeditor5-essentials": "^32.0.0",
35
+ "@ckeditor/ckeditor5-font": "^32.0.0",
36
+ "@ckeditor/ckeditor5-heading": "^32.0.0",
37
+ "@ckeditor/ckeditor5-highlight": "^32.0.0",
38
+ "@ckeditor/ckeditor5-horizontal-line": "^32.0.0",
39
+ "@ckeditor/ckeditor5-html-embed": "^32.0.0",
40
+ "@ckeditor/ckeditor5-image": "^32.0.0",
41
+ "@ckeditor/ckeditor5-indent": "^32.0.0",
42
+ "@ckeditor/ckeditor5-link": "^32.0.0",
43
+ "@ckeditor/ckeditor5-list": "^32.0.0",
44
+ "@ckeditor/ckeditor5-media-embed": "^32.0.0",
45
+ "@ckeditor/ckeditor5-page-break": "^32.0.0",
46
+ "@ckeditor/ckeditor5-paragraph": "^32.0.0",
47
+ "@ckeditor/ckeditor5-paste-from-office": "^32.0.0",
48
+ "@ckeditor/ckeditor5-source-editing": "^32.0.0",
49
+ "@ckeditor/ckeditor5-table": "^32.0.0",
50
+ "@ckeditor/ckeditor5-theme-lark": "^32.0.0",
51
+ "@ckeditor/ckeditor5-utils": "^32.0.0",
52
+ "webpack": "^5.58.1",
53
+ "webpack-cli": "^4.9.0"
54
54
  },
55
55
  "engines": {
56
- "node": ">=12.0.0",
56
+ "node": ">=14.0.0",
57
57
  "npm": ">=5.7.1"
58
58
  },
59
59
  "author": "CKSource (http://cksource.com/)",
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
package/src/converters.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
@@ -10,24 +10,6 @@
10
10
  import { toWidget } from 'ckeditor5/src/widget';
11
11
  import { setViewAttributes, mergeViewElementAttributes } from './conversionutils';
12
12
 
13
- /**
14
- * Conversion helper consuming all disallowed attributes from the definition view element.
15
- *
16
- * This converter listenes on `high` priority to ensure that all attributes are consumed
17
- * before standard priority converters.
18
- *
19
- * @param {module:html-support/dataschema~DataSchemaDefinition} definition
20
- * @param {module:html-support/datafilter~DataFilter} dataFilter
21
- * @returns {Function} Returns a conversion callback.
22
- */
23
- export function disallowedAttributesConverter( { view: viewName }, dataFilter ) {
24
- return dispatcher => {
25
- dispatcher.on( `element:${ viewName }`, ( evt, data, conversionApi ) => {
26
- dataFilter._consumeDisallowedAttributes( data.viewItem, conversionApi );
27
- }, { priority: 'high' } );
28
- };
29
- }
30
-
31
13
  /**
32
14
  * View-to-model conversion helper for object elements.
33
15
  *
@@ -90,8 +72,8 @@ export function toObjectWidgetConverter( editor, { view: viewName, isInline } )
90
72
  * @returns {module:engine/view/element~Element}
91
73
  */
92
74
  export function createObjectView( viewName, modelElement, writer ) {
93
- return writer.createRawElement( viewName, null, function( domElement ) {
94
- domElement.innerHTML = modelElement.getAttribute( 'htmlContent' );
75
+ return writer.createRawElement( viewName, null, ( domElement, domConverter ) => {
76
+ domConverter.setContentOf( domElement, modelElement.getAttribute( 'htmlContent' ) );
95
77
  } );
96
78
  }
97
79
 
package/src/datafilter.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
@@ -14,8 +14,6 @@ import { Matcher } from 'ckeditor5/src/engine';
14
14
  import { priorities, CKEditorError } from 'ckeditor5/src/utils';
15
15
  import { Widget } from 'ckeditor5/src/widget';
16
16
  import {
17
- disallowedAttributesConverter,
18
-
19
17
  viewToModelObjectConverter,
20
18
  toObjectWidgetConverter,
21
19
  createObjectView,
@@ -210,7 +208,7 @@ export default class DataFilter extends Plugin {
210
208
  }
211
209
 
212
210
  /**
213
- * Matches and consumes allowed view attributes.
211
+ * Matches and consumes allowed and disallowed view attributes and returns the allowed ones.
214
212
  *
215
213
  * @protected
216
214
  * @param {module:engine/view/element~Element} viewElement
@@ -221,22 +219,11 @@ export default class DataFilter extends Plugin {
221
219
  * @returns {Array.<String>} result.classes Set with matched class names.
222
220
  */
223
221
  _consumeAllowedAttributes( viewElement, conversionApi ) {
224
- return consumeAttributes( viewElement, conversionApi, this._allowedAttributes );
225
- }
222
+ // Make sure that the disabled attributes are handled before the allowed attributes are called.
223
+ // For example, for block images the <figure> converter triggers conversion for <img> first and then for other elements, i.e. <a>.
224
+ consumeAttributes( viewElement, conversionApi, this._disallowedAttributes );
226
225
 
227
- /**
228
- * Matches and consumes disallowed view attributes.
229
- *
230
- * @protected
231
- * @param {module:engine/view/element~Element} viewElement
232
- * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
233
- * @returns {Object} [result]
234
- * @returns {Object} result.attributes Set with matched attribute names.
235
- * @returns {Object} result.styles Set with matched style names.
236
- * @returns {Array.<String>} result.classes Set with matched class names.
237
- */
238
- _consumeDisallowedAttributes( viewElement, conversionApi ) {
239
- return consumeAttributes( viewElement, conversionApi, this._disallowedAttributes );
226
+ return consumeAttributes( viewElement, conversionApi, this._allowedAttributes );
240
227
  }
241
228
 
242
229
  /**
@@ -335,7 +322,6 @@ export default class DataFilter extends Plugin {
335
322
  name: viewName
336
323
  } );
337
324
 
338
- conversion.for( 'upcast' ).add( disallowedAttributesConverter( definition, this ) );
339
325
  conversion.for( 'upcast' ).elementToElement( {
340
326
  view: viewName,
341
327
  model: viewToModelObjectConverter( definition ),
@@ -400,7 +386,6 @@ export default class DataFilter extends Plugin {
400
386
  allowAttributes: 'htmlAttributes'
401
387
  } );
402
388
 
403
- conversion.for( 'upcast' ).add( disallowedAttributesConverter( definition, this ) );
404
389
  conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, this ) );
405
390
  conversion.for( 'downcast' ).add( modelToViewBlockAttributeConverter( definition ) );
406
391
  }
@@ -427,7 +412,6 @@ export default class DataFilter extends Plugin {
427
412
  schema.setAttributeProperties( attributeKey, definition.attributeProperties );
428
413
  }
429
414
 
430
- conversion.for( 'upcast' ).add( disallowedAttributesConverter( definition, this ) );
431
415
  conversion.for( 'upcast' ).add( viewToAttributeInlineConverter( definition, this ) );
432
416
 
433
417
  conversion.for( 'downcast' ).attributeToElement( {
package/src/dataschema.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
@@ -10,6 +10,7 @@
10
10
  import { Plugin } from 'ckeditor5/src/core';
11
11
  import { toArray } from 'ckeditor5/src/utils';
12
12
  import defaultConfig from './schemadefinitions';
13
+ import { mergeWith } from 'lodash-es';
13
14
 
14
15
  /**
15
16
  * Holds representation of the extended HTML document type definitions to be used by the
@@ -93,6 +94,30 @@ export default class DataSchema extends Plugin {
93
94
  this._definitions.set( definition.model, { ...definition, isInline: true } );
94
95
  }
95
96
 
97
+ /**
98
+ * Updates schema definition describing block element with new properties.
99
+ *
100
+ * Creates new scheme if it doesn't exist.
101
+ * Array properties are concatenated with original values.
102
+ *
103
+ * @param {module:html-support/dataschema~DataSchemaBlockElementDefinition} definition Definition update.
104
+ */
105
+ extendBlockElement( definition ) {
106
+ this._extendDefinition( { ...definition, isBlock: true } );
107
+ }
108
+
109
+ /**
110
+ * Updates schema definition describing inline element with new properties.
111
+ *
112
+ * Creates new scheme if it doesn't exist.
113
+ * Array properties are concatenated with original values.
114
+ *
115
+ * @param {module:html-support/dataschema~DataSchemaInlineElementDefinition} definition Definition update.
116
+ */
117
+ extendInlineElement( definition ) {
118
+ this._extendDefinition( { ...definition, isInline: true } );
119
+ }
120
+
96
121
  /**
97
122
  * Returns all definitions matching the given view name.
98
123
  *
@@ -155,6 +180,25 @@ export default class DataSchema extends Plugin {
155
180
  }
156
181
  }
157
182
  }
183
+
184
+ /**
185
+ * Updates schema definition with new properties.
186
+ *
187
+ * Creates new scheme if it doesn't exist.
188
+ * Array properties are concatenated with original values.
189
+ *
190
+ * @private
191
+ * @param {module:html-support/dataschema~DataSchemaDefinition} definition Definition update.
192
+ */
193
+ _extendDefinition( definition ) {
194
+ const currentDefinition = this._definitions.get( definition.model );
195
+
196
+ const mergedDefinition = mergeWith( {}, currentDefinition, definition, ( target, source ) => {
197
+ return Array.isArray( target ) ? target.concat( source ) : undefined;
198
+ } );
199
+
200
+ this._definitions.set( definition.model, mergedDefinition );
201
+ }
158
202
  }
159
203
 
160
204
  // Test view name against the given pattern.
@@ -203,7 +247,7 @@ function testViewName( pattern, viewName ) {
203
247
  *
204
248
  * @typedef {Object} module:html-support/dataschema~DataSchemaInlineElementDefinition
205
249
  * @property {module:engine/model/schema~AttributeProperties} [attributeProperties] Additional metadata describing the model attribute.
206
- * @property {Boolean} isInline Indicates that the definition descibes inline element.
250
+ * @property {Boolean} isInline Indicates that the definition describes inline element.
207
251
  * @property {Number} [priority] Element priority. Decides in what order elements are wrapped by
208
252
  * {@link module:engine/view/downcastwriter~DowncastWriter}.
209
253
  * Set by {@link module:html-support/dataschema~DataSchema#registerInlineElement} method.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
@@ -10,10 +10,13 @@
10
10
  import { Plugin } from 'ckeditor5/src/core';
11
11
 
12
12
  import DataFilter from './datafilter';
13
- import MediaEmbedElementSupport from './integrations/mediaembed';
14
- import TableElementSupport from './integrations/table';
15
13
  import CodeBlockElementSupport from './integrations/codeblock';
16
14
  import DualContentModelElementSupport from './integrations/dualcontent';
15
+ import HeadingElementSupport from './integrations/heading';
16
+ import ImageElementSupport from './integrations/image';
17
+ import MediaEmbedElementSupport from './integrations/mediaembed';
18
+ import ScriptElementSupport from './integrations/script';
19
+ import TableElementSupport from './integrations/table';
17
20
 
18
21
  /**
19
22
  * The General HTML Support feature.
@@ -37,10 +40,13 @@ export default class GeneralHtmlSupport extends Plugin {
37
40
  static get requires() {
38
41
  return [
39
42
  DataFilter,
40
- TableElementSupport,
41
43
  CodeBlockElementSupport,
42
44
  DualContentModelElementSupport,
43
- MediaEmbedElementSupport
45
+ HeadingElementSupport,
46
+ ImageElementSupport,
47
+ MediaEmbedElementSupport,
48
+ ScriptElementSupport,
49
+ TableElementSupport
44
50
  ];
45
51
  }
46
52
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
package/src/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import { Plugin } from 'ckeditor5/src/core';
11
- import { disallowedAttributesConverter } from '../converters';
12
11
  import { setViewAttributes } from '../conversionutils.js';
13
12
 
14
13
  import DataFilter from '../datafilter';
@@ -50,7 +49,6 @@ export default class CodeBlockElementSupport extends Plugin {
50
49
  allowAttributes: [ 'htmlAttributes', 'htmlContentAttributes' ]
51
50
  } );
52
51
 
53
- conversion.for( 'upcast' ).add( disallowedAttributesConverter( definition, dataFilter ) );
54
52
  conversion.for( 'upcast' ).add( viewToModelCodeBlockAttributeConverter( dataFilter ) );
55
53
  conversion.for( 'downcast' ).add( modelToViewCodeBlockAttributeConverter() );
56
54
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, 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
5
 
@@ -10,7 +10,6 @@
10
10
  import { Plugin } from 'ckeditor5/src/core';
11
11
  import { priorities } from 'ckeditor5/src/utils';
12
12
  import {
13
- disallowedAttributesConverter,
14
13
  modelToViewBlockAttributeConverter,
15
14
  viewToModelBlockAttributeConverter
16
15
  } from '../converters';
@@ -133,7 +132,6 @@ export default class DualContentModelElementSupport extends Plugin {
133
132
  allowAttributes: 'htmlAttributes'
134
133
  } );
135
134
 
136
- conversion.for( 'upcast' ).add( disallowedAttributesConverter( definition, dataFilter ) );
137
135
  conversion.for( 'upcast' ).add( viewToModelBlockAttributeConverter( definition, dataFilter ) );
138
136
  conversion.for( 'downcast' ).add( modelToViewBlockAttributeConverter( definition ) );
139
137
  }
@@ -0,0 +1,61 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, 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
+ /**
7
+ * @module html-support/integrations/heading
8
+ */
9
+
10
+ import { Plugin } from 'ckeditor5/src/core';
11
+
12
+ import DataSchema from '../dataschema';
13
+
14
+ /**
15
+ * Provides the General HTML Support integration with {@link module:heading/heading~Heading Heading} feature.
16
+ *
17
+ * @extends module:core/plugin~Plugin
18
+ */
19
+ export default class HeadingElementSupport extends Plugin {
20
+ /**
21
+ * @inheritDoc
22
+ */
23
+ static get requires() {
24
+ return [ DataSchema ];
25
+ }
26
+
27
+ /**
28
+ * @inheritDoc
29
+ */
30
+ init() {
31
+ const editor = this.editor;
32
+
33
+ if ( !editor.plugins.has( 'HeadingEditing' ) ) {
34
+ return;
35
+ }
36
+
37
+ const dataSchema = editor.plugins.get( DataSchema );
38
+ const options = editor.config.get( 'heading.options' );
39
+ const headerModels = [];
40
+
41
+ // We are registering all elements supported by HeadingEditing
42
+ // to enable custom attributes for those elements.
43
+ for ( const option of options ) {
44
+ if ( 'model' in option && 'view' in option ) {
45
+ dataSchema.registerBlockElement( {
46
+ view: option.view,
47
+ model: option.model
48
+ } );
49
+
50
+ headerModels.push( option.model );
51
+ }
52
+ }
53
+
54
+ dataSchema.extendBlockElement( {
55
+ model: 'htmlHgroup',
56
+ modelSchema: {
57
+ allowChildren: headerModels
58
+ }
59
+ } );
60
+ }
61
+ }
@@ -0,0 +1,193 @@
1
+ /**
2
+ * @license Copyright (c) 2003-2022, 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
+ /**
7
+ * @module html-support/integrations/image
8
+ */
9
+
10
+ import { Plugin } from 'ckeditor5/src/core';
11
+
12
+ import DataFilter from '../datafilter';
13
+ import { setViewAttributes } from '../conversionutils.js';
14
+
15
+ /**
16
+ * Provides the General HTML Support integration with the {@link module:image/image~Image Image} feature.
17
+ *
18
+ * @extends module:core/plugin~Plugin
19
+ */
20
+ export default class ImageElementSupport extends Plugin {
21
+ /**
22
+ * @inheritDoc
23
+ */
24
+ static get requires() {
25
+ return [ DataFilter ];
26
+ }
27
+
28
+ /**
29
+ * @inheritDoc
30
+ */
31
+ init() {
32
+ const editor = this.editor;
33
+
34
+ // At least one image plugin should be loaded for the integration to work properly.
35
+ if ( !editor.plugins.has( 'ImageInlineEditing' ) && !editor.plugins.has( 'ImageBlockEditing' ) ) {
36
+ return;
37
+ }
38
+
39
+ const schema = editor.model.schema;
40
+ const conversion = editor.conversion;
41
+ const dataFilter = editor.plugins.get( DataFilter );
42
+
43
+ dataFilter.on( 'register:img', ( evt, definition ) => {
44
+ if ( definition.model !== 'imageBlock' && definition.model !== 'imageInline' ) {
45
+ return;
46
+ }
47
+
48
+ if ( schema.isRegistered( 'imageBlock' ) ) {
49
+ schema.extend( 'imageBlock', {
50
+ allowAttributes: [
51
+ 'htmlAttributes',
52
+ // Figure and Link don't have model counterpart.
53
+ // We will preserve attributes on image model element using these attribute keys.
54
+ 'htmlFigureAttributes',
55
+ 'htmlLinkAttributes'
56
+ ]
57
+ } );
58
+ }
59
+
60
+ if ( schema.isRegistered( 'imageInline' ) ) {
61
+ schema.extend( 'imageInline', {
62
+ allowAttributes: [
63
+ // `htmlA` is needed for standard GHS link integration.
64
+ 'htmlA',
65
+ 'htmlAttributes'
66
+ ]
67
+ } );
68
+ }
69
+
70
+ conversion.for( 'upcast' ).add( viewToModelImageAttributeConverter( dataFilter ) );
71
+ conversion.for( 'downcast' ).add( modelToViewImageAttributeConverter() );
72
+
73
+ evt.stop();
74
+ } );
75
+ }
76
+ }
77
+
78
+ // View-to-model conversion helper preserving allowed attributes on the {@link module:image/image~Image Image}
79
+ // feature model element.
80
+ //
81
+ // @private
82
+ // @param {module:html-support/datafilter~DataFilter} dataFilter
83
+ // @returns {Function} Returns a conversion callback.
84
+ function viewToModelImageAttributeConverter( dataFilter ) {
85
+ return dispatcher => {
86
+ dispatcher.on( 'element:img', ( evt, data, conversionApi ) => {
87
+ const viewImageElement = data.viewItem;
88
+ const viewContainerElement = viewImageElement.parent;
89
+
90
+ preserveElementAttributes( viewImageElement, 'htmlAttributes' );
91
+
92
+ if ( viewContainerElement.is( 'element', 'figure' ) ) {
93
+ preserveElementAttributes( viewContainerElement, 'htmlFigureAttributes' );
94
+ } else if ( viewContainerElement.is( 'element', 'a' ) ) {
95
+ preserveLinkAttributes( viewContainerElement );
96
+ }
97
+
98
+ function preserveElementAttributes( viewElement, attributeName ) {
99
+ const viewAttributes = dataFilter._consumeAllowedAttributes( viewElement, conversionApi );
100
+
101
+ if ( viewAttributes ) {
102
+ conversionApi.writer.setAttribute( attributeName, viewAttributes, data.modelRange );
103
+ }
104
+ }
105
+
106
+ // For a block image, we want to preserve the attributes on our own.
107
+ // The inline image attributes will be handled by the GHS automatically.
108
+ function preserveLinkAttributes( viewContainerElement ) {
109
+ if ( data.modelRange && data.modelRange.getContainedElement().is( 'element', 'imageBlock' ) ) {
110
+ preserveElementAttributes( viewContainerElement, 'htmlLinkAttributes' );
111
+ }
112
+
113
+ // If we're in a link, then the `<figure>` element should be one level higher.
114
+ if ( viewContainerElement.parent.is( 'element', 'figure' ) ) {
115
+ preserveElementAttributes( viewContainerElement.parent, 'htmlFigureAttributes' );
116
+ }
117
+ }
118
+ }, { priority: 'low' } );
119
+ };
120
+ }
121
+
122
+ // A model-to-view conversion helper applying attributes from the {@link module:image/image~Image Image}
123
+ // feature.
124
+ //
125
+ // @private
126
+ // @returns {Function} Returns a conversion callback.
127
+ function modelToViewImageAttributeConverter() {
128
+ return dispatcher => {
129
+ addInlineAttributeConversion( 'htmlAttributes' );
130
+
131
+ addBlockAttributeConversion( 'img', 'htmlAttributes' );
132
+ addBlockAttributeConversion( 'figure', 'htmlFigureAttributes' );
133
+ addBlockImageLinkAttributeConversion();
134
+
135
+ function addInlineAttributeConversion( attributeName ) {
136
+ dispatcher.on( `attribute:${ attributeName }:imageInline`, ( evt, data, conversionApi ) => {
137
+ if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
138
+ return;
139
+ }
140
+
141
+ const viewElement = conversionApi.mapper.toViewElement( data.item );
142
+
143
+ setViewAttributes( conversionApi.writer, data.attributeNewValue, viewElement );
144
+ }, { priority: 'low' } );
145
+ }
146
+
147
+ function addBlockAttributeConversion( elementName, attributeName ) {
148
+ dispatcher.on( `attribute:${ attributeName }:imageBlock`, ( evt, data, conversionApi ) => {
149
+ if ( !conversionApi.consumable.consume( data.item, evt.name ) ) {
150
+ return;
151
+ }
152
+
153
+ const containerElement = conversionApi.mapper.toViewElement( data.item );
154
+ const viewElement = getDescendantElement( conversionApi.writer, containerElement, elementName );
155
+
156
+ setViewAttributes( conversionApi.writer, data.attributeNewValue, viewElement );
157
+ }, { priority: 'low' } );
158
+ }
159
+
160
+ // To have a link element in the view, we need to attach a converter to the `linkHref` attribute.
161
+ // Doing this directly on `htmlLinkAttributes` will fail, as the link wrapper is not yet called at that moment.
162
+ function addBlockImageLinkAttributeConversion( ) {
163
+ dispatcher.on( 'attribute:linkHref:imageBlock', ( evt, data, conversionApi ) => {
164
+ if ( !conversionApi.consumable.consume( data.item, 'attribute:htmlLinkAttributes:imageBlock' ) ) {
165
+ return;
166
+ }
167
+
168
+ const containerElement = conversionApi.mapper.toViewElement( data.item );
169
+ const viewElement = getDescendantElement( conversionApi.writer, containerElement, 'a' );
170
+
171
+ setViewAttributes( conversionApi.writer, data.item.getAttribute( 'htmlLinkAttributes' ), viewElement );
172
+ }, { priority: 'low' } );
173
+ }
174
+ };
175
+ }
176
+
177
+ // Returns the first view element descendant matching the given view name.
178
+ // Includes view element itself.
179
+ //
180
+ // @private
181
+ // @param {module:engine/view/downcastwriter~DowncastWriter} writer
182
+ // @param {module:engine/view/element~Element} containerElement
183
+ // @param {String} elementName
184
+ // @returns {module:engine/view/element~Element|null}
185
+ function getDescendantElement( writer, containerElement, elementName ) {
186
+ const range = writer.createRangeOn( containerElement );
187
+
188
+ for ( const { item } of range.getWalker() ) {
189
+ if ( item.is( 'element', elementName ) ) {
190
+ return item;
191
+ }
192
+ }
193
+ }