@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
@@ -7,10 +7,11 @@
7
7
  * @module engine/view/domconverter
8
8
  */
9
9
 
10
- /* globals document, Node, NodeFilter, Text */
10
+ /* globals document, Node, Text */
11
11
 
12
12
  import ViewText from './text';
13
13
  import ViewElement from './element';
14
+ import ViewUIElement from './uielement';
14
15
  import ViewPosition from './position';
15
16
  import ViewRange from './range';
16
17
  import ViewSelection from './selection';
@@ -25,9 +26,7 @@ import {
25
26
  import global from '@ckeditor/ckeditor5-utils/src/dom/global';
26
27
  import indexOf from '@ckeditor/ckeditor5-utils/src/dom/indexof';
27
28
  import getAncestors from '@ckeditor/ckeditor5-utils/src/dom/getancestors';
28
- import getCommonAncestor from '@ckeditor/ckeditor5-utils/src/dom/getcommonancestor';
29
29
  import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
30
- import { isElement } from 'lodash-es';
31
30
 
32
31
  const BR_FILLER_REF = BR_FILLER( document ); // eslint-disable-line new-cap
33
32
  const NBSP_FILLER_REF = NBSP_FILLER( document ); // eslint-disable-line new-cap
@@ -87,7 +86,29 @@ export default class DomConverter {
87
86
  * @readonly
88
87
  * @member {Array.<String>} module:engine/view/domconverter~DomConverter#blockElements
89
88
  */
90
- this.blockElements = [ 'p', 'div', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'li', 'dd', 'dt', 'figcaption', 'td', 'th' ];
89
+ this.blockElements = [
90
+ 'address', 'article', 'aside', 'blockquote', 'caption', 'center', 'dd', 'details', 'dir', 'div',
91
+ 'dl', 'dt', 'fieldset', 'figcaption', 'figure', 'footer', 'form', 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'header',
92
+ 'hgroup', 'legend', 'li', 'main', 'menu', 'nav', 'ol', 'p', 'pre', 'section', 'summary', 'table', 'tbody',
93
+ 'td', 'tfoot', 'th', 'thead', 'tr', 'ul'
94
+ ];
95
+
96
+ /**
97
+ * A list of elements that exist inline (in text) but their inner structure cannot be edited because
98
+ * of the way they are rendered by the browser. They are mostly HTML form elements but there are other
99
+ * elements such as `<img>` or `<iframe>` that also have non-editable children or no children whatsoever.
100
+ *
101
+ * Whether an element is considered an inline object has an impact on white space rendering (trimming)
102
+ * around (and inside of it). In short, white spaces in text nodes next to inline objects are not trimmed.
103
+ *
104
+ * You can extend this array if you introduce support for inline object elements which are not yet recognized here.
105
+ *
106
+ * @readonly
107
+ * @member {Array.<String>} module:engine/view/domconverter~DomConverter#inlineObjectElements
108
+ */
109
+ this.inlineObjectElements = [
110
+ 'object', 'iframe', 'input', 'button', 'textarea', 'select', 'option', 'video', 'embed', 'audio', 'img', 'canvas'
111
+ ];
91
112
 
92
113
  /**
93
114
  * The DOM-to-view mapping.
@@ -232,8 +253,12 @@ export default class DomConverter {
232
253
  this.bindDocumentFragments( domElement, viewNode );
233
254
  }
234
255
  } else if ( viewNode.is( 'uiElement' ) ) {
235
- // UIElement has its own render() method (see #799).
236
- domElement = viewNode.render( domDocument );
256
+ if ( viewNode.name === '$comment' ) {
257
+ domElement = domDocument.createComment( viewNode.getCustomProperty( '$rawContent' ) );
258
+ } else {
259
+ // UIElement has its own render() method (see #799).
260
+ domElement = viewNode.render( domDocument );
261
+ }
237
262
 
238
263
  if ( options.bind ) {
239
264
  this.bindElements( domElement, viewNode );
@@ -401,7 +426,9 @@ export default class DomConverter {
401
426
  * @param {Object} [options] Conversion options.
402
427
  * @param {Boolean} [options.bind=false] Determines whether new elements will be bound.
403
428
  * @param {Boolean} [options.withChildren=true] If `true`, node's and document fragment's children will be converted too.
404
- * @param {Boolean} [options.keepOriginalCase=false] If `false`, node's tag name will be converter to lower case.
429
+ * @param {Boolean} [options.keepOriginalCase=false] If `false`, node's tag name will be converted to lower case.
430
+ * @param {Boolean} [options.skipComments=false] If `false`, comment nodes will be converted to `$comment`
431
+ * {@link module:engine/view/uielement~UIElement view UI elements}.
405
432
  * @returns {module:engine/view/node~Node|module:engine/view/documentfragment~DocumentFragment|null} Converted node or document fragment
406
433
  * or `null` if DOM node is a {@link module:engine/view/filler filler} or the given node is an empty text node.
407
434
  */
@@ -417,6 +444,10 @@ export default class DomConverter {
417
444
  return hostElement;
418
445
  }
419
446
 
447
+ if ( this.isComment( domNode ) && options.skipComments ) {
448
+ return null;
449
+ }
450
+
420
451
  if ( isText( domNode ) ) {
421
452
  if ( isInlineFiller( domNode ) ) {
422
453
  return null;
@@ -425,8 +456,6 @@ export default class DomConverter {
425
456
 
426
457
  return textData === '' ? null : new ViewText( this.document, textData );
427
458
  }
428
- } else if ( this.isComment( domNode ) ) {
429
- return null;
430
459
  } else {
431
460
  if ( this.mapDomToView( domNode ) ) {
432
461
  return this.mapDomToView( domNode );
@@ -443,8 +472,7 @@ export default class DomConverter {
443
472
  }
444
473
  } else {
445
474
  // Create view element.
446
- const viewName = options.keepOriginalCase ? domNode.tagName : domNode.tagName.toLowerCase();
447
- viewElement = new ViewElement( this.document, viewName );
475
+ viewElement = this._createViewElement( domNode, options );
448
476
 
449
477
  if ( options.bind ) {
450
478
  this.bindElements( domNode, viewElement );
@@ -453,13 +481,18 @@ export default class DomConverter {
453
481
  // Copy element's attributes.
454
482
  const attrs = domNode.attributes;
455
483
 
456
- for ( let i = attrs.length - 1; i >= 0; i-- ) {
457
- viewElement._setAttribute( attrs[ i ].name, attrs[ i ].value );
484
+ if ( attrs ) {
485
+ for ( let i = attrs.length - 1; i >= 0; i-- ) {
486
+ viewElement._setAttribute( attrs[ i ].name, attrs[ i ].value );
487
+ }
458
488
  }
459
489
 
460
490
  // Treat this element's content as a raw data if it was registered as such.
461
- if ( options.withChildren !== false && this._rawContentElementMatcher.match( viewElement ) ) {
462
- viewElement._setCustomProperty( '$rawContent', domNode.innerHTML );
491
+ // Comment node is also treated as an element with raw data.
492
+ if ( this._isViewElementWithRawContent( viewElement, options ) || this.isComment( domNode ) ) {
493
+ const rawContent = this.isComment( domNode ) ? domNode.data : domNode.innerHTML;
494
+
495
+ viewElement._setCustomProperty( '$rawContent', rawContent );
463
496
 
464
497
  // Store a DOM node to prevent left trimming of the following text node.
465
498
  this._encounteredRawContentDomNodes.add( domNode );
@@ -950,7 +983,7 @@ export default class DomConverter {
950
983
  }
951
984
 
952
985
  /**
953
- * Returns block {@link module:engine/view/filler filler} node based on current {@link #blockFillerMode} setting.
986
+ * Returns the block {@link module:engine/view/filler filler} node based on the current {@link #blockFillerMode} setting.
954
987
  *
955
988
  * @private
956
989
  * @params {Document} domDocument
@@ -1029,8 +1062,8 @@ export default class DomConverter {
1029
1062
  // 1. Replace the first space with a nbsp if the previous node ends with a space or there is no previous node
1030
1063
  // (container element boundary).
1031
1064
  if ( data.charAt( 0 ) == ' ' ) {
1032
- const prevNode = this._getTouchingViewTextNode( node, false );
1033
- const prevEndsWithSpace = prevNode && this._nodeEndsWithSpace( prevNode );
1065
+ const prevNode = this._getTouchingInlineViewNode( node, false );
1066
+ const prevEndsWithSpace = prevNode && prevNode.is( '$textProxy' ) && this._nodeEndsWithSpace( prevNode );
1034
1067
 
1035
1068
  if ( prevEndsWithSpace || !prevNode ) {
1036
1069
  data = '\u00A0' + data.substr( 1 );
@@ -1047,9 +1080,10 @@ export default class DomConverter {
1047
1080
  //
1048
1081
  // More here: https://github.com/ckeditor/ckeditor5-engine/issues/1747.
1049
1082
  if ( data.charAt( data.length - 1 ) == ' ' ) {
1050
- const nextNode = this._getTouchingViewTextNode( node, true );
1083
+ const nextNode = this._getTouchingInlineViewNode( node, true );
1084
+ const nextStartsWithSpace = nextNode && nextNode.is( '$textProxy' ) && nextNode.data.charAt( 0 ) == ' ';
1051
1085
 
1052
- if ( data.charAt( data.length - 2 ) == ' ' || !nextNode || nextNode.data.charAt( 0 ) == ' ' ) {
1086
+ if ( data.charAt( data.length - 2 ) == ' ' || !nextNode || nextStartsWithSpace ) {
1053
1087
  data = data.substr( 0, data.length - 1 ) + '\u00A0';
1054
1088
  }
1055
1089
  }
@@ -1136,14 +1170,17 @@ export default class DomConverter {
1136
1170
  // ` \u00A0` to ensure proper rendering. Since here we convert back, we recognize those pairs and change them back to ` `.
1137
1171
  data = data.replace( / \u00A0/g, ' ' );
1138
1172
 
1173
+ const isNextNodeInlineObjectElement = nextNode && this.isElement( nextNode ) && nextNode.tagName != 'BR';
1174
+ const isNextNodeStartingWithSpace = nextNode && isText( nextNode ) && nextNode.data.charAt( 0 ) == ' ';
1175
+
1139
1176
  // Then, let's change the last nbsp to a space.
1140
- if ( /( |\u00A0)\u00A0$/.test( data ) || !nextNode || ( nextNode.data && nextNode.data.charAt( 0 ) == ' ' ) ) {
1177
+ if ( /( |\u00A0)\u00A0$/.test( data ) || !nextNode || isNextNodeInlineObjectElement || isNextNodeStartingWithSpace ) {
1141
1178
  data = data.replace( /\u00A0$/, ' ' );
1142
1179
  }
1143
1180
 
1144
1181
  // Then, change &nbsp; character that is at the beginning of the text node to space character.
1145
1182
  // We do that replacement only if this is the first node or the previous node ends on whitespace character.
1146
- if ( shouldLeftTrim ) {
1183
+ if ( shouldLeftTrim || prevNode && this.isElement( prevNode ) && prevNode.tagName != 'BR' ) {
1147
1184
  data = data.replace( /^\u00A0/, ' ' );
1148
1185
  }
1149
1186
 
@@ -1158,15 +1195,15 @@ export default class DomConverter {
1158
1195
  *
1159
1196
  * @private
1160
1197
  * @param {Node} node
1161
- * @param {Node} prevNode
1198
+ * @param {Node} prevNode Either DOM text or `<br>` or one of `#inlineObjectElements`.
1162
1199
  */
1163
1200
  _checkShouldLeftTrimDomText( node, prevNode ) {
1164
1201
  if ( !prevNode ) {
1165
1202
  return true;
1166
1203
  }
1167
1204
 
1168
- if ( isElement( prevNode ) ) {
1169
- return true;
1205
+ if ( this.isElement( prevNode ) ) {
1206
+ return prevNode.tagName === 'BR';
1170
1207
  }
1171
1208
 
1172
1209
  // Shouldn't left trim if previous node is a node that was encountered as a raw content node.
@@ -1183,7 +1220,7 @@ export default class DomConverter {
1183
1220
  *
1184
1221
  * @private
1185
1222
  * @param {Node} node
1186
- * @param {Node} nextNode
1223
+ * @param {Node} nextNode Either DOM text or `<br>` or one of `#inlineObjectElements`.
1187
1224
  */
1188
1225
  _checkShouldRightTrimDomText( node, nextNode ) {
1189
1226
  if ( nextNode ) {
@@ -1200,18 +1237,23 @@ export default class DomConverter {
1200
1237
  * @private
1201
1238
  * @param {module:engine/view/text~Text} node Reference node.
1202
1239
  * @param {Boolean} getNext
1203
- * @returns {module:engine/view/text~Text|null} Touching text node or `null` if there is no next or previous touching text node.
1240
+ * @returns {module:engine/view/text~Text|module:engine/view/element~Element|null} Touching text node, an inline object
1241
+ * or `null` if there is no next or previous touching text node.
1204
1242
  */
1205
- _getTouchingViewTextNode( node, getNext ) {
1243
+ _getTouchingInlineViewNode( node, getNext ) {
1206
1244
  const treeWalker = new ViewTreeWalker( {
1207
1245
  startPosition: getNext ? ViewPosition._createAfter( node ) : ViewPosition._createBefore( node ),
1208
1246
  direction: getNext ? 'forward' : 'backward'
1209
1247
  } );
1210
1248
 
1211
1249
  for ( const value of treeWalker ) {
1250
+ // Found an inline object (for example an image).
1251
+ if ( value.item.is( 'element' ) && this.inlineObjectElements.includes( value.item.name ) ) {
1252
+ return value.item;
1253
+ }
1212
1254
  // ViewContainerElement is found on a way to next ViewText node, so given `node` was first/last
1213
1255
  // text node in its container element.
1214
- if ( value.item.is( 'containerElement' ) ) {
1256
+ else if ( value.item.is( 'containerElement' ) ) {
1215
1257
  return null;
1216
1258
  }
1217
1259
  // <br> found – it works like a block boundary, so do not scan further.
@@ -1229,10 +1271,11 @@ export default class DomConverter {
1229
1271
 
1230
1272
  /**
1231
1273
  * Helper function. For the given text node, it finds the closest touching node which is either
1232
- * a text node or a `<br>`. The search is terminated at block element boundaries and if a matching node
1233
- * wasn't found so far, `null` is returned.
1274
+ * a text, `<br>` or an {@link #inlineObjectElements inline object}.
1234
1275
  *
1235
- * In the following DOM structure:
1276
+ * If no such node is found, `null` is returned.
1277
+ *
1278
+ * For instance, in the following DOM structure:
1236
1279
  *
1237
1280
  * <p>foo<b>bar</b><br>bom</p>
1238
1281
  *
@@ -1253,45 +1296,82 @@ export default class DomConverter {
1253
1296
  return null;
1254
1297
  }
1255
1298
 
1256
- const direction = getNext ? 'nextNode' : 'previousNode';
1257
- const document = node.ownerDocument;
1258
- const topmostParent = getAncestors( node )[ 0 ];
1299
+ const stepInto = getNext ? 'firstChild' : 'lastChild';
1300
+ const stepOver = getNext ? 'nextSibling' : 'previousSibling';
1259
1301
 
1260
- const treeWalker = document.createTreeWalker( topmostParent, NodeFilter.SHOW_TEXT | NodeFilter.SHOW_ELEMENT, {
1261
- acceptNode( node ) {
1262
- if ( isText( node ) ) {
1263
- return NodeFilter.FILTER_ACCEPT;
1264
- }
1302
+ let skipChildren = true;
1265
1303
 
1266
- if ( node.tagName == 'BR' ) {
1267
- return NodeFilter.FILTER_ACCEPT;
1268
- }
1304
+ do {
1305
+ if ( !skipChildren && node[ stepInto ] ) {
1306
+ node = node[ stepInto ];
1307
+ } else if ( node[ stepOver ] ) {
1308
+ node = node[ stepOver ];
1309
+ skipChildren = false;
1310
+ } else {
1311
+ node = node.parentNode;
1312
+ skipChildren = true;
1313
+ }
1269
1314
 
1270
- return NodeFilter.FILTER_SKIP;
1315
+ if ( !node || this._isBlockElement( node ) ) {
1316
+ return null;
1271
1317
  }
1272
- } );
1318
+ } while (
1319
+ !( isText( node ) || node.tagName == 'BR' || this._isInlineObjectElement( node ) )
1320
+ );
1273
1321
 
1274
- treeWalker.currentNode = node;
1322
+ return node;
1323
+ }
1275
1324
 
1276
- const touchingNode = treeWalker[ direction ]();
1325
+ /**
1326
+ * Returns `true` if a DOM node belongs to {@link #blockElements}. `false` otherwise.
1327
+ *
1328
+ * @private
1329
+ * @param {Node} node
1330
+ * @returns {Boolean}
1331
+ */
1332
+ _isBlockElement( node ) {
1333
+ return this.isElement( node ) && this.blockElements.includes( node.tagName.toLowerCase() );
1334
+ }
1277
1335
 
1278
- if ( touchingNode !== null ) {
1279
- const lca = getCommonAncestor( node, touchingNode );
1336
+ /**
1337
+ * Returns `true` if a DOM node belongs to {@link #inlineObjectElements}. `false` otherwise.
1338
+ *
1339
+ * @private
1340
+ * @param {Node} node
1341
+ * @returns {Boolean}
1342
+ */
1343
+ _isInlineObjectElement( node ) {
1344
+ return this.isElement( node ) && this.inlineObjectElements.includes( node.tagName.toLowerCase() );
1345
+ }
1280
1346
 
1281
- // If there is common ancestor between the text node and next/prev text node,
1282
- // and there are no block elements on a way from the text node to that ancestor,
1283
- // and there are no block elements on a way from next/prev text node to that ancestor...
1284
- if (
1285
- lca &&
1286
- !_hasDomParentOfType( node, this.blockElements, lca ) &&
1287
- !_hasDomParentOfType( touchingNode, this.blockElements, lca )
1288
- ) {
1289
- // Then they are in the same container element.
1290
- return touchingNode;
1291
- }
1347
+ /**
1348
+ * Creates view element basing on the node type.
1349
+ *
1350
+ * @private
1351
+ * @param {Node} node DOM node to check.
1352
+ * @param {Object} options Conversion options. See {@link module:engine/view/domconverter~DomConverter#domToView} options parameter.
1353
+ * @returns {Element}
1354
+ */
1355
+ _createViewElement( node, options ) {
1356
+ if ( this.isComment( node ) ) {
1357
+ return new ViewUIElement( this.document, '$comment' );
1292
1358
  }
1293
1359
 
1294
- return null;
1360
+ const viewName = options.keepOriginalCase ? node.tagName : node.tagName.toLowerCase();
1361
+
1362
+ return new ViewElement( this.document, viewName );
1363
+ }
1364
+
1365
+ /**
1366
+ * Checks if view element's content should be treated as a raw data.
1367
+ *
1368
+ * @private
1369
+ * @param {Element} viewElement View element to check.
1370
+ * @param {Object} options Conversion options. See {@link module:engine/view/domconverter~DomConverter#domToView} options parameter.
1371
+ * @returns {Boolean}
1372
+ */
1373
+ _isViewElementWithRawContent( viewElement, options ) {
1374
+ return options.withChildren !== false && this._rawContentElementMatcher.match( viewElement );
1295
1375
  }
1296
1376
  }
1297
1377
 
@@ -1300,14 +1380,9 @@ export default class DomConverter {
1300
1380
  //
1301
1381
  // @param {Node} node
1302
1382
  // @param {Array.<String>} types
1303
- // @param {Boolean} [boundaryParent] Can be given if parents should be checked up to a given element (excluding that element).
1304
1383
  // @returns {Boolean} `true` if such parent exists or `false` if it does not.
1305
- function _hasDomParentOfType( node, types, boundaryParent ) {
1306
- let parents = getAncestors( node );
1307
-
1308
- if ( boundaryParent ) {
1309
- parents = parents.slice( parents.indexOf( boundaryParent ) + 1 );
1310
- }
1384
+ function _hasDomParentOfType( node, types ) {
1385
+ const parents = getAncestors( node );
1311
1386
 
1312
1387
  return parents.some( parent => parent.tagName && types.includes( parent.tagName.toLowerCase() ) );
1313
1388
  }
@@ -1329,6 +1404,7 @@ function forEachDomNodeAncestor( node, callback ) {
1329
1404
  // A &nbsp; is a block filler only if it is a single child of a block element.
1330
1405
  //
1331
1406
  // @param {Node} domNode DOM node.
1407
+ // @param {Array.<String>} blockElements
1332
1408
  // @returns {Boolean}
1333
1409
  function isNbspBlockFiller( domNode, blockElements ) {
1334
1410
  const isNBSP = domNode.isEqualNode( NBSP_FILLER_REF );
@@ -1339,6 +1415,7 @@ function isNbspBlockFiller( domNode, blockElements ) {
1339
1415
  // Checks if domNode has block parent.
1340
1416
  //
1341
1417
  // @param {Node} domNode DOM node.
1418
+ // @param {Array.<String>} blockElements
1342
1419
  // @returns {Boolean}
1343
1420
  function hasBlockParent( domNode, blockElements ) {
1344
1421
  const parent = domNode.parentNode;
@@ -1347,13 +1424,14 @@ function hasBlockParent( domNode, blockElements ) {
1347
1424
  }
1348
1425
 
1349
1426
  /**
1350
- * Enum representing type of the block filler.
1427
+ * Enum representing the type of the block filler.
1351
1428
  *
1352
1429
  * Possible values:
1353
1430
  *
1354
- * * `br` - for `<br data-cke-filler="true">` block filler used in the editing view,
1355
- * * `nbsp` - for `&nbsp;` block fillers used in the data,
1356
- * * `markedNbsp` - for nbsp block fillers wrapped in a span: `<span data-cke-filler="true">&nbsp;</span>` used in the data.
1431
+ * * `br` &ndash; For the `<br data-cke-filler="true">` block filler used in the editing view.
1432
+ * * `nbsp` &ndash; For the `&nbsp;` block fillers used in the data.
1433
+ * * `markedNbsp` &ndash; For the `&nbsp;` block fillers wrapped in `<span>` elements: `<span data-cke-filler="true">&nbsp;</span>`
1434
+ * used in the data.
1357
1435
  *
1358
1436
  * @typedef {String} module:engine/view/filler~BlockFillerMode
1359
1437
  */
@@ -191,7 +191,7 @@ export default class DowncastWriter {
191
191
  createAttributeElement( name, attributes, options = {} ) {
192
192
  const attributeElement = new AttributeElement( this.document, name, attributes );
193
193
 
194
- if ( options.priority ) {
194
+ if ( typeof options.priority === 'number' ) {
195
195
  attributeElement._priority = options.priority;
196
196
  }
197
197
 
@@ -1075,6 +1075,7 @@ export default class DowncastWriter {
1075
1075
  * @param {module:engine/view/item~Item|module:engine/model/position~Position} itemOrPosition
1076
1076
  * @param {Number|'end'|'before'|'after'} [offset] Offset or one of the flags. Used only when
1077
1077
  * first parameter is a {@link module:engine/view/item~Item view item}.
1078
+ * @returns {module:engine/view/position~Position}
1078
1079
  */
1079
1080
  createPositionAt( itemOrPosition, offset ) {
1080
1081
  return Position._createAt( itemOrPosition, offset );
@@ -468,10 +468,11 @@ export default class Element extends Node {
468
468
  /**
469
469
  * Returns iterator that contains all style names.
470
470
  *
471
+ * @param {Boolean} [expand=false] Expand shorthand style properties and return all equivalent style representations.
471
472
  * @returns {Iterable.<String>}
472
473
  */
473
- getStyleNames() {
474
- return this._styles.getStyleNames();
474
+ getStyleNames( expand = false ) {
475
+ return this._styles.getStyleNames( expand );
475
476
  }
476
477
 
477
478
  /**
@@ -7,7 +7,7 @@ import { keyCodes } from '@ckeditor/ckeditor5-utils/src/keyboard';
7
7
  import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
8
8
 
9
9
  /**
10
- * Set of utils related to block and inline fillers handling.
10
+ * Set of utilities related to handling block and inline fillers.
11
11
  *
12
12
  * Browsers do not allow to put caret in elements which does not have height. Because of it, we need to fill all
13
13
  * empty elements which should be selectable with elements or characters called "fillers". Unfortunately there is no one
@@ -36,7 +36,7 @@ import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
36
36
  */
37
37
 
38
38
  /**
39
- * Non-breaking space filler creator. This is a function which creates `&nbsp;` text node.
39
+ * Non-breaking space filler creator. This function creates the `&nbsp;` text node.
40
40
  * It defines how the filler is created.
41
41
  *
42
42
  * @see module:engine/view/filler~MARKED_NBSP_FILLER
@@ -46,7 +46,7 @@ import isText from '@ckeditor/ckeditor5-utils/src/dom/istext';
46
46
  export const NBSP_FILLER = domDocument => domDocument.createTextNode( '\u00A0' );
47
47
 
48
48
  /**
49
- * Marked non-breaking space filler creator. This is a function which creates `<span data-cke-filler="true">&nbsp;</span>` element.
49
+ * Marked non-breaking space filler creator. This function creates the `<span data-cke-filler="true">&nbsp;</span>` element.
50
50
  * It defines how the filler is created.
51
51
  *
52
52
  * @see module:engine/view/filler~NBSP_FILLER
@@ -62,7 +62,7 @@ export const MARKED_NBSP_FILLER = domDocument => {
62
62
  };
63
63
 
64
64
  /**
65
- * `<br>` filler creator. This is a function which creates `<br data-cke-filler="true">` element.
65
+ * `<br>` filler creator. This function creates the `<br data-cke-filler="true">` element.
66
66
  * It defines how the filler is created.
67
67
  *
68
68
  * @see module:engine/view/filler~NBSP_FILLER