@ckeditor/ckeditor5-engine 27.1.0 → 29.2.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 (43) hide show
  1. package/LICENSE.md +1 -1
  2. package/README.md +3 -3
  3. package/package.json +22 -21
  4. package/src/controller/datacontroller.js +28 -7
  5. package/src/controller/editingcontroller.js +1 -1
  6. package/src/conversion/conversion.js +4 -4
  7. package/src/conversion/downcastdispatcher.js +6 -2
  8. package/src/conversion/downcasthelpers.js +48 -39
  9. package/src/conversion/mapper.js +1 -0
  10. package/src/conversion/modelconsumable.js +10 -5
  11. package/src/conversion/upcastdispatcher.js +6 -6
  12. package/src/conversion/upcasthelpers.js +34 -30
  13. package/src/dataprocessor/dataprocessor.jsdoc +5 -5
  14. package/src/dataprocessor/htmldataprocessor.js +38 -9
  15. package/src/dataprocessor/xmldataprocessor.js +5 -5
  16. package/src/index.js +1 -0
  17. package/src/model/element.js +3 -3
  18. package/src/model/liveposition.js +1 -1
  19. package/src/model/model.js +5 -5
  20. package/src/model/node.js +3 -3
  21. package/src/model/range.js +5 -3
  22. package/src/model/schema.js +103 -39
  23. package/src/model/selection.js +1 -1
  24. package/src/model/treewalker.js +3 -4
  25. package/src/model/utils/deletecontent.js +17 -4
  26. package/src/model/utils/insertcontent.js +15 -15
  27. package/src/model/utils/selection-post-fixer.js +1 -1
  28. package/src/view/documentselection.js +2 -2
  29. package/src/view/domconverter.js +150 -72
  30. package/src/view/downcastwriter.js +2 -1
  31. package/src/view/element.js +3 -2
  32. package/src/view/filler.js +4 -4
  33. package/src/view/matcher.js +419 -93
  34. package/src/view/observer/focusobserver.js +7 -3
  35. package/src/view/observer/mouseobserver.js +1 -1
  36. package/src/view/renderer.js +9 -1
  37. package/src/view/selection.js +2 -2
  38. package/src/view/styles/background.js +2 -0
  39. package/src/view/styles/border.js +107 -21
  40. package/src/view/styles/utils.js +1 -1
  41. package/src/view/stylesmap.js +45 -5
  42. package/src/view/upcastwriter.js +12 -11
  43. package/src/view/view.js +5 -0
package/LICENSE.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Software License Agreement
2
2
  ==========================
3
3
 
4
- **CKEditor 5 Editing Engine** – https://github.com/ckeditor/ckeditor5-engine <br>
4
+ **CKEditor 5 editing engine** – https://github.com/ckeditor/ckeditor5-engine <br>
5
5
  Copyright (c) 2003-2021, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
6
6
 
7
7
  Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
package/README.md CHANGED
@@ -2,8 +2,8 @@ CKEditor 5 editing engine
2
2
  ========================================
3
3
 
4
4
  [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-engine.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)
5
- [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-engine/status.svg)](https://david-dm.org/ckeditor/ckeditor5-engine)
6
- [![devDependency Status](https://david-dm.org/ckeditor/ckeditor5-engine/dev-status.svg)](https://david-dm.org/ckeditor/ckeditor5-engine?type=dev)
5
+ [![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5?branch=master)
6
+ [![Build Status](https://travis-ci.com/ckeditor/ckeditor5.svg?branch=master)](https://travis-ci.com/ckeditor/ckeditor5)
7
7
 
8
8
  The CKEditor 5 editing engine implements a flexible MVC-based architecture for creating rich text editing features.
9
9
 
@@ -21,7 +21,7 @@ The CKEditor 5 editing engine implements a flexible MVC-based architecture for c
21
21
 
22
22
  ## Documentation
23
23
 
24
- For a general introduction see the [Overview of CKEditor 5 Framework](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/overview.html) guide and then the [Editing engine architecture](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/architecture/editing-engine.html) guide.
24
+ For a general introduction see the [Overview of CKEditor 5 Framework](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/overview.html) guide and then the [Editing engine architecture guide](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/architecture/editing-engine.html).
25
25
 
26
26
  Additionally, refer to the [`@ckeditor/ckeditor5-engine` package](https://ckeditor.com/docs/ckeditor5/latest/api/engine.html) page in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/) for even more information.
27
27
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ckeditor/ckeditor5-engine",
3
- "version": "27.1.0",
3
+ "version": "29.2.0",
4
4
  "description": "The editing engine of CKEditor 5 – the best browser-based rich text editor.",
5
5
  "keywords": [
6
6
  "wysiwyg",
@@ -23,28 +23,28 @@
23
23
  ],
24
24
  "main": "src/index.js",
25
25
  "dependencies": {
26
- "@ckeditor/ckeditor5-utils": "^27.1.0",
26
+ "@ckeditor/ckeditor5-utils": "^29.2.0",
27
27
  "lodash-es": "^4.17.15"
28
28
  },
29
29
  "devDependencies": {
30
- "@ckeditor/ckeditor5-basic-styles": "^27.1.0",
31
- "@ckeditor/ckeditor5-block-quote": "^27.1.0",
32
- "@ckeditor/ckeditor5-clipboard": "^27.1.0",
33
- "@ckeditor/ckeditor5-core": "^27.1.0",
34
- "@ckeditor/ckeditor5-editor-classic": "^27.1.0",
35
- "@ckeditor/ckeditor5-enter": "^27.1.0",
36
- "@ckeditor/ckeditor5-essentials": "^27.1.0",
37
- "@ckeditor/ckeditor5-heading": "^27.1.0",
38
- "@ckeditor/ckeditor5-image": "^27.1.0",
39
- "@ckeditor/ckeditor5-link": "^27.1.0",
40
- "@ckeditor/ckeditor5-list": "^27.1.0",
41
- "@ckeditor/ckeditor5-paragraph": "^27.1.0",
42
- "@ckeditor/ckeditor5-table": "^27.1.0",
43
- "@ckeditor/ckeditor5-theme-lark": "^27.1.0",
44
- "@ckeditor/ckeditor5-typing": "^27.1.0",
45
- "@ckeditor/ckeditor5-ui": "^27.1.0",
46
- "@ckeditor/ckeditor5-undo": "^27.1.0",
47
- "@ckeditor/ckeditor5-widget": "^27.1.0",
30
+ "@ckeditor/ckeditor5-basic-styles": "^29.2.0",
31
+ "@ckeditor/ckeditor5-block-quote": "^29.2.0",
32
+ "@ckeditor/ckeditor5-clipboard": "^29.2.0",
33
+ "@ckeditor/ckeditor5-core": "^29.2.0",
34
+ "@ckeditor/ckeditor5-editor-classic": "^29.2.0",
35
+ "@ckeditor/ckeditor5-enter": "^29.2.0",
36
+ "@ckeditor/ckeditor5-essentials": "^29.2.0",
37
+ "@ckeditor/ckeditor5-heading": "^29.2.0",
38
+ "@ckeditor/ckeditor5-image": "^29.2.0",
39
+ "@ckeditor/ckeditor5-link": "^29.2.0",
40
+ "@ckeditor/ckeditor5-list": "^29.2.0",
41
+ "@ckeditor/ckeditor5-paragraph": "^29.2.0",
42
+ "@ckeditor/ckeditor5-table": "^29.2.0",
43
+ "@ckeditor/ckeditor5-theme-lark": "^29.2.0",
44
+ "@ckeditor/ckeditor5-typing": "^29.2.0",
45
+ "@ckeditor/ckeditor5-ui": "^29.2.0",
46
+ "@ckeditor/ckeditor5-undo": "^29.2.0",
47
+ "@ckeditor/ckeditor5-widget": "^29.2.0",
48
48
  "webpack": "^4.43.0",
49
49
  "webpack-cli": "^3.3.11"
50
50
  },
@@ -64,6 +64,7 @@
64
64
  "files": [
65
65
  "lang",
66
66
  "src",
67
- "theme"
67
+ "theme",
68
+ "ckeditor5-metadata.json"
68
69
  ]
69
70
  }
@@ -251,6 +251,7 @@ export default class DataController {
251
251
  // For document fragment, simply take the markers assigned to this document fragment.
252
252
  // For model root, all markers in that root will be taken.
253
253
  // For model element, we need to check which markers are intersecting with this element and relatively modify the markers' ranges.
254
+ // Collapsed markers at element boundary, although considered as not intersecting with the element, will also be returned.
254
255
  const markers = modelElementOrFragment.is( 'documentFragment' ) ?
255
256
  Array.from( modelElementOrFragment.markers ) :
256
257
  _getMarkersRelativeToElement( modelElementOrFragment );
@@ -347,11 +348,19 @@ export default class DataController {
347
348
  *
348
349
  * dataController.set( { main: '<p>Foo</p>', title: '<h1>Bar</h1>' } ); // Sets data on the `main` and `title` roots.
349
350
  *
351
+ * To set the data with preserved undo stacks and set the current change to this stack, use the `{ batchType: 'default' }` option.
352
+ *
353
+ * dataController.set( '<p>Foo</p>', { batchType: 'default' } ); // Sets data as a new change.
354
+ *
350
355
  * @fires set
351
356
  * @param {String|Object.<String,String>} data Input data as a string or an object containing `rootName` - `data`
352
357
  * pairs to set data on multiple roots at once.
358
+ * @param {Object} [options={}] Options for setting data.
359
+ * @param {'default'|'transparent'} [options.batchType='default'] The batch type that will be used to create a batch for the changes.
360
+ * When set to `default`, the undo and redo stacks will be preserved. Note that when not set, the undo feature (when present) will
361
+ * override it to `transparent` and all undo steps will be lost.
353
362
  */
354
- set( data ) {
363
+ set( data, options = {} ) {
355
364
  let newData = {};
356
365
 
357
366
  if ( typeof data === 'string' ) {
@@ -375,7 +384,9 @@ export default class DataController {
375
384
  throw new CKEditorError( 'datacontroller-set-non-existent-root', this );
376
385
  }
377
386
 
378
- this.model.enqueueChange( 'transparent', writer => {
387
+ const batchType = options.batchType || 'default';
388
+
389
+ this.model.enqueueChange( batchType, writer => {
379
390
  writer.setSelection( null );
380
391
  writer.removeSelectionAttribute( this.model.document.selection.getAttributeKeys() );
381
392
 
@@ -518,8 +529,9 @@ mix( DataController, ObservableMixin );
518
529
 
519
530
  // Helper function for downcast conversion.
520
531
  //
521
- // Takes a document element (element that is added to a model document) and checks which markers are inside it
522
- // and which markers are containing it. If the marker is intersecting with element, the intersection is returned.
532
+ // Takes a document element (element that is added to a model document) and checks which markers are inside it. If the marker is collapsed
533
+ // at element boundary, it is considered as contained inside the element and marker range is returned. Otherwise, if the marker is
534
+ // intersecting with the element, the intersection is returned.
523
535
  function _getMarkersRelativeToElement( element ) {
524
536
  const result = [];
525
537
  const doc = element.root.document;
@@ -531,10 +543,19 @@ function _getMarkersRelativeToElement( element ) {
531
543
  const elementRange = ModelRange._createIn( element );
532
544
 
533
545
  for ( const marker of doc.model.markers ) {
534
- const intersection = elementRange.getIntersection( marker.getRange() );
546
+ const markerRange = marker.getRange();
535
547
 
536
- if ( intersection ) {
537
- result.push( [ marker.name, intersection ] );
548
+ const isMarkerCollapsed = markerRange.isCollapsed;
549
+ const isMarkerAtElementBoundary = markerRange.start.isEqual( elementRange.start ) || markerRange.end.isEqual( elementRange.end );
550
+
551
+ if ( isMarkerCollapsed && isMarkerAtElementBoundary ) {
552
+ result.push( [ marker.name, markerRange ] );
553
+ } else {
554
+ const updatedMarkerRange = elementRange.getIntersection( markerRange );
555
+
556
+ if ( updatedMarkerRange ) {
557
+ result.push( [ marker.name, updatedMarkerRange ] );
558
+ }
538
559
  }
539
560
  }
540
561
 
@@ -104,7 +104,7 @@ export default class EditingController {
104
104
  this.downcastDispatcher.on( 'remove', remove(), { priority: 'low' } );
105
105
 
106
106
  // Attach default model selection converters.
107
- this.downcastDispatcher.on( 'selection', clearAttributes(), { priority: 'low' } );
107
+ this.downcastDispatcher.on( 'selection', clearAttributes(), { priority: 'high' } );
108
108
  this.downcastDispatcher.on( 'selection', convertRangeSelection(), { priority: 'low' } );
109
109
  this.downcastDispatcher.on( 'selection', convertCollapsedSelection(), { priority: 'low' } );
110
110
 
@@ -448,8 +448,8 @@ export default class Conversion {
448
448
  }
449
449
 
450
450
  /**
451
- * Sets up converters between the model and the view that convert a model attribute to a view attribute (and vice versa).
452
- * For example, `<image src='foo.jpg'></image>` is converted to `<img src='foo.jpg'></img>` (the same attribute key and value).
451
+ * Sets up converters between the model and the view that convert a model attribute to a view attribute (and vice versa). For example,
452
+ * `<imageBlock src='foo.jpg'></imageBlock>` is converted to `<img src='foo.jpg'></img>` (the same attribute key and value).
453
453
  * This type of converters is intended to be used with {@link module:engine/model/element~Element model element} nodes.
454
454
  * To convert text attributes {@link module:engine/conversion/conversion~Conversion#attributeToElement `attributeToElement converter`}
455
455
  * should be set up.
@@ -460,7 +460,7 @@ export default class Conversion {
460
460
  * // Attribute values are strictly specified.
461
461
  * editor.conversion.attributeToAttribute( {
462
462
  * model: {
463
- * name: 'image',
463
+ * name: 'imageInline',
464
464
  * key: 'aside',
465
465
  * values: [ 'aside' ]
466
466
  * },
@@ -476,7 +476,7 @@ export default class Conversion {
476
476
  * // Set the style attribute.
477
477
  * editor.conversion.attributeToAttribute( {
478
478
  * model: {
479
- * name: 'image',
479
+ * name: 'imageInline',
480
480
  * key: 'aside',
481
481
  * values: [ 'aside' ]
482
482
  * },
@@ -88,7 +88,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
88
88
  * // You will convert inserting a "paragraph" model element into the model.
89
89
  * downcastDispatcher.on( 'insert:paragraph', ( evt, data, conversionApi ) => {
90
90
  * // Remember to check whether the change has not been consumed yet and consume it.
91
- * if ( conversionApi.consumable.consume( data.item, 'insert' ) ) {
91
+ * if ( !conversionApi.consumable.consume( data.item, 'insert' ) ) {
92
92
  * return;
93
93
  * }
94
94
  *
@@ -341,6 +341,8 @@ export default class DowncastDispatcher {
341
341
  this.fire( 'selection', { selection }, this.conversionApi );
342
342
 
343
343
  if ( !selection.isCollapsed ) {
344
+ this._clearConversionApi();
345
+
344
346
  return;
345
347
  }
346
348
 
@@ -414,6 +416,8 @@ export default class DowncastDispatcher {
414
416
  // Do not fire events for each item inside the range if the range got consumed.
415
417
  //
416
418
  if ( !consumable.test( markerRange, eventName ) ) {
419
+ this._clearConversionApi();
420
+
417
421
  return;
418
422
  }
419
423
 
@@ -736,7 +740,7 @@ export default class DowncastDispatcher {
736
740
  * `name` is either `'$text'` if change was on {@link module:engine/model/text~Text a text node},
737
741
  * or the {@link module:engine/model/element~Element#name name} of element which attribute has changed.
738
742
  *
739
- * This way listeners can either listen to a general `attribute:bold` event or specific event (for example `attribute:src:image`).
743
+ * This way listeners can either listen to a general `attribute:bold` event or specific event (for example `attribute:src:imageBlock`).
740
744
  *
741
745
  * @event attribute
742
746
  * @param {Object} data Additional information about the change.
@@ -190,7 +190,7 @@ export default class DowncastHelpers extends ConversionHelpers {
190
190
  * Model attribute to view attribute conversion helper.
191
191
  *
192
192
  * This conversion results in adding an attribute to a view node, basing on an attribute from a model node. For example,
193
- * `<image src='foo.jpg'></image>` is converted to `<img src='foo.jpg'></img>`.
193
+ * `<imageInline src='foo.jpg'></imageInline>` is converted to `<img src='foo.jpg'></img>`.
194
194
  *
195
195
  * editor.conversion.for( 'downcast' ).attributeToAttribute( {
196
196
  * model: 'source',
@@ -205,7 +205,7 @@ export default class DowncastHelpers extends ConversionHelpers {
205
205
  *
206
206
  * editor.conversion.for( 'downcast' ).attributeToAttribute( {
207
207
  * model: {
208
- * name: 'image',
208
+ * name: 'imageInline',
209
209
  * key: 'source'
210
210
  * },
211
211
  * view: 'src'
@@ -272,8 +272,11 @@ export default class DowncastHelpers extends ConversionHelpers {
272
272
  /**
273
273
  * Model marker to view element conversion helper.
274
274
  *
275
- * **Note**: This method should be used only for editing downcast. For data downcast, use
276
- * {@link #markerToData `#markerToData()`} that produces valid HTML data.
275
+ * **Note**: This method should be used mainly for editing downcast and it is recommended
276
+ * to use {@link #markerToData `#markerToData()`} helper instead.
277
+ *
278
+ * This helper may produce invalid HTML code (e.g. a span between table cells).
279
+ * It should be used only when you are sure that the produced HTML will be semantically correct.
277
280
  *
278
281
  * This conversion results in creating a view element on the boundaries of the converted marker. If the converted marker
279
282
  * is collapsed, only one element is created. For example, model marker set like this: `<paragraph>F[oo b]ar</paragraph>`
@@ -349,8 +352,8 @@ export default class DowncastHelpers extends ConversionHelpers {
349
352
  *
350
353
  * {@link module:engine/view/containerelement~ContainerElement} may provide a custom way of handling highlight. Most often,
351
354
  * the element itself is given classes and attributes described in the highlight descriptor (instead of being wrapped in `<span>`).
352
- * For example, a model marker set like this: `[<image src="foo.jpg"></image>]` becomes `<img src="foo.jpg" class="comment"></img>`
353
- * in the view.
355
+ * For example, a model marker set like this:
356
+ * `[<imageInline src="foo.jpg"></imageInline>]` becomes `<img src="foo.jpg" class="comment"></img>` in the view.
354
357
  *
355
358
  * For container elements, the conversion is two-step. While the converter processes the highlight descriptor and passes it
356
359
  * to a container element, it is the container element instance itself that applies values from the highlight descriptor.
@@ -405,34 +408,33 @@ export default class DowncastHelpers extends ConversionHelpers {
405
408
  *
406
409
  * This conversion creates a representation for model marker boundaries in the view:
407
410
  *
408
- * * If the marker boundary is at a position where text nodes are allowed, then a view element with the specified tag name
409
- * and `name` attribute is added at this position.
410
- * * In other cases, a specified attribute is set on a view element that is before or after the marker boundary.
411
+ * * If the marker boundary is before or after a model element, a view attribute is set on a corresponding view element.
412
+ * * In other cases, a view element with the specified tag name is inserted at the corresponding view position.
411
413
  *
412
- * Typically, marker names use the `group:uniqueId:otherData` convention. For example: `comment:e34zfk9k2n459df53sjl34:zx32c`.
414
+ * Typically, the marker names use the `group:uniqueId:otherData` convention. For example: `comment:e34zfk9k2n459df53sjl34:zx32c`.
413
415
  * The default configuration for this conversion is that the first part is the `group` part and the rest of
414
416
  * the marker name becomes the `name` part.
415
417
  *
416
418
  * Tag and attribute names and values are generated from the marker name:
417
419
  *
418
- * * Templates for attributes are `data-[group]-start-before="[name]"`, `data-[group]-start-after="[name]"`,
420
+ * * The templates for attributes are `data-[group]-start-before="[name]"`, `data-[group]-start-after="[name]"`,
419
421
  * `data-[group]-end-before="[name]"` and `data-[group]-end-after="[name]"`.
420
- * * Templates for view elements are `<[group]-start name="[name]">` and `<[group]-end name="[name]">`.
422
+ * * The templates for view elements are `<[group]-start name="[name]">` and `<[group]-end name="[name]">`.
421
423
  *
422
424
  * Attributes mark whether the given marker's start or end boundary is before or after the given element.
423
- * Attributes `data-[group]-start-before` and `data-[group]-end-after` are favored.
425
+ * The `data-[group]-start-before` and `data-[group]-end-after` attributes are favored.
424
426
  * The other two are used when the former two cannot be used.
425
427
  *
426
428
  * The conversion configuration can take a function that will generate different group and name parts.
427
- * If such function is set as the `config.view` parameter, it is passed a marker name and it is expected to return an object with two
429
+ * If such a function is set as the `config.view` parameter, it is passed a marker name and it is expected to return an object with two
428
430
  * properties: `group` and `name`. If the function returns a falsy value, the conversion will not take place.
429
431
  *
430
432
  * Basic usage:
431
433
  *
432
434
  * // Using the default conversion.
433
- * // In this case, all markers whose name starts with 'comment:' will be converted.
435
+ * // In this case, all markers with names starting with 'comment:' will be converted.
434
436
  * // The `group` parameter will be set to `comment`.
435
- * // The `name` parameter will be the rest of the marker name (without `:`).
437
+ * // The `name` parameter will be the rest of the marker name (without the `:`).
436
438
  * editor.conversion.for( 'dataDowncast' ).markerToData( {
437
439
  * model: 'comment'
438
440
  * } );
@@ -442,7 +444,7 @@ export default class DowncastHelpers extends ConversionHelpers {
442
444
  *
443
445
  * // Model:
444
446
  * <paragraph>Foo[bar</paragraph>
445
- * <image src="abc.jpg"></image>]
447
+ * <imageBlock src="abc.jpg"></imageBlock>]
446
448
  *
447
449
  * // View:
448
450
  * <p>Foo<comment-start name="commentId:uid"></comment-start>bar</p>
@@ -455,7 +457,7 @@ export default class DowncastHelpers extends ConversionHelpers {
455
457
  * <p>Foo <myMarker-start></myMarker-start>bar</p>
456
458
  * <figure data-myMarker-end-after="" class="image"><img src="abc.jpg" /></figure>
457
459
  *
458
- * **Note:** A situation where some markers have the `name` part and some do not have it is incorrect and should be avoided.
460
+ * **Note:** A situation where some markers have the `name` part and some do not, is incorrect and should be avoided.
459
461
  *
460
462
  * Examples where `data-group-start-after` and `data-group-end-before` are used:
461
463
  *
@@ -499,14 +501,14 @@ export default class DowncastHelpers extends ConversionHelpers {
499
501
  *
500
502
  * This kind of conversion is useful for saving data into the database, so it should be used in the data conversion pipeline.
501
503
  *
502
- * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
503
- * to the conversion process.
504
+ * See the {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} API guide to learn how to
505
+ * add a converter to the conversion process.
504
506
  *
505
507
  * @method #markerToData
506
508
  * @param {Object} config Conversion configuration.
507
- * @param {String} config.model The name of the model marker (or model marker group) to convert.
509
+ * @param {String} config.model The name of the model marker (or the model marker group) to convert.
508
510
  * @param {Function} [config.view] A function that takes the model marker name and
509
- * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as a parameters
511
+ * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as the parameters
510
512
  * and returns an object with the `group` and `name` properties.
511
513
  * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
512
514
  * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
@@ -549,7 +551,7 @@ export function insertText() {
549
551
  */
550
552
  export function remove() {
551
553
  return ( evt, data, conversionApi ) => {
552
- // Find view range start position by mapping model position at which the remove happened.
554
+ // Find the view range start position by mapping the model position at which the remove happened.
553
555
  const viewStart = conversionApi.mapper.toViewPosition( data.position );
554
556
 
555
557
  const modelEnd = data.position.getShiftedBy( data.length );
@@ -570,7 +572,7 @@ export function remove() {
570
572
 
571
573
  /**
572
574
  * Creates a `<span>` {@link module:engine/view/attributeelement~AttributeElement view attribute element} from the information
573
- * provided by the {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor} object. If a priority
575
+ * provided by the {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor} object. If the priority
574
576
  * is not provided in the descriptor, the default priority will be used.
575
577
  *
576
578
  * @param {module:engine/view/downcastwriter~DowncastWriter} writer
@@ -584,7 +586,7 @@ export function createViewElementFromHighlightDescriptor( writer, descriptor ) {
584
586
  viewElement._addClass( descriptor.classes );
585
587
  }
586
588
 
587
- if ( descriptor.priority ) {
589
+ if ( typeof descriptor.priority === 'number' ) {
588
590
  viewElement._priority = descriptor.priority;
589
591
  }
590
592
 
@@ -945,33 +947,40 @@ function insertMarkerData( viewCreator ) {
945
947
  // Helper function for `insertMarkerData()` that marks a marker boundary at the beginning or end of given `range`.
946
948
  function handleMarkerBoundary( range, isStart, conversionApi, data, viewMarkerData ) {
947
949
  const modelPosition = isStart ? range.start : range.end;
948
- const canInsertElement = conversionApi.schema.checkChild( modelPosition, '$text' );
949
-
950
- if ( canInsertElement ) {
951
- const viewPosition = conversionApi.mapper.toViewPosition( modelPosition );
950
+ const elementAfter = modelPosition.nodeAfter && modelPosition.nodeAfter.is( 'element' ) ? modelPosition.nodeAfter : null;
951
+ const elementBefore = modelPosition.nodeBefore && modelPosition.nodeBefore.is( 'element' ) ? modelPosition.nodeBefore : null;
952
952
 
953
- insertMarkerAsElement( viewPosition, isStart, conversionApi, data, viewMarkerData );
954
- } else {
953
+ if ( elementAfter || elementBefore ) {
955
954
  let modelElement;
956
955
  let isBefore;
957
956
 
958
957
  // If possible, we want to add `data-group-start-before` and `data-group-end-after` attributes.
959
- // Below `if` is constructed in a way that will favor adding these attributes.
960
- //
961
- // Also, I assume that there will be always an element either after or before the position.
962
- // If not, then it is a case when we are not in a position where text is allowed and also there are no elements around...
963
- if ( isStart && modelPosition.nodeAfter || !isStart && !modelPosition.nodeBefore ) {
964
- modelElement = modelPosition.nodeAfter;
958
+ if ( isStart && elementAfter || !isStart && !elementBefore ) {
959
+ // [<elementAfter>...</elementAfter> -> <elementAfter data-group-start-before="...">...</elementAfter>
960
+ // <parent>]<elementAfter> -> <parent><elementAfter data-group-end-before="...">
961
+ modelElement = elementAfter;
965
962
  isBefore = true;
966
963
  } else {
967
- modelElement = modelPosition.nodeBefore;
964
+ // <elementBefore>...</elementBefore>] -> <elementBefore data-group-end-after="...">...</elementBefore>
965
+ // </elementBefore>[</parent> -> </elementBefore data-group-start-after="..."></parent>
966
+ modelElement = elementBefore;
968
967
  isBefore = false;
969
968
  }
970
969
 
971
970
  const viewElement = conversionApi.mapper.toViewElement( modelElement );
972
971
 
973
- insertMarkerAsAttribute( viewElement, isStart, isBefore, conversionApi, data, viewMarkerData );
972
+ // In rare circumstances, the model element may be not mapped to any view element and that would cause an error.
973
+ // One of those situations is a soft break inside code block.
974
+ if ( viewElement ) {
975
+ insertMarkerAsAttribute( viewElement, isStart, isBefore, conversionApi, data, viewMarkerData );
976
+
977
+ return;
978
+ }
974
979
  }
980
+
981
+ const viewPosition = conversionApi.mapper.toViewPosition( modelPosition );
982
+
983
+ insertMarkerAsElement( viewPosition, isStart, conversionApi, data, viewMarkerData );
975
984
  }
976
985
 
977
986
  // Helper function for `insertMarkerData()` that marks a marker boundary in the view as an attribute on a view element.
@@ -35,6 +35,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
35
35
  * and {@link module:engine/conversion/mapper~Mapper#toModelPosition toModelPosition} methods. `Mapper` adds it's own default callbacks
36
36
  * with `'lowest'` priority. To override default `Mapper` mapping, add custom callback with higher priority and
37
37
  * stop the event.
38
+ * @mixes module:utils/emittermixin~EmitterMixin
38
39
  */
39
40
  export default class Mapper {
40
41
  /**
@@ -38,12 +38,12 @@ import TextProxy from '../model/textproxy';
38
38
  *
39
39
  * Consuming multiple values in a single callback:
40
40
  *
41
- * // Converter for custom `image` element that might have a `caption` element inside which changes
41
+ * // Converter for custom `imageBlock` element that might have a `caption` element inside which changes
42
42
  * // how the image is displayed in the view:
43
43
  * //
44
44
  * // Model:
45
45
  * //
46
- * // [image]
46
+ * // [imageBlock]
47
47
  * // └─ [caption]
48
48
  * // └─ foo
49
49
  * //
@@ -53,8 +53,8 @@ import TextProxy from '../model/textproxy';
53
53
  * // ├─ <img />
54
54
  * // └─ <caption>
55
55
  * // └─ foo
56
- * modelConversionDispatcher.on( 'insert:image', ( evt, data, conversionApi ) => {
57
- * // First, consume the `image` element.
56
+ * modelConversionDispatcher.on( 'insert:imageBlock', ( evt, data, conversionApi ) => {
57
+ * // First, consume the `imageBlock` element.
58
58
  * conversionApi.consumable.consume( data.item, 'insert' );
59
59
  *
60
60
  * // Just create normal image element for the view.
@@ -63,7 +63,7 @@ import TextProxy from '../model/textproxy';
63
63
  * const insertPosition = conversionApi.mapper.toViewPosition( data.range.start );
64
64
  * const viewWriter = conversionApi.writer;
65
65
  *
66
- * // Check if the `image` element has children.
66
+ * // Check if the `imageBlock` element has children.
67
67
  * if ( data.item.childCount > 0 ) {
68
68
  * const modelCaption = data.item.getChild( 0 );
69
69
  *
@@ -320,5 +320,10 @@ export default class ModelConsumable {
320
320
  function _normalizeConsumableType( type ) {
321
321
  const parts = type.split( ':' );
322
322
 
323
+ // Markers are identified by the whole name (otherwise we would consume the whole markers group).
324
+ if ( parts[ 0 ] == 'addMarker' || parts[ 0 ] == 'removeMarker' ) {
325
+ return type;
326
+ }
327
+
323
328
  return parts.length > 1 ? parts[ 0 ] + ':' + parts[ 1 ] : parts[ 0 ];
324
329
  }
@@ -635,11 +635,11 @@ function createContextTree( contextDefinition, writer ) {
635
635
  *
636
636
  * Otherwise, ancestors are split.
637
637
  *
638
- * For instance, if `<image>` is not allowed in `<paragraph>` but is allowed in `$root`:
638
+ * For instance, if `<imageBlock>` is not allowed in `<paragraph>` but is allowed in `$root`:
639
639
  *
640
640
  * <paragraph>foo[]bar</paragraph>
641
641
  *
642
- * -> safe insert for `<image>` will split ->
642
+ * -> safe insert for `<imageBlock>` will split ->
643
643
  *
644
644
  * <paragraph>foo</paragraph>[]<paragraph>bar</paragraph>
645
645
  *
@@ -696,11 +696,11 @@ function createContextTree( contextDefinition, writer ) {
696
696
  *
697
697
  * Otherwise, ancestors are split and an object with a position and the copy of the split element is returned.
698
698
  *
699
- * For instance, if `<image>` is not allowed in `<paragraph>` but is allowed in `$root`:
699
+ * For instance, if `<imageBlock>` is not allowed in `<paragraph>` but is allowed in `$root`:
700
700
  *
701
701
  * <paragraph>foo[]bar</paragraph>
702
702
  *
703
- * -> split for `<image>` ->
703
+ * -> split for `<imageBlock>` ->
704
704
  *
705
705
  * <paragraph>foo</paragraph>[]<paragraph>bar</paragraph>
706
706
  *
@@ -722,8 +722,8 @@ function createContextTree( contextDefinition, writer ) {
722
722
  * Returns all the split parts of the given `element` that were created during upcasting through using {@link #splitToAllowedParent}.
723
723
  * It enables you to easily track these elements and continue processing them after they are split during the conversion of their children.
724
724
  *
725
- * <paragraph>Foo<image />bar<image />baz</paragraph> ->
726
- * <paragraph>Foo</paragraph><image /><paragraph>bar</paragraph><image /><paragraph>baz</paragraph>
725
+ * <paragraph>Foo<imageBlock />bar<imageBlock />baz</paragraph> ->
726
+ * <paragraph>Foo</paragraph><imageBlock /><paragraph>bar</paragraph><imageBlock /><paragraph>baz</paragraph>
727
727
  *
728
728
  * For a reference to any of above paragraphs, the function will return all three paragraphs (the original element included),
729
729
  * sorted in the order of their creation (the original element is the first one).