8
0
Просмотр исходного кода

Merge branch 'master' into i/5569

Maciej Gołaszewski 6 лет назад
Родитель
Сommit
2588ea0220
100 измененных файлов с 828 добавлено и 847 удалено
  1. 4 0
      packages/ckeditor5-engine/.editorconfig
  2. 1 1
      packages/ckeditor5-engine/.eslintrc.js
  3. 1 1
      packages/ckeditor5-engine/.github/PULL_REQUEST_TEMPLATE.md
  4. 3 0
      packages/ckeditor5-engine/.stylelintrc
  5. 34 0
      packages/ckeditor5-engine/CHANGELOG.md
  6. 1 1
      packages/ckeditor5-engine/LICENSE.md
  7. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/build-extending-content-source.js
  8. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-external-link-target.js
  9. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-heading-class.js
  10. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-link-class.js
  11. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-unsafe-link-class.js
  12. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-allow-div-attributes.js
  13. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-allow-link-target.js
  14. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-arbitrary-attribute-values.js
  15. 1 1
      packages/ckeditor5-engine/docs/_snippets/framework/extending-content-custom-figure-attributes.js
  16. 24 18
      packages/ckeditor5-engine/package.json
  17. 1 1
      packages/ckeditor5-engine/src/controller/datacontroller.js
  18. 14 2
      packages/ckeditor5-engine/src/controller/editingcontroller.js
  19. 1 1
      packages/ckeditor5-engine/src/conversion/conversion.js
  20. 1 1
      packages/ckeditor5-engine/src/conversion/conversionhelpers.js
  21. 1 1
      packages/ckeditor5-engine/src/conversion/downcastdispatcher.js
  22. 14 1
      packages/ckeditor5-engine/src/conversion/downcasthelpers.js
  23. 1 1
      packages/ckeditor5-engine/src/conversion/mapper.js
  24. 1 1
      packages/ckeditor5-engine/src/conversion/modelconsumable.js
  25. 1 1
      packages/ckeditor5-engine/src/conversion/upcastdispatcher.js
  26. 28 1
      packages/ckeditor5-engine/src/conversion/upcasthelpers.js
  27. 14 1
      packages/ckeditor5-engine/src/conversion/viewconsumable.js
  28. 1 1
      packages/ckeditor5-engine/src/dataprocessor/basichtmlwriter.js
  29. 1 1
      packages/ckeditor5-engine/src/dataprocessor/dataprocessor.jsdoc
  30. 1 1
      packages/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js
  31. 1 1
      packages/ckeditor5-engine/src/dataprocessor/htmlwriter.js
  32. 1 1
      packages/ckeditor5-engine/src/dataprocessor/xmldataprocessor.js
  33. 0 588
      packages/ckeditor5-engine/src/dev-utils/enableenginedebug.js
  34. 1 1
      packages/ckeditor5-engine/src/dev-utils/model.js
  35. 1 1
      packages/ckeditor5-engine/src/dev-utils/operationreplayer.js
  36. 103 0
      packages/ckeditor5-engine/src/dev-utils/utils.js
  37. 1 1
      packages/ckeditor5-engine/src/dev-utils/view.js
  38. 1 1
      packages/ckeditor5-engine/src/model/batch.js
  39. 1 1
      packages/ckeditor5-engine/src/model/differ.js
  40. 8 1
      packages/ckeditor5-engine/src/model/document.js
  41. 41 1
      packages/ckeditor5-engine/src/model/documentfragment.js
  42. 1 1
      packages/ckeditor5-engine/src/model/documentselection.js
  43. 64 1
      packages/ckeditor5-engine/src/model/element.js
  44. 1 1
      packages/ckeditor5-engine/src/model/history.js
  45. 1 1
      packages/ckeditor5-engine/src/model/item.jsdoc
  46. 1 1
      packages/ckeditor5-engine/src/model/liveposition.js
  47. 1 1
      packages/ckeditor5-engine/src/model/liverange.js
  48. 2 2
      packages/ckeditor5-engine/src/model/markercollection.js
  49. 39 2
      packages/ckeditor5-engine/src/model/model.js
  50. 1 1
      packages/ckeditor5-engine/src/model/node.js
  51. 1 1
      packages/ckeditor5-engine/src/model/nodelist.js
  52. 7 1
      packages/ckeditor5-engine/src/model/operation/attributeoperation.js
  53. 11 1
      packages/ckeditor5-engine/src/model/operation/detachoperation.js
  54. 7 1
      packages/ckeditor5-engine/src/model/operation/insertoperation.js
  55. 6 1
      packages/ckeditor5-engine/src/model/operation/markeroperation.js
  56. 7 1
      packages/ckeditor5-engine/src/model/operation/mergeoperation.js
  57. 9 1
      packages/ckeditor5-engine/src/model/operation/moveoperation.js
  58. 5 1
      packages/ckeditor5-engine/src/model/operation/nooperation.js
  59. 5 1
      packages/ckeditor5-engine/src/model/operation/operation.js
  60. 1 1
      packages/ckeditor5-engine/src/model/operation/operationfactory.js
  61. 6 1
      packages/ckeditor5-engine/src/model/operation/renameoperation.js
  62. 7 1
      packages/ckeditor5-engine/src/model/operation/rootattributeoperation.js
  63. 7 1
      packages/ckeditor5-engine/src/model/operation/splitoperation.js
  64. 1 1
      packages/ckeditor5-engine/src/model/operation/transform.js
  65. 1 1
      packages/ckeditor5-engine/src/model/operation/utils.js
  66. 9 1
      packages/ckeditor5-engine/src/model/position.js
  67. 9 2
      packages/ckeditor5-engine/src/model/range.js
  68. 9 1
      packages/ckeditor5-engine/src/model/rootelement.js
  69. 18 1
      packages/ckeditor5-engine/src/model/schema.js
  70. 1 1
      packages/ckeditor5-engine/src/model/selection.js
  71. 15 1
      packages/ckeditor5-engine/src/model/text.js
  72. 16 1
      packages/ckeditor5-engine/src/model/textproxy.js
  73. 1 1
      packages/ckeditor5-engine/src/model/treewalker.js
  74. 1 2
      packages/ckeditor5-engine/src/model/utils/deletecontent.js
  75. 1 1
      packages/ckeditor5-engine/src/model/utils/getselectedcontent.js
  76. 1 1
      packages/ckeditor5-engine/src/model/utils/insertcontent.js
  77. 1 1
      packages/ckeditor5-engine/src/model/utils/modifyselection.js
  78. 1 1
      packages/ckeditor5-engine/src/model/utils/selection-post-fixer.js
  79. 2 2
      packages/ckeditor5-engine/src/model/writer.js
  80. 1 1
      packages/ckeditor5-engine/src/utils/bindtwostepcarettoattribute.js
  81. 1 1
      packages/ckeditor5-engine/src/view/attributeelement.js
  82. 1 1
      packages/ckeditor5-engine/src/view/containerelement.js
  83. 24 1
      packages/ckeditor5-engine/src/view/document.js
  84. 21 1
      packages/ckeditor5-engine/src/view/documentfragment.js
  85. 1 1
      packages/ckeditor5-engine/src/view/documentselection.js
  86. 14 2
      packages/ckeditor5-engine/src/view/domconverter.js
  87. 42 26
      packages/ckeditor5-engine/src/view/downcastwriter.js
  88. 1 1
      packages/ckeditor5-engine/src/view/editableelement.js
  89. 118 116
      packages/ckeditor5-engine/src/view/element.js
  90. 1 1
      packages/ckeditor5-engine/src/view/elementdefinition.jsdoc
  91. 1 1
      packages/ckeditor5-engine/src/view/emptyelement.js
  92. 1 1
      packages/ckeditor5-engine/src/view/filler.js
  93. 1 1
      packages/ckeditor5-engine/src/view/item.jsdoc
  94. 1 1
      packages/ckeditor5-engine/src/view/matcher.js
  95. 1 1
      packages/ckeditor5-engine/src/view/node.js
  96. 1 1
      packages/ckeditor5-engine/src/view/observer/clickobserver.js
  97. 1 1
      packages/ckeditor5-engine/src/view/observer/compositionobserver.js
  98. 1 1
      packages/ckeditor5-engine/src/view/observer/domeventdata.js
  99. 1 1
      packages/ckeditor5-engine/src/view/observer/domeventobserver.js
  100. 1 1
      packages/ckeditor5-engine/src/view/observer/fakeselectionobserver.js

+ 4 - 0
packages/ckeditor5-engine/.editorconfig

@@ -10,3 +10,7 @@ charset = utf-8
 end_of_line = lf
 trim_trailing_whitespace = true
 insert_final_newline = true
+
+[package.json]
+indent_style = space
+tab_width = 2

+ 1 - 1
packages/ckeditor5-engine/.eslintrc.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/.github/PULL_REQUEST_TEMPLATE.md

@@ -1,6 +1,6 @@
 ### Suggested merge commit message ([convention](https://github.com/ckeditor/ckeditor5-design/wiki/Git-commit-message-convention))
 
-Type: Message. Closes #000.
+Type: Message. Closes ckeditor/ckeditor5#000.
 
 ---
 

+ 3 - 0
packages/ckeditor5-engine/.stylelintrc

@@ -0,0 +1,3 @@
+{
+	"extends": "stylelint-config-ckeditor5"
+}

+ 34 - 0
packages/ckeditor5-engine/CHANGELOG.md

@@ -1,6 +1,40 @@
 Changelog
 =========
 
+## [16.0.0](https://github.com/ckeditor/ckeditor5-engine/compare/v15.0.0...v16.0.0) (2019-12-04)
+
+### Other changes
+
+* Split debug tools between the engine files. You do not need to add debug plugin to the editor if you want to use `--debug engine` building flag anymore. Closes [ckeditor/ckeditor5#5649](https://github.com/ckeditor/ckeditor5/issues/5649). ([353f091](https://github.com/ckeditor/ckeditor5-engine/commit/353f091))
+
+
+## [15.0.0](https://github.com/ckeditor/ckeditor5-engine/compare/v14.0.0...v15.0.0) (2019-10-23)
+
+### MAJOR BREAKING CHANGES
+
+* The behavior of block filler detection on DOM to view conversion was changed. Now, it specifically checks the parent of a text node to check whether it is a block. Which means that a list of block element names has to be used. If you use custom elements or use one of the HTML elements which CKEditor 5 does not recognize as a block element, see [#404](https://github.com/ckeditor/ckeditor5-engine/issues/404) and `DomConverter.blockElements` documentation.
+* The `Selection#getTopMostBlocks()` was removed from the public API. Use `Selection#getSelectedBlocks()` instead.
+* The `Selection#getSelectedBlocks()` does not return blocks nested in other blocks now.
+
+### Features
+
+* Added support for creating elements from other XML namespaces. See [ckeditor/ckeditor5#2088](https://github.com/ckeditor/ckeditor5/issues/2088). ([a9190c9](https://github.com/ckeditor/ckeditor5-engine/commit/a9190c9))
+
+### Bug fixes
+
+* Added a proper check for name-only view matcher in attribute upcast converters. Closes [#1786](https://github.com/ckeditor/ckeditor5-engine/issues/1786). ([2210696](https://github.com/ckeditor/ckeditor5-engine/commit/2210696))
+* Improved filtering out disallowed attributes on child elements. [#1789](https://github.com/ckeditor/ckeditor5-engine/issues/1789). ([c5033b6](https://github.com/ckeditor/ckeditor5-engine/commit/c5033b6))
+* Improved performance when working with fake selections. Closes [#1791](https://github.com/ckeditor/ckeditor5-engine/issues/1791). ([f073ad5](https://github.com/ckeditor/ckeditor5-engine/commit/f073ad5))
+* Placeholder should not be visible in the read-only mode. Closes [ckeditor/ckeditor5#1987](https://github.com/ckeditor/ckeditor5/issues/1987). ([730c417](https://github.com/ckeditor/ckeditor5-engine/commit/730c417))
+* Remove only real block fillers on DOM to view conversion. Closes [#404](https://github.com/ckeditor/ckeditor5-engine/issues/404). ([6d2810b](https://github.com/ckeditor/ckeditor5-engine/commit/6d2810b))
+* The renderer should not update DOM selection when document has active composition. Closes [#1782](https://github.com/ckeditor/ckeditor5-engine/issues/1782). Closes [ckeditor/ckeditor5#1333](https://github.com/ckeditor/ckeditor5/issues/1333). ([c698683](https://github.com/ckeditor/ckeditor5-engine/commit/c698683))
+
+### Other changes
+
+* Added error handling to the common code execution paths. Part of [ckeditor/ckeditor5#1304](https://github.com/ckeditor/ckeditor5/issues/1304). ([220b52f](https://github.com/ckeditor/ckeditor5-engine/commit/220b52f))
+* Removed the `Selection#getTopMostBlocks()` method. Closes [ckeditor/ckeditor5-widget#95](https://github.com/ckeditor/ckeditor5-widget/issues/95). Closes [ckeditor/ckeditor5-table#199](https://github.com/ckeditor/ckeditor5-table/issues/199). ([7970f17](https://github.com/ckeditor/ckeditor5-engine/commit/7970f17))
+
+
 ## [14.0.0](https://github.com/ckeditor/ckeditor5-engine/compare/v13.2.1...v14.0.0) (2019-08-26)
 
 ### Features

+ 1 - 1
packages/ckeditor5-engine/LICENSE.md

@@ -2,7 +2,7 @@ Software License Agreement
 ==========================
 
 **CKEditor 5 Editing Engine** – https://github.com/ckeditor/ckeditor5-engine <br>
-Copyright (c) 2003-2019, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2020, [CKSource](http://cksource.com) Frederico Knabben. All rights reserved.
 
 Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html).
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/build-extending-content-source.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-external-link-target.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-heading-class.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-link-class.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-add-unsafe-link-class.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-allow-div-attributes.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-allow-link-target.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-arbitrary-attribute-values.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 1 - 1
packages/ckeditor5-engine/docs/_snippets/framework/extending-content-custom-figure-attributes.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md.
  */
 

+ 24 - 18
packages/ckeditor5-engine/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@ckeditor/ckeditor5-engine",
-  "version": "14.0.0",
+  "version": "16.0.0",
   "description": "The editing engine of CKEditor 5 – the best browser-based rich text editor.",
   "keywords": [
     "wysiwyg",
@@ -21,28 +21,30 @@
     "ckeditor 5"
   ],
   "dependencies": {
-    "@ckeditor/ckeditor5-utils": "^14.0.0",
+    "@ckeditor/ckeditor5-utils": "^16.0.0",
     "lodash-es": "^4.17.10"
   },
   "devDependencies": {
-    "@ckeditor/ckeditor5-basic-styles": "^11.1.4",
-    "@ckeditor/ckeditor5-block-quote": "^11.1.3",
-    "@ckeditor/ckeditor5-core": "^12.3.0",
-    "@ckeditor/ckeditor5-editor-classic": "^12.1.4",
-    "@ckeditor/ckeditor5-enter": "^11.1.0",
-    "@ckeditor/ckeditor5-essentials": "^11.0.5",
-    "@ckeditor/ckeditor5-heading": "^11.0.5",
-    "@ckeditor/ckeditor5-link": "^11.1.2",
-    "@ckeditor/ckeditor5-list": "^12.1.0",
-    "@ckeditor/ckeditor5-paragraph": "^11.0.5",
-    "@ckeditor/ckeditor5-theme-lark": "^14.2.0",
-    "@ckeditor/ckeditor5-typing": "^12.2.0",
-    "@ckeditor/ckeditor5-undo": "^11.0.5",
-    "@ckeditor/ckeditor5-widget": "^11.1.0",
+    "@ckeditor/ckeditor5-basic-styles": "^16.0.0",
+    "@ckeditor/ckeditor5-block-quote": "^16.0.0",
+    "@ckeditor/ckeditor5-core": "^16.0.0",
+    "@ckeditor/ckeditor5-editor-classic": "^16.0.0",
+    "@ckeditor/ckeditor5-enter": "^16.0.0",
+    "@ckeditor/ckeditor5-essentials": "^16.0.0",
+    "@ckeditor/ckeditor5-heading": "^16.0.0",
+    "@ckeditor/ckeditor5-link": "^16.0.0",
+    "@ckeditor/ckeditor5-list": "^16.0.0",
+    "@ckeditor/ckeditor5-paragraph": "^16.0.0",
+    "@ckeditor/ckeditor5-theme-lark": "^16.0.0",
+    "@ckeditor/ckeditor5-typing": "^16.0.0",
+    "@ckeditor/ckeditor5-undo": "^16.0.0",
+    "@ckeditor/ckeditor5-widget": "^16.0.0",
     "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^2.0.0",
     "husky": "^1.3.1",
-    "lint-staged": "^7.0.0"
+    "lint-staged": "^7.0.0",
+    "stylelint": "^11.1.1",
+    "stylelint-config-ckeditor5": "^1.0.0"
   },
   "engines": {
     "node": ">=8.0.0",
@@ -62,11 +64,15 @@
     "theme"
   ],
   "scripts": {
-    "lint": "eslint --quiet '**/*.js'"
+    "lint": "eslint --quiet '**/*.js'",
+    "stylelint": "stylelint --quiet --allow-empty-input 'theme/**/*.css' 'docs/**/*.css'"
   },
   "lint-staged": {
     "**/*.js": [
       "eslint --quiet"
+    ],
+    "**/*.css": [
+      "stylelint --quiet --allow-empty-input"
     ]
   },
   "eslintIgnore": [

+ 1 - 1
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 14 - 2
packages/ckeditor5-engine/src/controller/editingcontroller.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -17,6 +17,8 @@ import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import { convertSelectionChange } from '../conversion/upcasthelpers';
 
+// @if CK_DEBUG_ENGINE // const { dumpTrees, initDocumentDumping } = require( '../dev-utils/utils' );
+
 /**
  * Controller for the editing pipeline. The editing pipeline controls {@link ~EditingController#model model} rendering,
  * including selection handling. It also creates the {@link ~EditingController#view view} which builds a
@@ -73,7 +75,7 @@ export default class EditingController {
 		// model's change, they might trigger view rendering before the conversion is completed (e.g. before the selection
 		// is converted). We disable rendering for the length of the outermost model change() block to prevent that.
 		//
-		// See  https://github.com/ckeditor/ckeditor5-engine/issues/1528
+		// See https://github.com/ckeditor/ckeditor5-engine/issues/1528
 		this.listenTo( this.model, '_beforeChanges', () => {
 			this.view._disableRendering( true );
 		}, { priority: 'highest' } );
@@ -121,6 +123,16 @@ export default class EditingController {
 
 			return viewRoot;
 		} );
+
+		// @if CK_DEBUG_ENGINE // initDocumentDumping( this.model.document );
+		// @if CK_DEBUG_ENGINE // initDocumentDumping( this.view.document );
+
+		// @if CK_DEBUG_ENGINE // dumpTrees( this.model.document, this.model.document.version );
+		// @if CK_DEBUG_ENGINE // dumpTrees( this.view.document, this.model.document.version );
+
+		// @if CK_DEBUG_ENGINE // this.model.document.on( 'change', () => {
+		// @if CK_DEBUG_ENGINE //	dumpTrees( this.view.document, this.model.document.version );
+		// @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
 	}
 
 	/**

+ 1 - 1
packages/ckeditor5-engine/src/conversion/conversion.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/conversion/conversionhelpers.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/conversion/downcastdispatcher.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 14 - 1
packages/ckeditor5-engine/src/conversion/downcasthelpers.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -204,6 +204,19 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *			view: modelAttributeValue => ( { key: 'class', value: 'styled-' + modelAttributeValue } )
 	 *		} );
 	 *
+	 * **Note**: Downcasting to a style property requires providing `value` as an object:
+	 *
+	 *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
+	 *			model: 'lineHeight',
+	 *			view: modelAttributeValue => ( {
+	 *				key: 'style',
+	 *				value: {
+	 *					'line-height': modelAttributeValue,
+	 *					'border-bottom': '1px dotted #ba2'
+	 *				}
+	 *			} )
+	 *		} );
+	 *
 	 * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
 	 * to the conversion process.
 	 *

+ 1 - 1
packages/ckeditor5-engine/src/conversion/mapper.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/conversion/modelconsumable.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/conversion/upcastdispatcher.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 28 - 1
packages/ckeditor5-engine/src/conversion/upcasthelpers.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -237,6 +237,33 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 *			}
 	 *		} );
 	 *
+	 * Converting styles works a bit differently as it requires `view.styles` to be an object and by default
+	 * a model attribute will be set to `true` by such a converter. You can set the model attribute to any value by providing the `value`
+	 * callback that returns the desired value.
+	 *
+	 *		// Default conversion of font-weight style will result in setting bold attribute to true.
+	 *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
+	 *			view: {
+	 *				styles: {
+	 *					'font-weight': 'bold'
+	 *				}
+	 *			},
+	 *			model: 'bold'
+	 *		} );
+	 *
+	 *		// This converter will pass any style value to the `lineHeight` model attribute.
+	 *		editor.conversion.for( 'upcast' ).attributeToAttribute( {
+	 *			view: {
+	 *				styles: {
+	 *					'line-height': /[\s\S]+/
+	 *				}
+	 *			},
+	 *			model: {
+	 *				key: 'lineHeight',
+	 *				value: viewElement => viewElement.getStyle( 'line-height' )
+	 *			}
+	 *		} );
+	 *
 	 * See {@link module:engine/conversion/conversion~Conversion#for `conversion.for()`} to learn how to add a converter
 	 * to the conversion process.
 	 *

+ 14 - 1
packages/ckeditor5-engine/src/conversion/viewconsumable.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -9,6 +9,7 @@
 
 import { isArray } from 'lodash-es';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
+import StylesMap from '../view/stylesmap';
 
 /**
  * Class used for handling consumption of view {@link module:engine/view/element~Element elements},
@@ -507,6 +508,12 @@ class ViewElementConsumables {
 			}
 
 			consumables.set( name, true );
+
+			if ( type === 'styles' ) {
+				for ( const alsoName of StylesMap.getRelatedStyles( name ) ) {
+					consumables.set( alsoName, true );
+				}
+			}
 		}
 	}
 
@@ -568,6 +575,12 @@ class ViewElementConsumables {
 				this._consume( consumableName, [ ...this._consumables[ consumableName ].keys() ] );
 			} else {
 				consumables.set( name, false );
+
+				if ( type == 'styles' ) {
+					for ( const toConsume of StylesMap.getRelatedStyles( name ) ) {
+						consumables.set( toConsume, false );
+					}
+				}
 			}
 		}
 	}

+ 1 - 1
packages/ckeditor5-engine/src/dataprocessor/basichtmlwriter.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/dataprocessor/dataprocessor.jsdoc

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/dataprocessor/htmlwriter.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/dataprocessor/xmldataprocessor.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 0 - 588
packages/ckeditor5-engine/src/dev-utils/enableenginedebug.js

@@ -1,588 +0,0 @@
-/**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
- */
-
-/**
- * @module engine/dev-utils/enableenginedebug
- */
-
-/* global console */
-
-import OperationReplayer from './operationreplayer';
-
-import ModelPosition from '../model/position';
-import ModelRange from '../model/range';
-import ModelText from '../model/text';
-import ModelTextProxy from '../model/textproxy';
-import ModelElement from '../model/element';
-import Operation from '../model/operation/operation';
-import AttributeOperation from '../model/operation/attributeoperation';
-import DetachOperation from '../model/operation/detachoperation';
-import InsertOperation from '../model/operation/insertoperation';
-import MarkerOperation from '../model/operation/markeroperation';
-import MoveOperation from '../model/operation/moveoperation';
-import NoOperation from '../model/operation/nooperation';
-import RenameOperation from '../model/operation/renameoperation';
-import RootAttributeOperation from '../model/operation/rootattributeoperation';
-import SplitOperation from '../model/operation/splitoperation';
-import MergeOperation from '../model/operation/mergeoperation';
-import Model from '../model/model';
-import ModelDocument from '../model/document';
-import ModelDocumentFragment from '../model/documentfragment';
-import ModelRootElement from '../model/rootelement';
-
-import ViewDocument from '../view/document';
-import ViewElement from '../view/element';
-import ViewText from '../view/text';
-import ViewTextProxy from '../view/textproxy';
-import ViewDocumentFragment from '../view/documentfragment';
-
-import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import Editor from '@ckeditor/ckeditor5-core/src/editor/editor';
-
-// Sandbox class allows creating mocks of the functions and restoring these mocks to the original values.
-class Sandbox {
-	constructor() {
-		// An array that contains functions which restore the original values of mocked objects.
-		// @private
-		// @type {Array.<Function>}
-		this._restores = [];
-	}
-
-	// Creates a new mock.
-	//
-	// @param {Object} object Object to mock.
-	// @param {String} methodName Function to mock.
-	// @param {Function} fakeMethod Function that will be executed.
-	mock( object, methodName, fakeMethod ) {
-		const originalMethod = object[ methodName ];
-
-		object[ methodName ] = fakeMethod;
-
-		this._restores.unshift( () => {
-			if ( originalMethod ) {
-				object[ methodName ] = originalMethod;
-			} else {
-				delete object[ methodName ];
-			}
-		} );
-	}
-
-	// Restores all mocked functions.
-	restore() {
-		for ( const restore of this._restores ) {
-			restore();
-		}
-
-		this._restores = [];
-	}
-}
-
-const sandbox = new Sandbox();
-
-const treeDump = Symbol( '_treeDump' );
-
-// Maximum number of stored states of model and view document.
-const maxTreeDumpLength = 20;
-
-// Separator used to separate stringified operations.
-const LOG_SEPARATOR = '-------';
-
-// Specified whether debug tools were already enabled.
-let enabled = false;
-
-// Logging function used to log debug messages.
-let logger = console;
-
-/**
- * Enhances model classes with logging methods. Returns a plugin that should be loaded in the editor to
- * enable debugging features.
- *
- * Every operation applied on {@link module:engine/model/document~Document model.Document} is logged.
- *
- * The following classes are expanded with `log` and meaningful `toString` methods:
- * * {@link module:engine/model/position~Position model.Position},
- * * {@link module:engine/model/range~Range model.Range},
- * * {@link module:engine/model/text~Text model.Text},
- * * {@link module:engine/model/element~Element model.Element},
- * * {@link module:engine/model/rootelement~RootElement model.RootElement},
- * * {@link module:engine/model/documentfragment~DocumentFragment model.DocumentFragment},
- * * {@link module:engine/model/document~Document model.Document},
- * * all {@link module:engine/model/operation/operation~Operation operations}
- * * {@link module:engine/view/element~Element view.Element},
- * * {@link module:engine/view/documentfragment~DocumentFragment view.DocumentFragment},
- * * {@link module:engine/view/document~Document view.Document}.
- *
- * Additionally, the following logging utility methods are added:
- * * {@link module:engine/model/text~Text model.Text} `logExtended`,
- * * {@link module:engine/model/element~Element model.Element} `logExtended`,
- * * {@link module:engine/model/element~Element model.Element} `logAll`.
- *
- * Additionally, the following classes are expanded with `logTree` and `printTree` methods:
- * * {@link module:engine/model/element~Element model.Element},
- * * {@link module:engine/model/documentfragment~DocumentFragment model.DocumentFragment},
- * * {@link module:engine/view/element~Element view.Element},
- * * {@link module:engine/view/documentfragment~DocumentFragment view.DocumentFragment}.
- *
- * Finally, the following methods are added to {@link module:core/editor/editor~Editor}: `logModel`, `logView`, `logDocuments`.
- * All those methods take one parameter, which is the version of the {@link module:engine/model/document~Document model document}
- * for which the model or view document state should be logged.
- *
- * @param {Object} [_logger] An object with functions used to log messages and errors. By default messages are logged to the console.
- * If specified, it is expected to have `log()` and `error()` methods.
- * @returns {module:engine/dev-utils/enableenginedebug~DebugPlugin} The plugin to be loaded into the editor.
- */
-export default function enableEngineDebug( _logger = console ) {
-	logger = _logger;
-
-	if ( !enabled ) {
-		enabled = true;
-
-		enableLoggingTools();
-		enableDocumentTools();
-		enableReplayerTools();
-	}
-
-	return DebugPlugin;
-}
-
-/**
- * Restores all methods that have been overwritten.
- */
-export function disableEngineDebug() {
-	sandbox.restore();
-	enabled = false;
-}
-
-function enableLoggingTools() {
-	sandbox.mock( ModelPosition.prototype, 'toString', function() {
-		return `${ this.root } [ ${ this.path.join( ', ' ) } ]`;
-	} );
-
-	sandbox.mock( ModelPosition.prototype, 'log', function() {
-		logger.log( 'ModelPosition: ' + this );
-	} );
-
-	sandbox.mock( ModelRange.prototype, 'toString', function() {
-		return `${ this.root } [ ${ this.start.path.join( ', ' ) } ] - [ ${ this.end.path.join( ', ' ) } ]`;
-	} );
-
-	sandbox.mock( ModelRange.prototype, 'log', function() {
-		logger.log( 'ModelRange: ' + this );
-	} );
-
-	sandbox.mock( ModelText.prototype, 'toString', function() {
-		return `#${ this.data }`;
-	} );
-
-	sandbox.mock( ModelText.prototype, 'logExtended', function() {
-		logger.log( `ModelText: ${ this }, attrs: ${ mapString( this.getAttributes() ) }` );
-	} );
-
-	sandbox.mock( ModelText.prototype, 'log', function() {
-		logger.log( 'ModelText: ' + this );
-	} );
-
-	sandbox.mock( ModelTextProxy.prototype, 'toString', function() {
-		return `#${ this.data }`;
-	} );
-
-	sandbox.mock( ModelTextProxy.prototype, 'logExtended', function() {
-		logger.log( `ModelTextProxy: ${ this }, attrs: ${ mapString( this.getAttributes() ) }` );
-	} );
-
-	sandbox.mock( ModelTextProxy.prototype, 'log', function() {
-		logger.log( 'ModelTextProxy: ' + this );
-	} );
-
-	sandbox.mock( ModelElement.prototype, 'toString', function() {
-		return `<${ this.rootName || this.name }>`;
-	} );
-
-	sandbox.mock( ModelElement.prototype, 'log', function() {
-		logger.log( 'ModelElement: ' + this );
-	} );
-
-	sandbox.mock( ModelElement.prototype, 'logExtended', function() {
-		logger.log( `ModelElement: ${ this }, ${ this.childCount } children, attrs: ${ mapString( this.getAttributes() ) }` );
-	} );
-
-	sandbox.mock( ModelElement.prototype, 'logAll', function() {
-		logger.log( '--------------------' );
-
-		this.logExtended();
-		logger.log( 'List of children:' );
-
-		for ( const child of this.getChildren() ) {
-			child.log();
-		}
-	} );
-
-	sandbox.mock( ModelElement.prototype, 'printTree', function( level = 0 ) {
-		let string = '';
-
-		string += '\t'.repeat( level ) + `<${ this.rootName || this.name }${ mapToTags( this.getAttributes() ) }>`;
-
-		for ( const child of this.getChildren() ) {
-			string += '\n';
-
-			if ( child.is( 'text' ) ) {
-				const textAttrs = mapToTags( child._attrs );
-
-				string += '\t'.repeat( level + 1 );
-
-				if ( textAttrs !== '' ) {
-					string += `<$text${ textAttrs }>` + child.data + '</$text>';
-				} else {
-					string += child.data;
-				}
-			} else {
-				string += child.printTree( level + 1 );
-			}
-		}
-
-		if ( this.childCount ) {
-			string += '\n' + '\t'.repeat( level );
-		}
-
-		string += `</${ this.rootName || this.name }>`;
-
-		return string;
-	} );
-
-	sandbox.mock( ModelElement.prototype, 'logTree', function() {
-		logger.log( this.printTree() );
-	} );
-
-	sandbox.mock( ModelRootElement.prototype, 'toString', function() {
-		return this.rootName;
-	} );
-
-	sandbox.mock( ModelRootElement.prototype, 'log', function() {
-		logger.log( 'ModelRootElement: ' + this );
-	} );
-
-	sandbox.mock( ModelDocumentFragment.prototype, 'toString', function() {
-		return 'documentFragment';
-	} );
-
-	sandbox.mock( ModelDocumentFragment.prototype, 'log', function() {
-		logger.log( 'ModelDocumentFragment: ' + this );
-	} );
-
-	sandbox.mock( ModelDocumentFragment.prototype, 'printTree', function() {
-		let string = 'ModelDocumentFragment: [';
-
-		for ( const child of this.getChildren() ) {
-			string += '\n';
-
-			if ( child.is( 'text' ) ) {
-				const textAttrs = mapToTags( child._attrs );
-
-				string += '\t'.repeat( 1 );
-
-				if ( textAttrs !== '' ) {
-					string += `<$text${ textAttrs }>` + child.data + '</$text>';
-				} else {
-					string += child.data;
-				}
-			} else {
-				string += child.printTree( 1 );
-			}
-		}
-
-		string += '\n]';
-
-		return string;
-	} );
-
-	sandbox.mock( ModelDocumentFragment.prototype, 'logTree', function() {
-		logger.log( this.printTree() );
-	} );
-
-	sandbox.mock( Operation.prototype, 'log', function() {
-		logger.log( this.toString() );
-	} );
-
-	sandbox.mock( AttributeOperation.prototype, 'toString', function() {
-		return getClassName( this ) + `( ${ this.baseVersion } ): ` +
-			`"${ this.key }": ${ JSON.stringify( this.oldValue ) } -> ${ JSON.stringify( this.newValue ) }, ${ this.range }`;
-	} );
-
-	sandbox.mock( DetachOperation.prototype, 'toString', function() {
-		const range = ModelRange._createFromPositionAndShift( this.sourcePosition, this.howMany );
-		const nodes = Array.from( range.getItems() );
-		const nodeString = nodes.length > 1 ? `[ ${ nodes.length } ]` : nodes[ 0 ];
-
-		return getClassName( this ) + `( ${ this.baseVersion } ): ${ nodeString } -> ${ range }`;
-	} );
-
-	sandbox.mock( InsertOperation.prototype, 'toString', function() {
-		const nodeString = this.nodes.length > 1 ? `[ ${ this.nodes.length } ]` : this.nodes.getNode( 0 );
-
-		return getClassName( this ) + `( ${ this.baseVersion } ): ${ nodeString } -> ${ this.position }`;
-	} );
-
-	sandbox.mock( MarkerOperation.prototype, 'toString', function() {
-		return getClassName( this ) + `( ${ this.baseVersion } ): ` +
-			`"${ this.name }": ${ this.oldRange } -> ${ this.newRange }`;
-	} );
-
-	sandbox.mock( MoveOperation.prototype, 'toString', function() {
-		const range = ModelRange._createFromPositionAndShift( this.sourcePosition, this.howMany );
-
-		return getClassName( this ) + `( ${ this.baseVersion } ): ${ range } -> ${ this.targetPosition }`;
-	} );
-
-	sandbox.mock( NoOperation.prototype, 'toString', function() {
-		return `NoOperation( ${ this.baseVersion } )`;
-	} );
-
-	sandbox.mock( RenameOperation.prototype, 'toString', function() {
-		return getClassName( this ) + `( ${ this.baseVersion } ): ` +
-			`${ this.position }: "${ this.oldName }" -> "${ this.newName }"`;
-	} );
-
-	sandbox.mock( RootAttributeOperation.prototype, 'toString', function() {
-		return getClassName( this ) + `( ${ this.baseVersion } ): ` +
-			`"${ this.key }": ${ JSON.stringify( this.oldValue ) } -> ${ JSON.stringify( this.newValue ) }, ${ this.root.rootName }`;
-	} );
-
-	sandbox.mock( MergeOperation.prototype, 'toString', function() {
-		return getClassName( this ) + `( ${ this.baseVersion } ): ` +
-			`${ this.sourcePosition } -> ${ this.targetPosition } ( ${ this.howMany } ), ${ this.graveyardPosition }`;
-	} );
-
-	sandbox.mock( SplitOperation.prototype, 'toString', function() {
-		return getClassName( this ) + `( ${ this.baseVersion } ): ${ this.splitPosition } ` +
-			`( ${ this.howMany } ) -> ${ this.insertionPosition }${ this.graveyardPosition ? ' with ' + this.graveyardPosition : '' }`;
-	} );
-
-	sandbox.mock( ViewText.prototype, 'toString', function() {
-		return `#${ this.data }`;
-	} );
-
-	sandbox.mock( ViewText.prototype, 'logExtended', function() {
-		logger.log( 'ViewText: ' + this );
-	} );
-
-	sandbox.mock( ViewText.prototype, 'log', function() {
-		logger.log( 'ViewText: ' + this );
-	} );
-
-	sandbox.mock( ViewTextProxy.prototype, 'toString', function() {
-		return `#${ this.data }`;
-	} );
-
-	sandbox.mock( ViewTextProxy.prototype, 'logExtended', function() {
-		logger.log( 'ViewTextProxy: ' + this );
-	} );
-
-	sandbox.mock( ViewTextProxy.prototype, 'log', function() {
-		logger.log( 'ViewTextProxy: ' + this );
-	} );
-
-	sandbox.mock( ViewElement.prototype, 'printTree', function( level = 0 ) {
-		let string = '';
-
-		string += '\t'.repeat( level ) + `<${ this.name }${ mapToTags( this.getAttributes() ) }>`;
-
-		for ( const child of this.getChildren() ) {
-			if ( child.is( 'text' ) ) {
-				string += '\n' + '\t'.repeat( level + 1 ) + child.data;
-			} else {
-				string += '\n' + child.printTree( level + 1 );
-			}
-		}
-
-		if ( this.childCount ) {
-			string += '\n' + '\t'.repeat( level );
-		}
-
-		string += `</${ this.name }>`;
-
-		return string;
-	} );
-
-	sandbox.mock( ViewElement.prototype, 'logTree', function() {
-		logger.log( this.printTree() );
-	} );
-
-	sandbox.mock( ViewDocumentFragment.prototype, 'printTree', function() {
-		let string = 'ViewDocumentFragment: [';
-
-		for ( const child of this.getChildren() ) {
-			if ( child.is( 'text' ) ) {
-				string += '\n' + '\t'.repeat( 1 ) + child.data;
-			} else {
-				string += '\n' + child.printTree( 1 );
-			}
-		}
-
-		string += '\n]';
-
-		return string;
-	} );
-
-	sandbox.mock( ViewDocumentFragment.prototype, 'logTree', function() {
-		logger.log( this.printTree() );
-	} );
-}
-
-function enableReplayerTools() {
-	const _modelApplyOperation = Model.prototype.applyOperation;
-
-	sandbox.mock( Model.prototype, 'applyOperation', function( operation ) {
-		if ( !this._appliedOperations ) {
-			this._appliedOperations = [];
-		}
-
-		this._appliedOperations.push( operation );
-
-		return _modelApplyOperation.call( this, operation );
-	} );
-
-	sandbox.mock( Model.prototype, 'getAppliedOperations', function() {
-		if ( !this._appliedOperations ) {
-			return '';
-		}
-
-		return this._appliedOperations.map( JSON.stringify ).join( LOG_SEPARATOR );
-	} );
-
-	sandbox.mock( Model.prototype, 'createReplayer', function( stringifiedOperations ) {
-		return new OperationReplayer( this, LOG_SEPARATOR, stringifiedOperations );
-	} );
-}
-
-function enableDocumentTools() {
-	const _modelApplyOperation = Model.prototype.applyOperation;
-
-	sandbox.mock( Model.prototype, 'applyOperation', function( operation ) {
-		logger.log( 'Applying ' + operation );
-
-		if ( !this._operationLogs ) {
-			this._operationLogs = [];
-		}
-
-		this._operationLogs.push( JSON.stringify( operation ) );
-
-		return _modelApplyOperation.call( this, operation );
-	} );
-
-	sandbox.mock( ModelDocument.prototype, 'log', function( version = null ) {
-		version = version === null ? this.version : version;
-
-		logDocument( this, version );
-	} );
-
-	sandbox.mock( ViewDocument.prototype, 'log', function( version ) {
-		logDocument( this, version );
-	} );
-
-	sandbox.mock( Editor.prototype, 'logModel', function( version = null ) {
-		version = version === null ? this.model.document.version : version;
-
-		this.model.document.log( version );
-	} );
-
-	sandbox.mock( Editor.prototype, 'logView', function( version ) {
-		this.editing.view.document.log( version );
-	} );
-
-	sandbox.mock( Editor.prototype, 'logDocuments', function( version = null ) {
-		version = version === null ? this.model.document.version : version;
-
-		this.logModel( version );
-		this.logView( version );
-	} );
-
-	function logDocument( document, version ) {
-		logger.log( '--------------------' );
-
-		if ( document[ treeDump ][ version ] ) {
-			logger.log( document[ treeDump ][ version ] );
-		} else {
-			logger.log( 'Tree log unavailable for given version: ' + version );
-		}
-	}
-}
-
-/**
- * Plugin that enables debugging features on the editor's model and view documents.
- */
-class DebugPlugin extends Plugin {
-	constructor( editor ) {
-		super( editor );
-
-		const model = this.editor.model;
-		const modelDocument = model.document;
-		const view = this.editor.editing.view;
-		const viewDocument = view.document;
-
-		modelDocument[ treeDump ] = [];
-		viewDocument[ treeDump ] = [];
-
-		dumpTrees( modelDocument, modelDocument.version );
-		dumpTrees( viewDocument, modelDocument.version );
-
-		model.on( 'applyOperation', () => {
-			dumpTrees( modelDocument, modelDocument.version );
-		}, { priority: 'lowest' } );
-
-		model.document.on( 'change', () => {
-			dumpTrees( viewDocument, modelDocument.version );
-		}, { priority: 'lowest' } );
-	}
-}
-
-// Helper function, stores the `document` state for a given `version` as a string in a private property.
-function dumpTrees( document, version ) {
-	let string = '';
-
-	for ( const root of document.roots ) {
-		string += root.printTree() + '\n';
-	}
-
-	document[ treeDump ][ version ] = string.substr( 0, string.length - 1 ); // Remove the last "\n".
-
-	const overflow = document[ treeDump ].length - maxTreeDumpLength;
-
-	if ( overflow > 0 ) {
-		document[ treeDump ][ overflow - 1 ] = null;
-	}
-}
-
-// Helper function, returns the class name of a given `Operation`.
-// @param {module:engine/model/operation/operation~Operation}
-// @returns {String} Class name.
-function getClassName( obj ) {
-	return obj.constructor.className;
-}
-
-// Helper function, converts a map to the {"key1":"value1","key2":"value2"} format.
-// @param {Map} map Map to convert.
-// @returns {String} Converted map.
-function mapString( map ) {
-	const obj = {};
-
-	for ( const entry of map ) {
-		obj[ entry[ 0 ] ] = entry[ 1 ];
-	}
-
-	return JSON.stringify( obj );
-}
-
-// Helper function, converts a map to the key1="value1" key2="value1" format.
-// @param {Map} map Map to convert.
-// @returns {String} Converted map.
-function mapToTags( map ) {
-	let string = '';
-
-	for ( const entry of map ) {
-		string += ` ${ entry[ 0 ] }=${ JSON.stringify( entry[ 1 ] ) }`;
-	}
-
-	return string;
-}

+ 1 - 1
packages/ckeditor5-engine/src/dev-utils/model.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/dev-utils/operationreplayer.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 103 - 0
packages/ckeditor5-engine/src/dev-utils/utils.js

@@ -0,0 +1,103 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * Note: This package is used only internally for debugging purposes and should not be used
+ * in other environments. It uses a few special methods not existing in the default
+ * building process. That is also why there are no tests for this file.
+ *
+ * @module engine/dev-utils/utils
+ */
+
+/* globals console */
+
+/**
+ * Helper function, converts a map to the 'key1="value1" key2="value1"' format.
+ *
+ * @private
+ * @param {Map} map Map to convert.
+ * @returns {String} Converted map.
+ */
+export function convertMapToTags( map ) {
+	let string = '';
+
+	for ( const entry of map ) {
+		string += ` ${ entry[ 0 ] }=${ JSON.stringify( entry[ 1 ] ) }`;
+	}
+
+	return string;
+}
+
+/**
+ * Helper function, converts a map to the '{"key1":"value1","key2":"value2"}' format.
+ *
+ * @private
+ * @param {Map} map Map to convert.
+ * @returns {String} Converted map.
+ */
+export function convertMapToStringifiedObject( map ) {
+	const obj = {};
+
+	for ( const entry of map ) {
+		obj[ entry[ 0 ] ] = entry[ 1 ];
+	}
+
+	return JSON.stringify( obj );
+}
+
+const treeDump = Symbol( '_treeDump' );
+const maxTreeDumpLength = 20;
+
+/**
+ * Helper function that stores the `document` state for a given `version`.
+ *
+ * @private
+ * @param {*} document
+ * @param {*} version
+ */
+export function dumpTrees( document, version ) {
+	console.log( document, version );
+
+	let string = '';
+
+	for ( const root of document.roots ) {
+		string += root.printTree() + '\n';
+	}
+
+	document[ treeDump ][ version ] = string.substr( 0, string.length - 1 ); // Remove the last "\n".
+
+	const overflow = document[ treeDump ].length - maxTreeDumpLength;
+
+	if ( overflow > 0 ) {
+		document[ treeDump ][ overflow - 1 ] = null;
+	}
+}
+
+/**
+ * Helper function that initializes document dumping.
+ *
+ * @private
+ * @param {*} document
+ */
+export function initDocumentDumping( document ) {
+	document[ treeDump ] = [];
+}
+
+/**
+ * Helper function that logs document for the given version.
+ *
+ * @private
+ * @param {*} document
+ * @param {*} version
+ */
+export function logDocument( document, version ) {
+	console.log( '--------------------' );
+
+	if ( document[ treeDump ][ version ] ) {
+		console.log( document[ treeDump ][ version ] );
+	} else {
+		console.log( 'Tree log unavailable for given version: ' + version );
+	}
+}

+ 1 - 1
packages/ckeditor5-engine/src/dev-utils/view.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/batch.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/differ.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 8 - 1
packages/ckeditor5-engine/src/model/document.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -18,6 +18,8 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import { isInsideSurrogatePair, isInsideCombinedSymbol } from '@ckeditor/ckeditor5-utils/src/unicode';
 import { clone } from 'lodash-es';
 
+// @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' );
+
 const graveyardName = '$graveyard';
 
 /**
@@ -465,6 +467,11 @@ export default class Document {
 	 * @event change:data
 	 * @param {module:engine/model/batch~Batch} batch The batch that was used in the executed changes block.
 	 */
+
+	// @if CK_DEBUG_ENGINE // log( version = null ) {
+	// @if CK_DEBUG_ENGINE // 	version = version === null ? this.version : version;
+	// @if CK_DEBUG_ENGINE // 	logDocument( this, version );
+	// @if CK_DEBUG_ENGINE // }
 }
 
 mix( Document, EmitterMixin );

+ 41 - 1
packages/ckeditor5-engine/src/model/documentfragment.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -13,6 +13,8 @@ import Text from './text';
 import TextProxy from './textproxy';
 import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';
 
+// @if CK_DEBUG_ENGINE // const { stringifyMap } = require( '../dev-utils/utils' );
+
 /**
  * DocumentFragment represents a part of model which does not have a common root but it's top-level nodes
  * can be seen as siblings. In other words, it is a detached part of model tree, without a root.
@@ -318,6 +320,44 @@ export default class DocumentFragment {
 
 		return nodes;
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return 'documentFragment';
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( 'ModelDocumentFragment: ' + this );
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // printTree() {
+	// @if CK_DEBUG_ENGINE //	let string = 'ModelDocumentFragment: [';
+
+	// @if CK_DEBUG_ENGINE //	for ( const child of this.getChildren() ) {
+	// @if CK_DEBUG_ENGINE //		string += '\n';
+
+	// @if CK_DEBUG_ENGINE //		if ( child.is( 'text' ) ) {
+	// @if CK_DEBUG_ENGINE //			const textAttrs = stringifyMap( child._attrs );
+
+	// @if CK_DEBUG_ENGINE //			string += '\t'.repeat( 1 );
+
+	// @if CK_DEBUG_ENGINE //			if ( textAttrs !== '' ) {
+	// @if CK_DEBUG_ENGINE //				string += `<$text${ textAttrs }>` + child.data + '</$text>';
+	// @if CK_DEBUG_ENGINE //			} else {
+	// @if CK_DEBUG_ENGINE //				string += child.data;
+	// @if CK_DEBUG_ENGINE //			}
+	// @if CK_DEBUG_ENGINE //		} else {
+	// @if CK_DEBUG_ENGINE //			string += child.printTree( 1 );
+	// @if CK_DEBUG_ENGINE //		}
+	// @if CK_DEBUG_ENGINE //	}
+
+	// @if CK_DEBUG_ENGINE //	string += '\n]';
+
+	// @if CK_DEBUG_ENGINE //	return string;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logTree() {
+	// @if CK_DEBUG_ENGINE // 	console.log( this.printTree() );
+	// @if CK_DEBUG_ENGINE // }
 }
 
 // Converts strings to Text and non-iterables to arrays.

+ 1 - 1
packages/ckeditor5-engine/src/model/documentselection.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 64 - 1
packages/ckeditor5-engine/src/model/element.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -13,6 +13,8 @@ import Text from './text';
 import TextProxy from './textproxy';
 import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';
 
+// @if CK_DEBUG_ENGINE // const { stringifyMap, convertMapToStringifiedObject, convertMapToTags } = require( '../dev-utils/utils' );
+
 /**
  * Model element. Type of {@link module:engine/model/node~Node node} that has a {@link module:engine/model/element~Element#name name} and
  * {@link module:engine/model/element~Element#getChildren child nodes}.
@@ -322,6 +324,67 @@ export default class Element extends Node {
 
 		return new Element( json.name, json.attributes, children );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `<${ this.rootName || this.name }>`;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( 'ModelElement: ' + this );
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logExtended() {
+	// @if CK_DEBUG_ENGINE // 	console.log( `ModelElement: ${ this }, ${ this.childCount } children,
+	// @if CK_DEBUG_ENGINE //		attrs: ${ convertMapToStringifiedObject( this.getAttributes() ) }` );
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logAll() {
+	// @if CK_DEBUG_ENGINE // 	console.log( '--------------------' );
+	// @if CK_DEBUG_ENGINE //
+	// @if CK_DEBUG_ENGINE // 	this.logExtended();
+	// @if CK_DEBUG_ENGINE //	console.log( 'List of children:' );
+	// @if CK_DEBUG_ENGINE //
+	// @if CK_DEBUG_ENGINE // 	for ( const child of this.getChildren() ) {
+	// @if CK_DEBUG_ENGINE // 		child.log();
+	// @if CK_DEBUG_ENGINE // 	}
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // printTree( level = 0) {
+	// @if CK_DEBUG_ENGINE // 	let string = '';
+
+	// @if CK_DEBUG_ENGINE // 	string += '\t'.repeat( level );
+	// @if CK_DEBUG_ENGINE // 	string += `<${ this.rootName || this.name }${ convertMapToTags( this.getAttributes() ) }>`;
+
+	// @if CK_DEBUG_ENGINE // 	for ( const child of this.getChildren() ) {
+	// @if CK_DEBUG_ENGINE // 		string += '\n';
+
+	// @if CK_DEBUG_ENGINE // 		if ( child.is( 'text' ) ) {
+	// @if CK_DEBUG_ENGINE // 			const textAttrs = convertMapToTags( child._attrs );
+
+	// @if CK_DEBUG_ENGINE // 			string += '\t'.repeat( level + 1 );
+
+	// @if CK_DEBUG_ENGINE // 			if ( textAttrs !== '' ) {
+	// @if CK_DEBUG_ENGINE // 				string += `<$text${ textAttrs }>` + child.data + '</$text>';
+	// @if CK_DEBUG_ENGINE // 			} else {
+	// @if CK_DEBUG_ENGINE // 				string += child.data;
+	// @if CK_DEBUG_ENGINE // 			}
+	// @if CK_DEBUG_ENGINE // 		} else {
+	// @if CK_DEBUG_ENGINE // 			string += child.printTree( level + 1 );
+	// @if CK_DEBUG_ENGINE // 		}
+	// @if CK_DEBUG_ENGINE // 	}
+
+	// @if CK_DEBUG_ENGINE // 	if ( this.childCount ) {
+	// @if CK_DEBUG_ENGINE // 		string += '\n' + '\t'.repeat( level );
+	// @if CK_DEBUG_ENGINE // 	}
+
+	// @if CK_DEBUG_ENGINE // 	string += `</${ this.rootName || this.name }>`;
+
+	// @if CK_DEBUG_ENGINE // 	return string;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logTree() {
+	// @if CK_DEBUG_ENGINE // 	console.log( this.printTree() );
+	// @if CK_DEBUG_ENGINE // }
 }
 
 // Converts strings to Text and non-iterables to arrays.

+ 1 - 1
packages/ckeditor5-engine/src/model/history.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/item.jsdoc

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/liveposition.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/liverange.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 2 - 2
packages/ckeditor5-engine/src/model/markercollection.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -289,7 +289,7 @@ mix( MarkerCollection, EmitterMixin );
  * using common prefixes, separated with `:`, for example: `user:john` or `search:3`. That's useful in term of creating
  * namespaces for custom elements (e.g. comments, highlights). You can use this prefixes in
  * {@link module:engine/model/markercollection~MarkerCollection#event:update} listeners to listen on changes in a group of markers.
- * For instance: `model.markers.on( 'set:user', callback );` will be called whenever any `user:*` markers changes.
+ * For instance: `model.markers.on( 'update:user', callback );` will be called whenever any `user:*` markers changes.
  *
  * There are two types of markers.
  *

+ 39 - 2
packages/ckeditor5-engine/src/model/model.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -26,6 +26,9 @@ import getSelectedContent from './utils/getselectedcontent';
 import { injectSelectionPostFixer } from './utils/selection-post-fixer';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
+// @if CK_DEBUG_ENGINE // const { dumpTrees } = require( '../dev-utils/utils' );
+// @if CK_DEBUG_ENGINE // const { OperationReplayer } = require( '../dev-utils/operationreplayer' ).default;
+
 /**
  * Editor's data model. Read about the model in the
  * {@glink framework/guides/architecture/editing-engine engine architecture guide}.
@@ -116,6 +119,10 @@ export default class Model {
 		} );
 
 		injectSelectionPostFixer( this );
+
+		// @if CK_DEBUG_ENGINE // this.on( 'applyOperation', () => {
+		// @if CK_DEBUG_ENGINE // 	dumpTrees( this.document, this.document.version );
+		// @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
 	}
 
 	/**
@@ -165,6 +172,8 @@ export default class Model {
 				return callback( this._currentWriter );
 			}
 		} catch ( err ) {
+			// @if CK_DEBUG // throw err;
+			/* istanbul ignore next */
 			CKEditorError.rethrowUnexpectedError( err, this );
 		}
 	}
@@ -217,6 +226,8 @@ export default class Model {
 				this._runPendingChanges();
 			}
 		} catch ( err ) {
+			// @if CK_DEBUG // throw err;
+			/* istanbul ignore next */
 			CKEditorError.rethrowUnexpectedError( err, this );
 		}
 	}
@@ -233,9 +244,35 @@ export default class Model {
 	 * @param {module:engine/model/operation/operation~Operation} operation The operation to apply.
 	 */
 	applyOperation( operation ) {
+		// @if CK_DEBUG_ENGINE // console.log( 'Applying ' + operation );
+
+		// @if CK_DEBUG_ENGINE // if ( !this._operationLogs ) {
+		// @if CK_DEBUG_ENGINE //	this._operationLogs = [];
+		// @if CK_DEBUG_ENGINE // }
+
+		// @if CK_DEBUG_ENGINE // this._operationLogs.push( JSON.stringify( operation ) );
+
+		// @if CK_DEBUG_ENGINE //if ( !this._appliedOperations ) {
+		// @if CK_DEBUG_ENGINE //	this._appliedOperations = [];
+		// @if CK_DEBUG_ENGINE //}
+
+		// @if CK_DEBUG_ENGINE //this._appliedOperations.push( operation );
+
 		operation._execute();
 	}
 
+	// @if CK_DEBUG_ENGINE // getAppliedOperation() {
+	// @if CK_DEBUG_ENGINE //	if ( !this._appliedOperations ) {
+	// @if CK_DEBUG_ENGINE //		return '';
+	// @if CK_DEBUG_ENGINE //	}
+
+	// @if CK_DEBUG_ENGINE //	return this._appliedOperations.map( JSON.stringify ).join( '-------' );
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // createReplayer( stringifiedOperations ) {
+	// @if CK_DEBUG_ENGINE //	return new OperationReplayer( this, '-------', stringifiedOperations );
+	// @if CK_DEBUG_ENGINE // }
+
 	/**
 	 * Inserts content at the position in the editor specified by the selection, as one would expect the paste
 	 * functionality to work.
@@ -269,7 +306,7 @@ export default class Model {
 	 *
 	 *		// Let's create a document fragment containing such content as:
 	 *		//
-	 *		// <paragrap>foo</paragraph>
+	 *		// <paragraph>foo</paragraph>
 	 *		// <blockQuote>
 	 *		//    <paragraph>bar</paragraph>
 	 *		// </blockQuote>

+ 1 - 1
packages/ckeditor5-engine/src/model/node.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/nodelist.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 7 - 1
packages/ckeditor5-engine/src/model/operation/attributeoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -194,4 +194,10 @@ export default class AttributeOperation extends Operation {
 	static fromJSON( json, document ) {
 		return new AttributeOperation( Range.fromJSON( json.range, document ), json.key, json.oldValue, json.newValue, json.baseVersion );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `AttributeOperation( ${ this.baseVersion } ): ` +
+	// @if CK_DEBUG_ENGINE //		`"${ this.key }": ${ JSON.stringify( this.oldValue ) }` +
+	// @if CK_DEBUG_ENGINE //		` -> ${ JSON.stringify( this.newValue ) }, ${ this.range }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 11 - 1
packages/ckeditor5-engine/src/model/operation/detachoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -12,6 +12,8 @@ import Range from '../range';
 import { _remove } from './utils';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
+// @if CK_DEBUG_ENGINE // const ModelRange = require( '../range' ).default;
+
 /**
  * Operation to permanently remove node from detached root.
  * Note this operation is only a local operation and won't be send to the other clients.
@@ -90,4 +92,12 @@ export default class DetachOperation extends Operation {
 	static get className() {
 		return 'DetachOperation';
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // const range = ModelRange._createFromPositionAndShift( this.sourcePosition, this.howMany );
+	// @if CK_DEBUG_ENGINE //	const nodes = Array.from( range.getItems() );
+	// @if CK_DEBUG_ENGINE //	const nodeString = nodes.length > 1 ? `[ ${ nodes.length } ]` : nodes[ 0 ];
+
+	// @if CK_DEBUG_ENGINE //	return `DetachOperation( ${ this.baseVersion } ): ${ nodeString } -> ${ range }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 7 - 1
packages/ckeditor5-engine/src/model/operation/insertoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -179,4 +179,10 @@ export default class InsertOperation extends Operation {
 
 		return insert;
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	const nodeString = this.nodes.length > 1 ? `[ ${ this.nodes.length } ]` : this.nodes.getNode( 0 );
+
+	// @if CK_DEBUG_ENGINE //	return `InsertOperation( ${ this.baseVersion } ): ${ nodeString } -> ${ this.position }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 6 - 1
packages/ckeditor5-engine/src/model/operation/markeroperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -146,4 +146,9 @@ export default class MarkerOperation extends Operation {
 			json.baseVersion
 		);
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `MarkerOperation( ${ this.baseVersion } ): ` +
+	// @if CK_DEBUG_ENGINE //		`"${ this.name }": ${ this.oldRange } -> ${ this.newRange }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 7 - 1
packages/ckeditor5-engine/src/model/operation/mergeoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -210,4 +210,10 @@ export default class MergeOperation extends Operation {
 
 		return new this( sourcePosition, json.howMany, targetPosition, graveyardPosition, json.baseVersion );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `MergeOperation( ${ this.baseVersion } ): ` +
+	// @if CK_DEBUG_ENGINE //		`${ this.sourcePosition } -> ${ this.targetPosition }` +
+	// @if CK_DEBUG_ENGINE //		` ( ${ this.howMany } ), ${ this.graveyardPosition }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 9 - 1
packages/ckeditor5-engine/src/model/operation/moveoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -14,6 +14,8 @@ import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import compareArrays from '@ckeditor/ckeditor5-utils/src/comparearrays';
 import { _move } from './utils';
 
+// @if CK_DEBUG_ENGINE // const ModelRange = require( '../range' ).default;
+
 /**
  * Operation to move a range of {@link module:engine/model/item~Item model items}
  * to given {@link module:engine/model/position~Position target position}.
@@ -198,4 +200,10 @@ export default class MoveOperation extends Operation {
 
 		return new this( sourcePosition, json.howMany, targetPosition, json.baseVersion );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	const range = ModelRange._createFromPositionAndShift( this.sourcePosition, this.howMany );
+
+	// @if CK_DEBUG_ENGINE //	return `MoveOperation( ${ this.baseVersion } ): ${ range } -> ${ this.targetPosition }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 5 - 1
packages/ckeditor5-engine/src/model/operation/nooperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -51,4 +51,8 @@ export default class NoOperation extends Operation {
 	static get className() {
 		return 'NoOperation';
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `NoOperation( ${ this.baseVersion } )`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 5 - 1
packages/ckeditor5-engine/src/model/operation/operation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -132,4 +132,8 @@ export default class Operation {
 	static fromJSON( json ) {
 		return new this( json.baseVersion );
 	}
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( this.toString() );
+	// @if CK_DEBUG_ENGINE // }
 }

+ 1 - 1
packages/ckeditor5-engine/src/model/operation/operationfactory.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 6 - 1
packages/ckeditor5-engine/src/model/operation/renameoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -147,4 +147,9 @@ export default class RenameOperation extends Operation {
 	static fromJSON( json, document ) {
 		return new RenameOperation( Position.fromJSON( json.position, document ), json.oldName, json.newName, json.baseVersion );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `RenameOperation( ${ this.baseVersion } ): ` +
+	// @if CK_DEBUG_ENGINE //		`${ this.position }: "${ this.oldName }" -> "${ this.newName }"`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 7 - 1
packages/ckeditor5-engine/src/model/operation/rootattributeoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -207,4 +207,10 @@ export default class RootAttributeOperation extends Operation {
 
 		return new RootAttributeOperation( document.getRoot( json.root ), json.key, json.oldValue, json.newValue, json.baseVersion );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `RootAttributeOperation( ${ this.baseVersion } ): ` +
+	// @if CK_DEBUG_ENGINE //		`"${ this.key }": ${ JSON.stringify( this.oldValue ) }` +
+	// @if CK_DEBUG_ENGINE //		` -> ${ JSON.stringify( this.newValue ) }, ${ this.root.rootName }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 7 - 1
packages/ckeditor5-engine/src/model/operation/splitoperation.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -250,4 +250,10 @@ export default class SplitOperation extends Operation {
 
 		return split;
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `SplitOperation( ${ this.baseVersion } ): ${ this.splitPosition } ` +
+	// @if CK_DEBUG_ENGINE //		`( ${ this.howMany } ) -> ${ this.insertionPosition }` +
+	// @if CK_DEBUG_ENGINE //		`${ this.graveyardPosition ? ' with ' + this.graveyardPosition : '' }`;
+	// @if CK_DEBUG_ENGINE // }
 }

+ 1 - 1
packages/ckeditor5-engine/src/model/operation/transform.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/operation/utils.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 9 - 1
packages/ckeditor5-engine/src/model/position.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -1014,6 +1014,14 @@ export default class Position {
 
 		return new Position( doc.getRoot( json.root ), json.path, json.stickiness );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `${ this.root } [ ${ this.path.join( ', ' ) } ]`;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( 'ModelPosition: ' + this );
+	// @if CK_DEBUG_ENGINE // }
 }
 
 /**

+ 9 - 2
packages/ckeditor5-engine/src/model/range.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -387,7 +387,6 @@ export default class Range {
 	 * You may specify additional options for the tree walker. See {@link module:engine/model/treewalker~TreeWalker} for
 	 * a full list of available options.
 	 *
-	 * @method getItems
 	 * @param {Object} options Object with configuration options. See {@link module:engine/model/treewalker~TreeWalker}.
 	 * @returns {Iterable.<module:engine/model/item~Item>}
 	 */
@@ -958,4 +957,12 @@ export default class Range {
 	static fromJSON( json, doc ) {
 		return new this( Position.fromJSON( json.start, doc ), Position.fromJSON( json.end, doc ) );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `${ this.root } [ ${ this.start.path.join( ', ' ) } ] - [ ${ this.end.path.join( ', ' ) } ]`;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( 'ModelPosition: ' + this );
+	// @if CK_DEBUG_ENGINE // }
 }

+ 9 - 1
packages/ckeditor5-engine/src/model/rootelement.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -98,4 +98,12 @@ export default class RootElement extends Element {
 	toJSON() {
 		return this.rootName;
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return this.rootName;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( 'ModelRootElement: ' + this );
+	// @if CK_DEBUG_ENGINE // }
 }

+ 18 - 1
packages/ckeditor5-engine/src/model/schema.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -1268,6 +1268,23 @@ export class SchemaContext {
 	endsWith( query ) {
 		return Array.from( this.getNames() ).join( ' ' ).endsWith( query );
 	}
+
+	/**
+	 * Checks whether the context starts with the given nodes.
+	 *
+	 *		const ctx = new SchemaContext( [ rootElement, paragraphElement, textNode ] );
+	 *
+	 *		ctx.endsWith( '$root' ); // -> true
+	 *		ctx.endsWith( '$root paragraph' ); // -> true
+	 *		ctx.endsWith( '$text' ); // -> false
+	 *		ctx.endsWith( 'paragraph' ); // -> false
+	 *
+	 * @param {String} query
+	 * @returns {Boolean}
+	 */
+	startsWith( query ) {
+		return Array.from( this.getNames() ).join( ' ' ).startsWith( query );
+	}
 }
 
 /**

+ 1 - 1
packages/ckeditor5-engine/src/model/selection.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 15 - 1
packages/ckeditor5-engine/src/model/text.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -9,6 +9,8 @@
 
 import Node from './node';
 
+// @if CK_DEBUG_ENGINE // const { convertMapToStringifiedObject } = require( '../dev-utils/utils' );
+
 /**
  * Model text node. Type of {@link module:engine/model/node~Node node} that contains {@link module:engine/model/text~Text#data text data}.
  *
@@ -115,4 +117,16 @@ export default class Text extends Node {
 	static fromJSON( json ) {
 		return new Text( json.data, json.attributes );
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `#${ this.data }`;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logExtended() {
+	// @if CK_DEBUG_ENGINE // 	console.log( `ModelText: ${ this }, attrs: ${ convertMapToStringifiedObject( this.getAttributes() ) }` );
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( 'ModelText: ' + this );
+	// @if CK_DEBUG_ENGINE // }
 }

+ 16 - 1
packages/ckeditor5-engine/src/model/textproxy.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -9,6 +9,8 @@
 
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
+// @if CK_DEBUG_ENGINE // const { convertMapToStringifiedObject } = require( '../dev-utils/utils' );
+
 /**
  * `TextProxy` represents a part of {@link module:engine/model/text~Text text node}.
  *
@@ -267,4 +269,17 @@ export default class TextProxy {
 	getAttributeKeys() {
 		return this.textNode.getAttributeKeys();
 	}
+
+	// @if CK_DEBUG_ENGINE // toString() {
+	// @if CK_DEBUG_ENGINE // 	return `#${ this.data }`;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // log() {
+	// @if CK_DEBUG_ENGINE // 	console.log( 'ModelTextProxy: ' + this );
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logExtended() {
+	// @if CK_DEBUG_ENGINE // 	console.log( `ModelTextProxy: ${ this }, ` +
+	// @if CK_DEBUG_ENGINE // 		`attrs: ${ convertMapToStringifiedObject( this.getAttributes() ) }` );
+	// @if CK_DEBUG_ENGINE // }
 }

+ 1 - 1
packages/ckeditor5-engine/src/model/treewalker.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 2
packages/ckeditor5-engine/src/model/utils/deletecontent.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -23,7 +23,6 @@ import DocumentSelection from '../documentselection';
  * should be performed.
  * @param {module:engine/model/selection~Selection|module:engine/model/documentselection~DocumentSelection} selection
  * Selection of which the content should be deleted.
- * @param {module:engine/model/batch~Batch} batch Batch to which the operations will be added.
  * @param {Object} [options]
  * @param {Boolean} [options.leaveUnmerged=false] Whether to merge elements after removing the content of the selection.
  *

+ 1 - 1
packages/ckeditor5-engine/src/model/utils/getselectedcontent.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/utils/insertcontent.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/utils/modifyselection.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/model/utils/selection-post-fixer.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 2 - 2
packages/ckeditor5-engine/src/model/writer.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -1328,7 +1328,7 @@ export default class Writer {
 	 */
 	_assertWriterUsedCorrectly() {
 		/**
-		 * Trying to use a writer outside a {@link module:engine/model/model~Model#change `change()` or
+		 * Trying to use a writer outside a {@link module:engine/model/model~Model#change `change()`} or
 		 * {@link module:engine/model/model~Model#enqueueChange `enqueueChange()`} blocks.
 		 *
 		 * The writer can only be used inside these blocks which ensures that the model

+ 1 - 1
packages/ckeditor5-engine/src/utils/bindtwostepcarettoattribute.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/attributeelement.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/containerelement.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 24 - 1
packages/ckeditor5-engine/src/view/document.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -11,6 +11,9 @@ import DocumentSelection from './documentselection';
 import Collection from '@ckeditor/ckeditor5-utils/src/collection';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import ObservableMixin from '@ckeditor/ckeditor5-utils/src/observablemixin';
+import StylesMap from './stylesmap';
+
+// @if CK_DEBUG_ENGINE // const { logDocument } = require( '../dev-utils/utils' );
 
 /**
  * Document class creates an abstract layer over the content editable area, contains a tree of view elements and
@@ -158,6 +161,22 @@ export default class Document {
 		this.stopListening();
 	}
 
+	/**
+	 * Adds a style processor normalization rules.
+	 *
+	 * The available style processors:
+	 *
+	 * * background: {@link module:engine/view/styles/background~addBackgroundRules}
+	 * * border: {@link module:engine/view/styles/border~addBorderRules}
+	 * * margin: {@link module:engine/view/styles/margin~addMarginRules}
+	 * * padding: {@link module:engine/view/styles/padding~addPaddingRules}
+	 *
+	 * @param {Function} callback
+	 */
+	addStyleProcessorRules( callback ) {
+		callback( StylesMap._styleProcessor );
+	}
+
 	/**
 	 * Performs post-fixer loops. Executes post-fixer callbacks as long as none of them has done any changes to the model.
 	 *
@@ -185,6 +204,10 @@ export default class Document {
 	 *
 	 * @event layoutChanged
 	 */
+
+	// @if CK_DEBUG_ENGINE // log( version ) {
+	// @if CK_DEBUG_ENGINE //	logDocument( this, version );
+	// @if CK_DEBUG_ENGINE // }
 }
 
 mix( Document, ObservableMixin );

+ 21 - 1
packages/ckeditor5-engine/src/view/documentfragment.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -210,6 +210,26 @@ export default class DocumentFragment {
 	_fireChange( type, node ) {
 		this.fire( 'change:' + type, node );
 	}
+
+	// @if CK_DEBUG_ENGINE // printTree() {
+	// @if CK_DEBUG_ENGINE //	let string = 'ViewDocumentFragment: [';
+
+	// @if CK_DEBUG_ENGINE //	for ( const child of this.getChildren() ) {
+	// @if CK_DEBUG_ENGINE //		if ( child.is( 'text' ) ) {
+	// @if CK_DEBUG_ENGINE //			string += '\n' + '\t'.repeat( 1 ) + child.data;
+	// @if CK_DEBUG_ENGINE //		} else {
+	// @if CK_DEBUG_ENGINE //			string += '\n' + child.printTree( 1 );
+	// @if CK_DEBUG_ENGINE //		}
+	// @if CK_DEBUG_ENGINE //	}
+
+	// @if CK_DEBUG_ENGINE //	string += '\n]';
+
+	// @if CK_DEBUG_ENGINE //	return string;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logTree() {
+	// @if CK_DEBUG_ENGINE // 	console.log( this.printTree() );
+	// @if CK_DEBUG_ENGINE // }
 }
 
 mix( DocumentFragment, EmitterMixin );

+ 1 - 1
packages/ckeditor5-engine/src/view/documentselection.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 14 - 2
packages/ckeditor5-engine/src/view/domconverter.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -807,11 +807,23 @@ export default class DomConverter {
 	 *
 	 * **Note:**: For the `'nbsp'` mode the method also checks context of a node so it cannot be a detached node.
 	 *
+	 * **Note:** A special case in the `'nbsp'` mode exists where the `<br>` in `<p><br></p>` is treated as a block filler.
+	 *
 	 * @param {Node} domNode DOM node to check.
 	 * @returns {Boolean} True if a node is considered a block filler for given mode.
 	 */
 	isBlockFiller( domNode ) {
-		return this.blockFillerMode == 'br' ? domNode.isEqualNode( BR_FILLER_REF ) : isNbspBlockFiller( domNode, this.blockElements );
+		if ( this.blockFillerMode == 'br' ) {
+			return domNode.isEqualNode( BR_FILLER_REF );
+		}
+
+		// Special case for <p><br></p> in which case the <br> should be treated as filler even
+		// when we're in the 'nbsp' mode. See ckeditor5#5564.
+		if ( domNode.tagName === 'BR' && hasBlockParent( domNode, this.blockElements ) && domNode.parentNode.childNodes.length === 1 ) {
+			return true;
+		}
+
+		return isNbspBlockFiller( domNode, this.blockElements );
 	}
 
 	/**

+ 42 - 26
packages/ckeditor5-engine/src/view/downcastwriter.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -81,7 +81,7 @@ export default class DowncastWriter {
 	 *		writer.setSelection( position );
 	 *
 	 * 		// Sets collapsed selection at the position of given item and offset.
-	 *		const paragraph = writer.createContainerElement( 'paragraph' );
+	 *		const paragraph = writer.createContainerElement( 'p' );
 	 *		writer.setSelection( paragraph, offset );
 	 *
 	 * Creates a range inside an {@link module:engine/view/element~Element element} which starts before the first child of
@@ -152,13 +152,13 @@ export default class DowncastWriter {
 	 * Creates new {@link module:engine/view/attributeelement~AttributeElement}.
 	 *
 	 *		writer.createAttributeElement( 'strong' );
-	 *		writer.createAttributeElement( 'strong', { alignment: 'center' } );
+	 *		writer.createAttributeElement( 'a', { href: 'foo.bar' } );
 	 *
 	 *		// Make `<a>` element contain other attributes element so the `<a>` element is not broken.
 	 *		writer.createAttributeElement( 'a', { href: 'foo.bar' }, { priority: 5 } );
 	 *
 	 *		// Set `id` of a marker element so it is not joined or merged with "normal" elements.
-	 *		writer.createAttributeElement( 'span', { class: 'myMarker' }, { id: 'marker:my' } );
+	 *		writer.createAttributeElement( 'span', { class: 'my-marker' }, { id: 'marker:my' } );
 	 *
 	 * @param {String} name Name of the element.
 	 * @param {Object} [attributes] Element's attributes.
@@ -184,8 +184,16 @@ export default class DowncastWriter {
 	/**
 	 * Creates new {@link module:engine/view/containerelement~ContainerElement}.
 	 *
-	 *		writer.createContainerElement( 'paragraph' );
-	 *		writer.createContainerElement( 'paragraph', { alignment: 'center' } );
+	 *		writer.createContainerElement( 'p' );
+	 *
+	 *		// Create element with custom attributes.
+	 *		writer.createContainerElement( 'div', { id: 'foo-bar', 'data-baz': '123' } );
+	 *
+	 *		// Create element with custom styles.
+	 *		writer.createContainerElement( 'p', { style: 'font-weight: bold; padding-bottom: 10px' } );
+	 *
+	 *		// Create element with custom classes.
+	 *		writer.createContainerElement( 'p', { class: 'foo bar baz' } );
 	 *
 	 * @param {String} name Name of the element.
 	 * @param {Object} [attributes] Elements attributes.
@@ -199,7 +207,7 @@ export default class DowncastWriter {
 	 * Creates new {@link module:engine/view/editableelement~EditableElement}.
 	 *
 	 *		writer.createEditableElement( 'div' );
-	 *		writer.createEditableElement( 'div', { alignment: 'center' } );
+	 *		writer.createEditableElement( 'div', { id: 'foo-1234' } );
 	 *
 	 * @param {String} name Name of the element.
 	 * @param {Object} [attributes] Elements attributes.
@@ -216,7 +224,7 @@ export default class DowncastWriter {
 	 * Creates new {@link module:engine/view/emptyelement~EmptyElement}.
 	 *
 	 *		writer.createEmptyElement( 'img' );
-	 *		writer.createEmptyElement( 'img', { alignment: 'center' } );
+	 *		writer.createEmptyElement( 'img', { id: 'foo-1234' } );
 	 *
 	 * @param {String} name Name of the element.
 	 * @param {Object} [attributes] Elements attributes.
@@ -230,7 +238,7 @@ export default class DowncastWriter {
 	 * Creates new {@link module:engine/view/uielement~UIElement}.
 	 *
 	 *		writer.createUIElement( 'span' );
-	 *		writer.createUIElement( 'span', { alignment: 'center' } );
+	 *		writer.createUIElement( 'span', { id: 'foo-1234' } );
 	 *
 	 * Custom render function can be provided as third parameter:
 	 *
@@ -316,6 +324,10 @@ export default class DowncastWriter {
 	 *			position: 'fixed'
 	 *		}, element );
 	 *
+	 * **Note**: The passed style can be normalized if
+	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * See {@link module:engine/view/stylesmap~StylesMap#set `StylesMap#set()`} for details.
+	 *
 	 * @param {String|Object} property Property name or object with key - value pairs.
 	 * @param {String} [value] Value to set. This parameter is ignored if object is provided as the first parameter.
 	 * @param {module:engine/view/element~Element} element Element to set styles on.
@@ -331,9 +343,13 @@ export default class DowncastWriter {
 	/**
 	 * Removes specified style from the element.
 	 *
-	 *		writer.removeStyle( 'color', element );  // Removes 'color' style.
+	 *		writer.removeStyle( 'color', element ); // Removes 'color' style.
 	 *		writer.removeStyle( [ 'color', 'border-top' ], element ); // Removes both 'color' and 'border-top' styles.
 	 *
+	 * **Note**: This method can work with normalized style names if
+	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
+	 *
 	 * @param {Array.<String>|String} property
 	 * @param {module:engine/view/element~Element} element
 	 */
@@ -370,10 +386,10 @@ export default class DowncastWriter {
 	 *
 	 * In following examples `<p>` is a container, `<b>` and `<u>` are attribute nodes:
 	 *
-	 *        <p>foo<b><u>bar{}</u></b></p> -> <p>foo<b><u>bar</u></b>[]</p>
-	 *        <p>foo<b><u>{}bar</u></b></p> -> <p>foo{}<b><u>bar</u></b></p>
-	 *        <p>foo<b><u>b{}ar</u></b></p> -> <p>foo<b><u>b</u></b>[]<b><u>ar</u></b></p>
-	 *        <p><b>fo{o</b><u>ba}r</u></p> -> <p><b>fo</b><b>o</b><u>ba</u><u>r</u></b></p>
+	 *		<p>foo<b><u>bar{}</u></b></p> -> <p>foo<b><u>bar</u></b>[]</p>
+	 *		<p>foo<b><u>{}bar</u></b></p> -> <p>foo{}<b><u>bar</u></b></p>
+	 *		<p>foo<b><u>b{}ar</u></b></p> -> <p>foo<b><u>b</u></b>[]<b><u>ar</u></b></p>
+	 *		<p><b>fo{o</b><u>ba}r</u></p> -> <p><b>fo</b><b>o</b><u>ba</u><u>r</u></b></p>
 	 *
 	 * **Note:** {@link module:engine/view/documentfragment~DocumentFragment DocumentFragment} is treated like a container.
 	 *
@@ -416,10 +432,10 @@ export default class DowncastWriter {
 	 * has to be directly inside container element and cannot be in root. Does not break if position is at the beginning
 	 * or at the end of it's parent element.
 	 *
-	 *        <p>foo^bar</p> -> <p>foo</p><p>bar</p>
-	 *        <div><p>foo</p>^<p>bar</p></div> -> <div><p>foo</p></div><div><p>bar</p></div>
-	 *        <p>^foobar</p> -> ^<p>foobar</p>
-	 *        <p>foobar^</p> -> <p>foobar</p>^
+	 *		<p>foo^bar</p> -> <p>foo</p><p>bar</p>
+	 *		<div><p>foo</p>^<p>bar</p></div> -> <div><p>foo</p></div><div><p>bar</p></div>
+	 *		<p>^foobar</p> -> ^<p>foobar</p>
+	 *		<p>foobar^</p> -> <p>foobar</p>^
 	 *
 	 * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#breakAttributes breakAttributes} and
 	 * {@link module:engine/view/downcastwriter~DowncastWriter#breakContainer breakContainer} is that `breakAttributes` breaks all
@@ -480,14 +496,14 @@ export default class DowncastWriter {
 	 *
 	 * In following examples `<p>` is a container and `<b>` is an attribute element:
 	 *
-	 *        <p>foo[]bar</p> -> <p>foo{}bar</p>
-	 *        <p><b>foo</b>[]<b>bar</b></p> -> <p><b>foo{}bar</b></p>
-	 *        <p><b foo="bar">a</b>[]<b foo="baz">b</b></p> -> <p><b foo="bar">a</b>[]<b foo="baz">b</b></p>
+	 *		<p>foo[]bar</p> -> <p>foo{}bar</p>
+	 *		<p><b>foo</b>[]<b>bar</b></p> -> <p><b>foo{}bar</b></p>
+	 *		<p><b foo="bar">a</b>[]<b foo="baz">b</b></p> -> <p><b foo="bar">a</b>[]<b foo="baz">b</b></p>
 	 *
 	 * It will also take care about empty attributes when merging:
 	 *
-	 *        <p><b>[]</b></p> -> <p>[]</p>
-	 *        <p><b>foo</b><i>[]</i><b>bar</b></p> -> <p><b>foo{}bar</b></p>
+	 *		<p><b>[]</b></p> -> <p>[]</p>
+	 *		<p><b>foo</b><i>[]</i><b>bar</b></p> -> <p><b>foo{}bar</b></p>
 	 *
 	 * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#mergeAttributes mergeAttributes} and
 	 * {@link module:engine/view/downcastwriter~DowncastWriter#mergeContainers mergeContainers} is that `mergeAttributes` merges two
@@ -553,8 +569,8 @@ export default class DowncastWriter {
 	 * Merges two {@link module:engine/view/containerelement~ContainerElement container elements} that are before and after given position.
 	 * Precisely, the element after the position is removed and it's contents are moved to element before the position.
 	 *
-	 *        <p>foo</p>^<p>bar</p> -> <p>foo^bar</p>
-	 *        <div>foo</div>^<p>bar</p> -> <div>foo^bar</div>
+	 *		<p>foo</p>^<p>bar</p> -> <p>foo^bar</p>
+	 *		<div>foo</div>^<p>bar</p> -> <div>foo^bar</div>
 	 *
 	 * **Note:** Difference between {@link module:engine/view/downcastwriter~DowncastWriter#mergeAttributes mergeAttributes} and
 	 * {@link module:engine/view/downcastwriter~DowncastWriter#mergeContainers mergeContainers} is that `mergeAttributes` merges two
@@ -1026,7 +1042,7 @@ export default class DowncastWriter {
 	 *		const selection = writer.createSelection( position );
 	 *
 	 *		// Creates collapsed selection at the position of given item and offset.
-	 *		const paragraph = writer.createContainerElement( 'paragraph' );
+	 *		const paragraph = writer.createContainerElement( 'p' );
 	 *		const selection = writer.createSelection( paragraph, offset );
 	 *
 	 *		// Creates a range inside an {@link module:engine/view/element~Element element} which starts before the

+ 1 - 1
packages/ckeditor5-engine/src/view/editableelement.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 118 - 116
packages/ckeditor5-engine/src/view/element.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
@@ -14,6 +14,9 @@ import objectToMap from '@ckeditor/ckeditor5-utils/src/objecttomap';
 import isIterable from '@ckeditor/ckeditor5-utils/src/isiterable';
 import Matcher from './matcher';
 import { isPlainObject } from 'lodash-es';
+import StylesMap from './stylesmap';
+
+// @if CK_DEBUG_ENGINE // const { convertMapToTags } = require( '../dev-utils/utils' );
 
 /**
  * View element.
@@ -103,16 +106,17 @@ export default class Element extends Node {
 		}
 
 		/**
-		 * Map of styles.
+		 * Normalized styles.
 		 *
 		 * @protected
-		 * @member {Map} module:engine/view/element~Element#_styles
+		 * @member {module:engine/view/stylesmap~StylesMap} module:engine/view/element~Element#_styles
 		 */
-		this._styles = new Map();
+		this._styles = new StylesMap();
 
 		if ( this._attrs.has( 'style' ) ) {
 			// Remove style attribute and handle it by styles map.
-			parseInlineStyles( this._styles, this._attrs.get( 'style' ) );
+			this._styles.setTo( this._attrs.get( 'style' ) );
+
 			this._attrs.delete( 'style' );
 		}
 
@@ -219,7 +223,7 @@ export default class Element extends Node {
 			yield 'class';
 		}
 
-		if ( this._styles.size > 0 ) {
+		if ( !this._styles.isEmpty ) {
 			yield 'style';
 		}
 
@@ -241,7 +245,7 @@ export default class Element extends Node {
 			yield [ 'class', this.getAttribute( 'class' ) ];
 		}
 
-		if ( this._styles.size > 0 ) {
+		if ( !this._styles.isEmpty ) {
 			yield [ 'style', this.getAttribute( 'style' ) ];
 		}
 	}
@@ -262,17 +266,9 @@ export default class Element extends Node {
 		}
 
 		if ( key == 'style' ) {
-			if ( this._styles.size > 0 ) {
-				let styleString = '';
-
-				for ( const [ property, value ] of this._styles ) {
-					styleString += `${ property }:${ value };`;
-				}
+			const inlineStyle = this._styles.toString();
 
-				return styleString;
-			}
-
-			return undefined;
+			return inlineStyle == '' ? undefined : inlineStyle;
 		}
 
 		return this._attrs.get( key );
@@ -290,7 +286,7 @@ export default class Element extends Node {
 		}
 
 		if ( key == 'style' ) {
-			return this._styles.size > 0;
+			return !this._styles.isEmpty;
 		}
 
 		return this._attrs.has( key );
@@ -340,8 +336,11 @@ export default class Element extends Node {
 		}
 
 		// Check if styles are the same.
-		for ( const [ property, value ] of this._styles ) {
-			if ( !otherElement._styles.has( property ) || otherElement._styles.get( property ) !== value ) {
+		for ( const property of this._styles.getStyleNames() ) {
+			if (
+				!otherElement._styles.has( property ) ||
+				otherElement._styles.getAsString( property ) !== this._styles.getAsString( property )
+			) {
 				return false;
 			}
 		}
@@ -378,14 +377,67 @@ export default class Element extends Node {
 	}
 
 	/**
-	 * Returns style value for given property.
-	 * Undefined is returned if style does not exist.
+	 * Returns style value for the given property mae.
+	 * If the style does not exist `undefined` is returned.
+	 *
+	 * **Note**: This method can work with normalized style names if
+	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * See {@link module:engine/view/stylesmap~StylesMap#getAsString `StylesMap#getAsString()`} for details.
+	 *
+	 * For an element with style set to `'margin:1px'`:
+	 *
+	 *		// Enable 'margin' shorthand processing:
+	 *		editor.editing.view.document.addStyleProcessorRules( addMarginRules );
+	 *
+	 *		const element = view.change( writer => {
+	 *			const element = writer.createElement();
+	 *			writer.setStyle( 'margin', '1px' );
+	 *			writer.setStyle( 'margin-bottom', '3em' );
+	 *
+	 *			return element;
+	 *		} );
+	 *
+	 *		element.getStyle( 'margin' ); // -> 'margin: 1px 1px 3em;'
 	 *
 	 * @param {String} property
 	 * @returns {String|undefined}
 	 */
 	getStyle( property ) {
-		return this._styles.get( property );
+		return this._styles.getAsString( property );
+	}
+
+	/**
+	 * Returns a normalized style object or single style value.
+	 *
+	 * For an element with style set to: margin:1px 2px 3em;
+	 *
+	 *		element.getNormalizedStyle( 'margin' ) );
+	 *
+	 * will return:
+	 *
+	 *		{
+	 *			top: '1px',
+	 *			right: '2px',
+	 *			bottom: '3em',
+	 *			left: '2px'    // a normalized value from margin shorthand
+	 *		}
+	 *
+	 * and reading for single style value:
+	 *
+	 *		styles.getNormalizedStyle( 'margin-left' );
+	 *
+	 * Will return a `2px` string.
+	 *
+	 * **Note**: This method will return normalized values only if
+	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * See {@link module:engine/view/stylesmap~StylesMap#getNormalized `StylesMap#getNormalized()`} for details.
+	 *
+	 *
+	 * @param {String} property Name of CSS property
+	 * @returns {Object|String|undefined}
+	 */
+	getNormalizedStyle( property ) {
+		return this._styles.getNormalized( property );
 	}
 
 	/**
@@ -394,7 +446,7 @@ export default class Element extends Node {
 	 * @returns {Iterable.<String>}
 	 */
 	getStyleNames() {
-		return this._styles.keys();
+		return this._styles.getStyleNames();
 	}
 
 	/**
@@ -479,18 +531,18 @@ export default class Element extends Node {
 	 *		// returns 'foo class="baz" style="border-color:white;color:red" apple="20" banana="10"'
 	 *		element.getIdentity();
 	 *
-	 * NOTE: Classes, styles and other attributes are sorted alphabetically.
+	 * **Note**: Classes, styles and other attributes are sorted alphabetically.
 	 *
 	 * @returns {String}
 	 */
 	getIdentity() {
 		const classes = Array.from( this._classes ).sort().join( ',' );
-		const styles = Array.from( this._styles ).map( i => `${ i[ 0 ] }:${ i[ 1 ] }` ).sort().join( ';' );
+		const styles = this._styles.toString();
 		const attributes = Array.from( this._attrs ).map( i => `${ i[ 0 ] }="${ i[ 1 ] }"` ).sort().join( ' ' );
 
 		return this.name +
 			( classes == '' ? '' : ` class="${ classes }"` ) +
-			( styles == '' ? '' : ` style="${ styles }"` ) +
+			( !styles ? '' : ` style="${ styles }"` ) +
 			( attributes == '' ? '' : ` ${ attributes }` );
 	}
 
@@ -517,7 +569,7 @@ export default class Element extends Node {
 		// Classes and styles are cloned separately - this solution is faster than adding them back to attributes and
 		// parse once again in constructor.
 		cloned._classes = new Set( this._classes );
-		cloned._styles = new Map( this._styles );
+		cloned._styles.set( this._styles.getNormalized() );
 
 		// Clone custom properties.
 		cloned._customProperties = new Map( this._customProperties );
@@ -614,7 +666,7 @@ export default class Element extends Node {
 		if ( key == 'class' ) {
 			parseClasses( this._classes, value );
 		} else if ( key == 'style' ) {
-			parseInlineStyles( this._styles, value );
+			this._styles.setTo( value );
 		} else {
 			this._attrs.set( key, value );
 		}
@@ -645,7 +697,7 @@ export default class Element extends Node {
 
 		// Remove style attribute.
 		if ( key == 'style' ) {
-			if ( this._styles.size > 0 ) {
+			if ( !this._styles.isEmpty ) {
 				this._styles.clear();
 
 				return true;
@@ -703,6 +755,10 @@ export default class Element extends Node {
 	 *			position: 'fixed'
 	 *		} );
 	 *
+	 * **Note**: This method can work with normalized style names if
+	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * See {@link module:engine/view/stylesmap~StylesMap#set `StylesMap#set()`} for details.
+	 *
 	 * @see module:engine/view/downcastwriter~DowncastWriter#setStyle
 	 * @protected
 	 * @param {String|Object} property Property name or object with key - value pairs.
@@ -712,15 +768,7 @@ export default class Element extends Node {
 	_setStyle( property, value ) {
 		this._fireChange( 'attributes', this );
 
-		if ( isPlainObject( property ) ) {
-			const keys = Object.keys( property );
-
-			for ( const key of keys ) {
-				this._styles.set( key, property[ key ] );
-			}
-		} else {
-			this._styles.set( property, value );
-		}
+		this._styles.set( property, value );
 	}
 
 	/**
@@ -729,6 +777,10 @@ export default class Element extends Node {
 	 *		element._removeStyle( 'color' );  // Removes 'color' style.
 	 *		element._removeStyle( [ 'color', 'border-top' ] ); // Removes both 'color' and 'border-top' styles.
 	 *
+	 * **Note**: This method can work with normalized style names if
+	 * {@link module:engine/view/document~Document#addStyleProcessorRules a particular style processor rule is enabled}.
+	 * See {@link module:engine/view/stylesmap~StylesMap#remove `StylesMap#remove()`} for details.
+	 *
 	 * @see module:engine/view/downcastwriter~DowncastWriter#removeStyle
 	 * @protected
 	 * @param {Array.<String>|String} property
@@ -738,7 +790,7 @@ export default class Element extends Node {
 		this._fireChange( 'attributes', this );
 
 		property = Array.isArray( property ) ? property : [ property ];
-		property.forEach( name => this._styles.delete( name ) );
+		property.forEach( name => this._styles.remove( name ) );
 	}
 
 	/**
@@ -772,6 +824,32 @@ export default class Element extends Node {
 	 * @abstract
 	 * @method module:engine/view/element~Element#getFillerOffset
 	 */
+
+	// @if CK_DEBUG_ENGINE // printTree( level = 0) {
+	// @if CK_DEBUG_ENGINE // 	let string = '';
+
+	// @if CK_DEBUG_ENGINE //	string += '\t'.repeat( level ) + `<${ this.name }${ convertMapToTags( this.getAttributes() ) }>`;
+
+	// @if CK_DEBUG_ENGINE //	for ( const child of this.getChildren() ) {
+	// @if CK_DEBUG_ENGINE //		if ( child.is( 'text' ) ) {
+	// @if CK_DEBUG_ENGINE //			string += '\n' + '\t'.repeat( level + 1 ) + child.data;
+	// @if CK_DEBUG_ENGINE //		} else {
+	// @if CK_DEBUG_ENGINE //			string += '\n' + child.printTree( level + 1 );
+	// @if CK_DEBUG_ENGINE //		}
+	// @if CK_DEBUG_ENGINE //	}
+
+	// @if CK_DEBUG_ENGINE //	if ( this.childCount ) {
+	// @if CK_DEBUG_ENGINE //		string += '\n' + '\t'.repeat( level );
+	// @if CK_DEBUG_ENGINE //	}
+
+	// @if CK_DEBUG_ENGINE //	string += `</${ this.name }>`;
+
+	// @if CK_DEBUG_ENGINE //	return string;
+	// @if CK_DEBUG_ENGINE // }
+
+	// @if CK_DEBUG_ENGINE // logTree() {
+	// @if CK_DEBUG_ENGINE // 	console.log( this.printTree() );
+	// @if CK_DEBUG_ENGINE // }
 }
 
 // Parses attributes provided to the element constructor before they are applied to an element. If attributes are passed
@@ -798,82 +876,6 @@ function parseAttributes( attrs ) {
 	return attrs;
 }
 
-// Parses inline styles and puts property - value pairs into styles map.
-// Styles map is cleared before insertion.
-//
-// @param {Map.<String, String>} stylesMap Map to insert parsed properties and values.
-// @param {String} stylesString Styles to parse.
-function parseInlineStyles( stylesMap, stylesString ) {
-	// `null` if no quote was found in input string or last found quote was a closing quote. See below.
-	let quoteType = null;
-	let propertyNameStart = 0;
-	let propertyValueStart = 0;
-	let propertyName = null;
-
-	stylesMap.clear();
-
-	// Do not set anything if input string is empty.
-	if ( stylesString === '' ) {
-		return;
-	}
-
-	// Fix inline styles that do not end with `;` so they are compatible with algorithm below.
-	if ( stylesString.charAt( stylesString.length - 1 ) != ';' ) {
-		stylesString = stylesString + ';';
-	}
-
-	// Seek the whole string for "special characters".
-	for ( let i = 0; i < stylesString.length; i++ ) {
-		const char = stylesString.charAt( i );
-
-		if ( quoteType === null ) {
-			// No quote found yet or last found quote was a closing quote.
-			switch ( char ) {
-				case ':':
-					// Most of time colon means that property name just ended.
-					// Sometimes however `:` is found inside property value (for example in background image url).
-					if ( !propertyName ) {
-						// Treat this as end of property only if property name is not already saved.
-						// Save property name.
-						propertyName = stylesString.substr( propertyNameStart, i - propertyNameStart );
-						// Save this point as the start of property value.
-						propertyValueStart = i + 1;
-					}
-
-					break;
-
-				case '"':
-				case '\'':
-					// Opening quote found (this is an opening quote, because `quoteType` is `null`).
-					quoteType = char;
-
-					break;
-
-				case ';': {
-					// Property value just ended.
-					// Use previously stored property value start to obtain property value.
-					const propertyValue = stylesString.substr( propertyValueStart, i - propertyValueStart );
-
-					if ( propertyName ) {
-						// Save parsed part.
-						stylesMap.set( propertyName.trim(), propertyValue.trim() );
-					}
-
-					propertyName = null;
-
-					// Save this point as property name start. Property name starts immediately after previous property value ends.
-					propertyNameStart = i + 1;
-
-					break;
-				}
-			}
-		} else if ( char === quoteType ) {
-			// If a quote char is found and it is a closing quote, mark this fact by `null`-ing `quoteType`.
-			quoteType = null;
-		}
-	}
-}
-
 // Parses class attribute and puts all classes into classes set.
 // Classes set s cleared before insertion.
 //

+ 1 - 1
packages/ckeditor5-engine/src/view/elementdefinition.jsdoc

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/emptyelement.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/filler.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/item.jsdoc

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/matcher.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/node.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/observer/clickobserver.js

@@ -1,6 +1,6 @@
 
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/observer/compositionobserver.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/observer/domeventdata.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/observer/domeventobserver.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

+ 1 - 1
packages/ckeditor5-engine/src/view/observer/fakeselectionobserver.js

@@ -1,5 +1,5 @@
 /**
- * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 

Некоторые файлы не были показаны из-за большого количества измененных файлов