@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
@@ -18,23 +18,23 @@ import Text from './text';
18
18
  import TreeWalker from './treewalker';
19
19
 
20
20
  /**
21
- * The model's schema. It defines allowed and disallowed structures of nodes as well as nodes' attributes.
22
- * The schema is usually defined by features and based on them the editing framework and features
23
- * make decisions how to change and process the model.
21
+ * The model's schema. It defines the allowed and disallowed structures of nodes as well as nodes' attributes.
22
+ * The schema is usually defined by the features and based on them, the editing framework and features
23
+ * make decisions on how to change and process the model.
24
24
  *
25
25
  * The instance of schema is available in {@link module:engine/model/model~Model#schema `editor.model.schema`}.
26
26
  *
27
27
  * Read more about the schema in:
28
28
  *
29
- * * {@glink framework/guides/architecture/editing-engine#schema Schema} section of the
30
- * {@glink framework/guides/architecture/editing-engine Introduction to the Editing engine architecture}.
31
- * * {@glink framework/guides/deep-dive/schema Schema deep dive} guide.
29
+ * * The {@glink framework/guides/architecture/editing-engine#schema schema section} of the
30
+ * {@glink framework/guides/architecture/editing-engine Introduction to the Editing engine architecture} guide.
31
+ * * The {@glink framework/guides/deep-dive/schema Schema deep dive} guide.
32
32
  *
33
33
  * @mixes module:utils/observablemixin~ObservableMixin
34
34
  */
35
35
  export default class Schema {
36
36
  /**
37
- * Creates schema instance.
37
+ * Creates a schema instance.
38
38
  */
39
39
  constructor() {
40
40
  this._sourceDefinitions = {};
@@ -61,7 +61,7 @@ export default class Schema {
61
61
  }
62
62
 
63
63
  /**
64
- * Registers schema item. Can only be called once for every item name.
64
+ * Registers a schema item. Can only be called once for every item name.
65
65
  *
66
66
  * schema.register( 'paragraph', {
67
67
  * inheritAllFrom: '$block'
@@ -205,6 +205,7 @@ export default class Schema {
205
205
  * schema.isRegistered( 'foo' ); // -> false
206
206
  *
207
207
  * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
208
+ * @returns {Boolean}
208
209
  */
209
210
  isRegistered( item ) {
210
211
  return !!this.getDefinition( item );
@@ -220,10 +221,11 @@ export default class Schema {
220
221
  * const paragraphElement = writer.createElement( 'paragraph' );
221
222
  * schema.isBlock( paragraphElement ); // -> true
222
223
  *
223
- * See the {@glink framework/guides/deep-dive/schema#block-elements Block elements} section of the Schema deep dive
224
- * guide for more details.
224
+ * See the {@glink framework/guides/deep-dive/schema#block-elements Block elements} section of
225
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
225
226
  *
226
227
  * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
228
+ * @returns {Boolean}
227
229
  */
228
230
  isBlock( item ) {
229
231
  const def = this.getDefinition( item );
@@ -243,12 +245,13 @@ export default class Schema {
243
245
  * schema.isLimit( 'paragraph' ); // -> false
244
246
  * schema.isLimit( '$root' ); // -> true
245
247
  * schema.isLimit( editor.model.document.getRoot() ); // -> true
246
- * schema.isLimit( 'image' ); // -> true
248
+ * schema.isLimit( 'imageBlock' ); // -> true
247
249
  *
248
- * See the {@glink framework/guides/deep-dive/schema#limit-elements Limit elements} section of the Schema deep dive
249
- * guide for more details.
250
+ * See the {@glink framework/guides/deep-dive/schema#limit-elements Limit elements} section of
251
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
250
252
  *
251
253
  * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
254
+ * @returns {Boolean}
252
255
  */
253
256
  isLimit( item ) {
254
257
  const def = this.getDefinition( item );
@@ -269,15 +272,16 @@ export default class Schema {
269
272
  * was set to `true`.
270
273
  *
271
274
  * schema.isObject( 'paragraph' ); // -> false
272
- * schema.isObject( 'image' ); // -> true
275
+ * schema.isObject( 'imageBlock' ); // -> true
273
276
  *
274
- * const imageElement = writer.createElement( 'image' );
277
+ * const imageElement = writer.createElement( 'imageBlock' );
275
278
  * schema.isObject( imageElement ); // -> true
276
279
  *
277
- * See the {@glink framework/guides/deep-dive/schema#object-elements Object elements} section of the Schema deep dive
278
- * guide for more details.
280
+ * See the {@glink framework/guides/deep-dive/schema#object-elements Object elements} section of
281
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
279
282
  *
280
283
  * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
284
+ * @returns {Boolean}
281
285
  */
282
286
  isObject( item ) {
283
287
  const def = this.getDefinition( item );
@@ -301,10 +305,11 @@ export default class Schema {
301
305
  * const text = writer.createText( 'foo' );
302
306
  * schema.isInline( text ); // -> true
303
307
  *
304
- * See the {@glink framework/guides/deep-dive/schema#inline-elements Inline elements} section of the Schema deep dive
305
- * guide for more details.
308
+ * See the {@glink framework/guides/deep-dive/schema#inline-elements Inline elements} section of
309
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
306
310
  *
307
311
  * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
312
+ * @returns {Boolean}
308
313
  */
309
314
  isInline( item ) {
310
315
  const def = this.getDefinition( item );
@@ -318,16 +323,17 @@ export default class Schema {
318
323
  *
319
324
  * schema.isSelectable( 'paragraph' ); // -> false
320
325
  * schema.isSelectable( 'heading1' ); // -> false
321
- * schema.isSelectable( 'image' ); // -> true
326
+ * schema.isSelectable( 'imageBlock' ); // -> true
322
327
  * schema.isSelectable( 'tableCell' ); // -> true
323
328
  *
324
329
  * const text = writer.createText( 'foo' );
325
330
  * schema.isSelectable( text ); // -> false
326
331
  *
327
- * See the {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements} section of the Schema deep dive}
328
- * guide for more details.
332
+ * See the {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements section} of
333
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
329
334
  *
330
335
  * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
336
+ * @returns {Boolean}
331
337
  */
332
338
  isSelectable( item ) {
333
339
  const def = this.getDefinition( item );
@@ -345,16 +351,17 @@ export default class Schema {
345
351
  *
346
352
  * schema.isContent( 'paragraph' ); // -> false
347
353
  * schema.isContent( 'heading1' ); // -> false
348
- * schema.isContent( 'image' ); // -> true
354
+ * schema.isContent( 'imageBlock' ); // -> true
349
355
  * schema.isContent( 'horizontalLine' ); // -> true
350
356
  *
351
357
  * const text = writer.createText( 'foo' );
352
358
  * schema.isContent( text ); // -> true
353
359
  *
354
- * See the {@glink framework/guides/deep-dive/schema#content-elements Content elements} section of the Schema deep dive}
355
- * guide for more details.
360
+ * See the {@glink framework/guides/deep-dive/schema#content-elements Content elements section} of
361
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide for more details.
356
362
  *
357
363
  * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
364
+ * @returns {Boolean}
358
365
  */
359
366
  isContent( item ) {
360
367
  const def = this.getDefinition( item );
@@ -384,6 +391,7 @@ export default class Schema {
384
391
  * @fires checkChild
385
392
  * @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the child will be checked.
386
393
  * @param {module:engine/model/node~Node|String} def The child to check.
394
+ * @returns {Boolean}
387
395
  */
388
396
  checkChild( context, def ) {
389
397
  // Note: context and child are already normalized here to a SchemaContext and SchemaCompiledItemDefinition.
@@ -408,6 +416,7 @@ export default class Schema {
408
416
  * @fires checkAttribute
409
417
  * @param {module:engine/model/schema~SchemaContextDefinition} context The context in which the attribute will be checked.
410
418
  * @param {String} attributeName
419
+ * @returns {Boolean}
411
420
  */
412
421
  checkAttribute( context, attributeName ) {
413
422
  const def = this.getDefinition( context.last );
@@ -883,6 +892,10 @@ export default class Schema {
883
892
  compiledDefinitions[ itemName ] = compileBaseItemRule( sourceRules[ itemName ], itemName );
884
893
  }
885
894
 
895
+ for ( const itemName of itemNames ) {
896
+ compileAllowChildren( compiledDefinitions, itemName );
897
+ }
898
+
886
899
  for ( const itemName of itemNames ) {
887
900
  compileAllowContentOf( compiledDefinitions, itemName );
888
901
  }
@@ -898,6 +911,7 @@ export default class Schema {
898
911
 
899
912
  for ( const itemName of itemNames ) {
900
913
  cleanUpAllowIn( compiledDefinitions, itemName );
914
+ setupAllowChildren( compiledDefinitions, itemName );
901
915
  cleanUpAllowAttributes( compiledDefinitions, itemName );
902
916
  }
903
917
 
@@ -1090,6 +1104,7 @@ mix( Schema, ObservableMixin );
1090
1104
  * You can define the following rules:
1091
1105
  *
1092
1106
  * * {@link ~SchemaItemDefinition#allowIn `allowIn`} – Defines in which other items this item will be allowed.
1107
+ * * {@link ~SchemaItemDefinition#allowChildren `allowChildren`} – Defines which other items are allowed inside this item.
1093
1108
  * * {@link ~SchemaItemDefinition#allowAttributes `allowAttributes`} – Defines allowed attributes of the given item.
1094
1109
  * * {@link ~SchemaItemDefinition#allowContentOf `allowContentOf`} – Inherits "allowed children" from other items.
1095
1110
  * * {@link ~SchemaItemDefinition#allowWhere `allowWhere`} – Inherits "allowed in" from other items.
@@ -1112,7 +1127,7 @@ mix( Schema, ObservableMixin );
1112
1127
  * In other words, all actions that happen inside a limit element are limited to its content.
1113
1128
  * All objects are treated as limit elements, too.
1114
1129
  * * {@link ~SchemaItemDefinition#isObject `isObject`} – Whether an item is "self-contained" and should be treated as a whole.
1115
- * Examples of object elements: `image`, `table`, `video`, etc. An object is also a limit, so
1130
+ * Examples of object elements: `imageBlock`, `table`, `video`, etc. An object is also a limit, so
1116
1131
  * {@link module:engine/model/schema~Schema#isLimit `isLimit()`} returns `true` for object elements automatically.
1117
1132
  *
1118
1133
  * Read more about the meaning of these types in the
@@ -1157,21 +1172,29 @@ mix( Schema, ObservableMixin );
1157
1172
  * isBlock: true
1158
1173
  * } );
1159
1174
  *
1160
- * Make `image` a block object, which is allowed everywhere where `$block` is.
1175
+ * Allow `paragraph` inside a `$root` and allow `$text` as a `paragraph` child:
1176
+ *
1177
+ * schema.register( 'paragraph', {
1178
+ * allowIn: '$root',
1179
+ * allowChildren: '$text',
1180
+ * isBlock: true
1181
+ * } );
1182
+ *
1183
+ * Make `imageBlock` a block object, which is allowed everywhere where `$block` is.
1161
1184
  * Also, allow `src` and `alt` attributes in it:
1162
1185
  *
1163
- * schema.register( 'image', {
1186
+ * schema.register( 'imageBlock', {
1164
1187
  * allowWhere: '$block',
1165
1188
  * allowAttributes: [ 'src', 'alt' ],
1166
1189
  * isBlock: true,
1167
1190
  * isObject: true
1168
1191
  * } );
1169
1192
  *
1170
- * Make `caption` allowed in `image` and make it allow all the content of `$block`s (usually, `$text`).
1193
+ * Make `caption` allowed in `imageBlock` and make it allow all the content of `$block`s (usually, `$text`).
1171
1194
  * Also, mark it as a limit element so it cannot be split:
1172
1195
  *
1173
1196
  * schema.register( 'caption', {
1174
- * allowIn: 'image',
1197
+ * allowIn: 'imageBlock',
1175
1198
  * allowContentOf: '$block',
1176
1199
  * isLimit: true
1177
1200
  * } );
@@ -1205,6 +1228,7 @@ mix( Schema, ObservableMixin );
1205
1228
  * @typedef {Object} module:engine/model/schema~SchemaItemDefinition
1206
1229
  *
1207
1230
  * @property {String|Array.<String>} allowIn Defines in which other items this item will be allowed.
1231
+ * @property {String|Array.<String>} allowChildren Defines which other items are allowed inside this item.
1208
1232
  * @property {String|Array.<String>} allowAttributes Defines allowed attributes of the given item.
1209
1233
  * @property {String|Array.<String>} allowContentOf Inherits "allowed children" from other items.
1210
1234
  * @property {String|Array.<String>} allowWhere Inherits "allowed in" from other items.
@@ -1219,14 +1243,15 @@ mix( Schema, ObservableMixin );
1219
1243
  * Most block type items will inherit from `$block` (through `inheritAllFrom`).
1220
1244
  *
1221
1245
  * Read more about the block elements in the
1222
- * {@glink framework/guides/deep-dive/schema#block-elements Block elements} section of the Schema deep dive} guide.
1246
+ * {@glink framework/guides/deep-dive/schema#block-elements Block elements section} of
1247
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive}.
1223
1248
  *
1224
1249
  * @property {Boolean} isInline
1225
1250
  * Whether an item is "text-like" and should be treated as an inline node. Examples of inline elements:
1226
1251
  * `$text`, `softBreak` (`<br>`), etc.
1227
1252
  *
1228
1253
  * Read more about the inline elements in the
1229
- * {@glink framework/guides/deep-dive/schema#inline-elements Inline elements} section of the Schema deep dive} guide.
1254
+ * {@glink framework/guides/deep-dive/schema#inline-elements Inline elements section} of the Schema deep dive guide.
1230
1255
  *
1231
1256
  * @property {Boolean} isLimit
1232
1257
  * It can be understood as whether this element should not be split by <kbd>Enter</kbd>.
@@ -1234,36 +1259,40 @@ mix( Schema, ObservableMixin );
1234
1259
  * a limit element are limited to its content.
1235
1260
  *
1236
1261
  * Read more about the limit elements in the
1237
- * {@glink framework/guides/deep-dive/schema#limit-elements Limit elements} section of the Schema deep dive} guide.
1262
+ * {@glink framework/guides/deep-dive/schema#limit-elements Limit elements section} of
1263
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide.
1238
1264
  *
1239
1265
  * @property {Boolean} isObject
1240
1266
  * Whether an item is "self-contained" and should be treated as a whole. Examples of object elements:
1241
- * `image`, `table`, `video`, etc.
1267
+ * `imageBlock`, `table`, `video`, etc.
1242
1268
  *
1243
1269
  * **Note:** An object is also a limit, so
1244
1270
  * {@link module:engine/model/schema~Schema#isLimit `isLimit()`} returns `true` for object elements automatically.
1245
1271
  *
1246
1272
  * Read more about the object elements in the
1247
- * {@glink framework/guides/deep-dive/schema#object-elements Object elements} section of the Schema deep dive} guide.
1273
+ * {@glink framework/guides/deep-dive/schema#object-elements Object elements section} of the Schema deep dive guide.
1248
1274
  *
1249
1275
  * @property {Boolean} isSelectable
1250
- * `true` when an element should be selectable as a whole by the user. Examples of selectable elements: `image`, `table`, `tableCell`, etc.
1276
+ * `true` when an element should be selectable as a whole by the user. Examples of selectable elements: `imageBlock`, `table`, `tableCell`,
1277
+ * etc.
1251
1278
  *
1252
1279
  * **Note:** An object is also a selectable element, so
1253
1280
  * {@link module:engine/model/schema~Schema#isSelectable `isSelectable()`} returns `true` for object elements automatically.
1254
1281
  *
1255
1282
  * Read more about selectable elements in the
1256
- * {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements} section of the Schema deep dive} guide.
1283
+ * {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements section} of
1284
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide.
1257
1285
  *
1258
1286
  * @property {Boolean} isContent
1259
1287
  * An item is a content when it always finds its way to the editor data output regardless of the number and type of its descendants.
1260
- * Examples of content elements: `$text`, `image`, `table`, etc. (but not `paragraph`, `heading1` or `tableCell`).
1288
+ * Examples of content elements: `$text`, `imageBlock`, `table`, etc. (but not `paragraph`, `heading1` or `tableCell`).
1261
1289
  *
1262
1290
  * **Note:** An object is also a content element, so
1263
1291
  * {@link module:engine/model/schema~Schema#isContent `isContent()`} returns `true` for object elements automatically.
1264
1292
  *
1265
1293
  * Read more about content elements in the
1266
- * {@glink framework/guides/deep-dive/schema#content-elements Content elements} section of the Schema deep dive} guide.
1294
+ * {@glink framework/guides/deep-dive/schema#content-elements Content elements section} of
1295
+ * the {@glink framework/guides/deep-dive/schema Schema deep dive} guide.
1267
1296
  */
1268
1297
 
1269
1298
  /**
@@ -1280,6 +1309,7 @@ mix( Schema, ObservableMixin );
1280
1309
  * * The `name` property,
1281
1310
  * * The `is*` properties,
1282
1311
  * * The `allowIn` array,
1312
+ * * The `allowChildren` array,
1283
1313
  * * The `allowAttributes` array.
1284
1314
  *
1285
1315
  * @typedef {Object} module:engine/model/schema~SchemaCompiledItemDefinition
@@ -1562,6 +1592,8 @@ function compileBaseItemRule( sourceItemRules, itemName ) {
1562
1592
  allowAttributes: [],
1563
1593
  allowAttributesOf: [],
1564
1594
 
1595
+ allowChildren: [],
1596
+
1565
1597
  inheritTypesFrom: []
1566
1598
  };
1567
1599
 
@@ -1574,6 +1606,8 @@ function compileBaseItemRule( sourceItemRules, itemName ) {
1574
1606
  copyProperty( sourceItemRules, itemRule, 'allowAttributes' );
1575
1607
  copyProperty( sourceItemRules, itemRule, 'allowAttributesOf' );
1576
1608
 
1609
+ copyProperty( sourceItemRules, itemRule, 'allowChildren' );
1610
+
1577
1611
  copyProperty( sourceItemRules, itemRule, 'inheritTypesFrom' );
1578
1612
 
1579
1613
  makeInheritAllWork( sourceItemRules, itemRule );
@@ -1581,6 +1615,25 @@ function compileBaseItemRule( sourceItemRules, itemName ) {
1581
1615
  return itemRule;
1582
1616
  }
1583
1617
 
1618
+ function compileAllowChildren( compiledDefinitions, itemName ) {
1619
+ const item = compiledDefinitions[ itemName ];
1620
+
1621
+ for ( const allowChildrenItem of item.allowChildren ) {
1622
+ const allowedChildren = compiledDefinitions[ allowChildrenItem ];
1623
+
1624
+ // The allowChildren property may point to an unregistered element.
1625
+ if ( !allowedChildren ) {
1626
+ continue;
1627
+ }
1628
+
1629
+ allowedChildren.allowIn.push( itemName );
1630
+ }
1631
+
1632
+ // The allowIn property already includes correct items, reset the allowChildren property
1633
+ // to avoid duplicates later when setting up compilation results.
1634
+ item.allowChildren.length = 0;
1635
+ }
1636
+
1584
1637
  function compileAllowContentOf( compiledDefinitions, itemName ) {
1585
1638
  for ( const allowContentOfItemName of compiledDefinitions[ itemName ].allowContentOf ) {
1586
1639
  // The allowContentOf property may point to an unregistered element.
@@ -1654,6 +1707,17 @@ function cleanUpAllowIn( compiledDefinitions, itemName ) {
1654
1707
  itemRule.allowIn = Array.from( new Set( existingItems ) );
1655
1708
  }
1656
1709
 
1710
+ // Setup allowChildren items based on allowIn.
1711
+ function setupAllowChildren( compiledDefinitions, itemName ) {
1712
+ const itemRule = compiledDefinitions[ itemName ];
1713
+
1714
+ for ( const allowedParentItemName of itemRule.allowIn ) {
1715
+ const allowedParentItem = compiledDefinitions[ allowedParentItemName ];
1716
+
1717
+ allowedParentItem.allowChildren.push( itemName );
1718
+ }
1719
+ }
1720
+
1657
1721
  function cleanUpAllowAttributes( compiledDefinitions, itemName ) {
1658
1722
  const itemRule = compiledDefinitions[ itemName ];
1659
1723
 
@@ -71,7 +71,7 @@ export default class Selection {
71
71
  * // Creates backward selection.
72
72
  * const selection = writer.createSelection( range, { backward: true } );
73
73
  *
74
- * @param {module:engine/model/selection~Selectable} selectable
74
+ * @param {module:engine/model/selection~Selectable} [selectable]
75
75
  * @param {Number|'before'|'end'|'after'|'on'|'in'} [placeOrOffset] Sets place or offset of the selection.
76
76
  * @param {Object} [options]
77
77
  * @param {Boolean} [options.backward] Sets this selection instance to be backward.
@@ -382,10 +382,9 @@ function formatReturnValue( type, item, previousPosition, nextPosition, length )
382
382
  /**
383
383
  * Type of the step made by {@link module:engine/model/treewalker~TreeWalker}.
384
384
  * Possible values: `'elementStart'` if walker is at the beginning of a node, `'elementEnd'` if walker is at the end of node,
385
- * `'character'` if walker traversed over a character, or `'text'` if walker traversed over multiple characters (available in
386
- * character merging mode, see {@link module:engine/model/treewalker~TreeWalker#constructor}).
385
+ * or `'text'` if walker traversed over text.
387
386
  *
388
- * @typedef {'elementStart'|'elementEnd'|'character'|'text'} module:engine/model/treewalker~TreeWalkerValueType
387
+ * @typedef {'elementStart'|'elementEnd'|'text'} module:engine/model/treewalker~TreeWalkerValueType
389
388
  */
390
389
 
391
390
  /**
@@ -404,7 +403,7 @@ function formatReturnValue( type, item, previousPosition, nextPosition, length )
404
403
  * the position after the item.
405
404
  * * Backward iteration: For `'elementEnd'` it is last position inside element. For all other types it is the position
406
405
  * before the item.
407
- * @property {Number} [length] Length of the item. For `'elementStart'` and `'character'` it is 1. For `'text'` it is
406
+ * @property {Number} [length] Length of the item. For `'elementStart'` it is 1. For `'text'` it is
408
407
  * the length of the text. For `'elementEnd'` it is `undefined`.
409
408
  */
410
409
 
@@ -45,7 +45,7 @@ import DocumentSelection from '../documentselection';
45
45
  * @param {Boolean} [options.doNotAutoparagraph=false] Whether to create a paragraph if after content deletion selection is moved
46
46
  * to a place where text cannot be inserted.
47
47
  *
48
- * For example `<paragraph>x</paragraph>[<image src="foo.jpg"></image>]` will become:
48
+ * For example `<paragraph>x</paragraph>[<imageBlock src="foo.jpg"></imageBlock>]` will become:
49
49
  *
50
50
  * * `<paragraph>x</paragraph><paragraph>[]</paragraph>` with the option disabled (`doNotAutoparagraph == false`)
51
51
  * * `<paragraph>x</paragraph>[]` with the option enabled (`doNotAutoparagraph == true`).
@@ -53,9 +53,9 @@ import DocumentSelection from '../documentselection';
53
53
  * If you use this option you need to make sure to handle invalid selections yourself or leave
54
54
  * them to the selection post-fixer (may not always work).
55
55
  *
56
- * **Note:** if there is no valid position for the selection, the paragraph will always be created:
56
+ * **Note:** If there is no valid position for the selection, the paragraph will always be created:
57
57
  *
58
- * `[<image src="foo.jpg"></image>]` -> `<paragraph>[]</paragraph>`.
58
+ * `[<imageBlock src="foo.jpg"></imageBlock>]` -> `<paragraph>[]</paragraph>`.
59
59
  */
60
60
  export default function deleteContent( model, selection, options = {} ) {
61
61
  if ( selection.isCollapsed ) {
@@ -148,7 +148,20 @@ function getLivePositionsForSelectedBlocks( range ) {
148
148
  // This is how modifySelection works and here we are making use of it.
149
149
  model.modifySelection( selection, { direction: 'backward' } );
150
150
 
151
- endPosition = selection.getLastPosition();
151
+ const newEndPosition = selection.getLastPosition();
152
+
153
+ // For such a model and selection:
154
+ // <paragraph>A[</paragraph><imageBlock></imageBlock><paragraph>]B</paragraph>
155
+ //
156
+ // After modifySelection(), we would end up with this:
157
+ // <paragraph>A[</paragraph>]<imageBlock></imageBlock><paragraph>B</paragraph>
158
+ //
159
+ // So we need to check if there is no content in the skipped range (because we want to include the <imageBlock>).
160
+ const skippedRange = model.createRange( newEndPosition, endPosition );
161
+
162
+ if ( !model.hasContent( skippedRange, { ignoreMarkers: true } ) ) {
163
+ endPosition = newEndPosition;
164
+ }
152
165
  }
153
166
  }
154
167
 
@@ -698,7 +698,7 @@ class Insertion {
698
698
  // Do not autoparagraph if the paragraph won't be allowed there,
699
699
  // cause that would lead to an infinite loop. The paragraph would be rejected in
700
700
  // the next _handleNode() call and we'd be here again.
701
- if ( this._getAllowedIn( paragraph, this.position.parent ) && this.schema.checkChild( paragraph, node ) ) {
701
+ if ( this._getAllowedIn( this.position.parent, paragraph ) && this.schema.checkChild( paragraph, node ) ) {
702
702
  paragraph._appendChild( node );
703
703
  this._handleNode( paragraph );
704
704
  }
@@ -747,7 +747,7 @@ class Insertion {
747
747
  * `false` is returned if the node isn't allowed at any position up in the tree, `true` if was.
748
748
  */
749
749
  _checkAndSplitToAllowedPosition( node ) {
750
- const allowedIn = this._getAllowedIn( node, this.position.parent );
750
+ const allowedIn = this._getAllowedIn( this.position.parent, node );
751
751
 
752
752
  if ( !allowedIn ) {
753
753
  return false;
@@ -759,11 +759,6 @@ class Insertion {
759
759
  }
760
760
 
761
761
  while ( allowedIn != this.position.parent ) {
762
- // If a parent which we'd need to leave is a limit element, break.
763
- if ( this.schema.isLimit( this.position.parent ) ) {
764
- return false;
765
- }
766
-
767
762
  if ( this.position.isAtStart ) {
768
763
  // If insertion position is at the beginning of the parent, move it out instead of splitting.
769
764
  // <p>^Foo</p> -> ^<p>Foo</p>
@@ -806,19 +801,24 @@ class Insertion {
806
801
  * Gets the element in which the given node is allowed. It checks the passed element and all its ancestors.
807
802
  *
808
803
  * @private
809
- * @param {module:engine/model/node~Node} node The node to check.
810
- * @param {module:engine/model/element~Element} element The element in which the node's correctness should be checked.
804
+ * @param {module:engine/model/element~Element} contextElement The element in which context the node should be checked.
805
+ * @param {module:engine/model/node~Node} childNode The node to check.
811
806
  * @returns {module:engine/model/element~Element|null}
812
807
  */
813
- _getAllowedIn( node, element ) {
814
- if ( this.schema.checkChild( element, node ) ) {
815
- return element;
808
+ _getAllowedIn( contextElement, childNode ) {
809
+ if ( this.schema.checkChild( contextElement, childNode ) ) {
810
+ return contextElement;
816
811
  }
817
812
 
818
- if ( element.parent ) {
819
- return this._getAllowedIn( node, element.parent );
813
+ // If the child wasn't allowed in the context element and the element is a limit there's no point in
814
+ // checking any further towards the root. This is it: the limit is unsplittable and there's nothing
815
+ // we can do about it. Without this check, the algorithm will analyze parent of the limit and may create
816
+ // an illusion of the child being allowed. There's no way to insert it down there, though. It results in
817
+ // infinite loops.
818
+ if ( this.schema.isLimit( contextElement ) ) {
819
+ return null;
820
820
  }
821
821
 
822
- return null;
822
+ return this._getAllowedIn( contextElement.parent, childNode );
823
823
  }
824
824
  }
@@ -24,7 +24,7 @@ import Position from '../position';
24
24
  * boundary (a range must be rooted within one limit element).
25
25
  * * Only {@link module:engine/model/schema~Schema#isSelectable selectable elements} can be selected from the outside
26
26
  * (e.g. `[<paragraph>foo</paragraph>]` is invalid). This rule applies independently to both selection ends, so this
27
- * selection is correct: `<paragraph>f[oo</paragraph><image></image>]`.
27
+ * selection is correct: `<paragraph>f[oo</paragraph><imageBlock></imageBlock>]`.
28
28
  *
29
29
  * If the position is not correct, the post-fixer will automatically correct it.
30
30
  *
@@ -98,7 +98,7 @@ export default class DocumentSelection {
98
98
  * Returns true if selection instance is marked as `fake`.
99
99
  *
100
100
  * @see #_setTo
101
- * @returns {Boolean}
101
+ * @type {Boolean}
102
102
  */
103
103
  get isFake() {
104
104
  return this._selection.isFake;
@@ -108,7 +108,7 @@ export default class DocumentSelection {
108
108
  * Returns fake selection label.
109
109
  *
110
110
  * @see #_setTo
111
- * @returns {String}
111
+ * @type {String}
112
112
  */
113
113
  get fakeSelectionLabel() {
114
114
  return this._selection.fakeSelectionLabel;