Jelajahi Sumber

Merge branch 'master' into t/ckeditor5/1514

Maciej Gołaszewski 6 tahun lalu
induk
melakukan
6489d2adfa
100 mengubah file dengan 655 tambahan dan 349 penghapusan
  1. 1 1
      packages/ckeditor5-engine/.eslintrc.js
  2. 6 7
      packages/ckeditor5-engine/.travis.yml
  3. 56 0
      packages/ckeditor5-engine/CHANGELOG.md
  4. 1 2
      packages/ckeditor5-engine/README.md
  5. 17 16
      packages/ckeditor5-engine/package.json
  6. 6 5
      packages/ckeditor5-engine/src/controller/datacontroller.js
  7. 1 1
      packages/ckeditor5-engine/src/controller/editingcontroller.js
  8. 8 5
      packages/ckeditor5-engine/src/conversion/conversion.js
  9. 1 1
      packages/ckeditor5-engine/src/conversion/conversionhelpers.js
  10. 1 1
      packages/ckeditor5-engine/src/conversion/downcastdispatcher.js
  11. 4 5
      packages/ckeditor5-engine/src/conversion/downcasthelpers.js
  12. 17 5
      packages/ckeditor5-engine/src/conversion/mapper.js
  13. 1 1
      packages/ckeditor5-engine/src/conversion/modelconsumable.js
  14. 4 4
      packages/ckeditor5-engine/src/conversion/upcastdispatcher.js
  15. 1 1
      packages/ckeditor5-engine/src/conversion/upcasthelpers.js
  16. 2 2
      packages/ckeditor5-engine/src/conversion/viewconsumable.js
  17. 1 1
      packages/ckeditor5-engine/src/dataprocessor/basichtmlwriter.js
  18. 1 1
      packages/ckeditor5-engine/src/dataprocessor/dataprocessor.jsdoc
  19. 1 1
      packages/ckeditor5-engine/src/dataprocessor/htmldataprocessor.js
  20. 1 1
      packages/ckeditor5-engine/src/dataprocessor/htmlwriter.js
  21. 1 1
      packages/ckeditor5-engine/src/dataprocessor/xmldataprocessor.js
  22. 1 1
      packages/ckeditor5-engine/src/dev-utils/enableenginedebug.js
  23. 1 1
      packages/ckeditor5-engine/src/dev-utils/model.js
  24. 1 1
      packages/ckeditor5-engine/src/dev-utils/operationreplayer.js
  25. 1 1
      packages/ckeditor5-engine/src/dev-utils/view.js
  26. 1 1
      packages/ckeditor5-engine/src/model/batch.js
  27. 27 1
      packages/ckeditor5-engine/src/model/differ.js
  28. 18 1
      packages/ckeditor5-engine/src/model/document.js
  29. 1 1
      packages/ckeditor5-engine/src/model/documentfragment.js
  30. 36 37
      packages/ckeditor5-engine/src/model/documentselection.js
  31. 1 1
      packages/ckeditor5-engine/src/model/element.js
  32. 1 1
      packages/ckeditor5-engine/src/model/history.js
  33. 1 1
      packages/ckeditor5-engine/src/model/item.jsdoc
  34. 3 2
      packages/ckeditor5-engine/src/model/liveposition.js
  35. 1 1
      packages/ckeditor5-engine/src/model/liverange.js
  36. 7 7
      packages/ckeditor5-engine/src/model/markercollection.js
  37. 4 3
      packages/ckeditor5-engine/src/model/model.js
  38. 3 3
      packages/ckeditor5-engine/src/model/node.js
  39. 13 7
      packages/ckeditor5-engine/src/model/nodelist.js
  40. 4 2
      packages/ckeditor5-engine/src/model/operation/attributeoperation.js
  41. 2 2
      packages/ckeditor5-engine/src/model/operation/detachoperation.js
  42. 3 2
      packages/ckeditor5-engine/src/model/operation/insertoperation.js
  43. 1 1
      packages/ckeditor5-engine/src/model/operation/markeroperation.js
  44. 4 4
      packages/ckeditor5-engine/src/model/operation/mergeoperation.js
  45. 5 5
      packages/ckeditor5-engine/src/model/operation/moveoperation.js
  46. 1 1
      packages/ckeditor5-engine/src/model/operation/nooperation.js
  47. 1 1
      packages/ckeditor5-engine/src/model/operation/operation.js
  48. 1 1
      packages/ckeditor5-engine/src/model/operation/operationfactory.js
  49. 5 3
      packages/ckeditor5-engine/src/model/operation/renameoperation.js
  50. 5 1
      packages/ckeditor5-engine/src/model/operation/rootattributeoperation.js
  51. 5 5
      packages/ckeditor5-engine/src/model/operation/splitoperation.js
  52. 1 1
      packages/ckeditor5-engine/src/model/operation/transform.js
  53. 11 5
      packages/ckeditor5-engine/src/model/operation/utils.js
  54. 28 7
      packages/ckeditor5-engine/src/model/position.js
  55. 15 2
      packages/ckeditor5-engine/src/model/range.js
  56. 1 1
      packages/ckeditor5-engine/src/model/rootelement.js
  57. 20 9
      packages/ckeditor5-engine/src/model/schema.js
  58. 13 5
      packages/ckeditor5-engine/src/model/selection.js
  59. 1 1
      packages/ckeditor5-engine/src/model/text.js
  60. 3 3
      packages/ckeditor5-engine/src/model/textproxy.js
  61. 6 2
      packages/ckeditor5-engine/src/model/treewalker.js
  62. 1 1
      packages/ckeditor5-engine/src/model/utils/deletecontent.js
  63. 1 1
      packages/ckeditor5-engine/src/model/utils/getselectedcontent.js
  64. 10 3
      packages/ckeditor5-engine/src/model/utils/insertcontent.js
  65. 1 1
      packages/ckeditor5-engine/src/model/utils/modifyselection.js
  66. 1 1
      packages/ckeditor5-engine/src/model/utils/selection-post-fixer.js
  67. 70 27
      packages/ckeditor5-engine/src/model/writer.js
  68. 1 1
      packages/ckeditor5-engine/src/utils/bindtwostepcarettoattribute.js
  69. 3 2
      packages/ckeditor5-engine/src/view/attributeelement.js
  70. 1 1
      packages/ckeditor5-engine/src/view/containerelement.js
  71. 1 1
      packages/ckeditor5-engine/src/view/document.js
  72. 1 1
      packages/ckeditor5-engine/src/view/documentfragment.js
  73. 1 1
      packages/ckeditor5-engine/src/view/documentselection.js
  74. 31 20
      packages/ckeditor5-engine/src/view/domconverter.js
  75. 54 49
      packages/ckeditor5-engine/src/view/downcastwriter.js
  76. 2 2
      packages/ckeditor5-engine/src/view/editableelement.js
  77. 1 1
      packages/ckeditor5-engine/src/view/element.js
  78. 1 1
      packages/ckeditor5-engine/src/view/elementdefinition.jsdoc
  79. 5 2
      packages/ckeditor5-engine/src/view/emptyelement.js
  80. 1 1
      packages/ckeditor5-engine/src/view/filler.js
  81. 1 1
      packages/ckeditor5-engine/src/view/item.jsdoc
  82. 1 1
      packages/ckeditor5-engine/src/view/matcher.js
  83. 2 2
      packages/ckeditor5-engine/src/view/node.js
  84. 1 1
      packages/ckeditor5-engine/src/view/observer/clickobserver.js
  85. 1 1
      packages/ckeditor5-engine/src/view/observer/compositionobserver.js
  86. 1 1
      packages/ckeditor5-engine/src/view/observer/domeventdata.js
  87. 1 1
      packages/ckeditor5-engine/src/view/observer/domeventobserver.js
  88. 1 1
      packages/ckeditor5-engine/src/view/observer/fakeselectionobserver.js
  89. 1 1
      packages/ckeditor5-engine/src/view/observer/focusobserver.js
  90. 44 0
      packages/ckeditor5-engine/src/view/observer/inputobserver.js
  91. 2 2
      packages/ckeditor5-engine/src/view/observer/keyobserver.js
  92. 1 1
      packages/ckeditor5-engine/src/view/observer/mouseobserver.js
  93. 1 1
      packages/ckeditor5-engine/src/view/observer/mutationobserver.js
  94. 1 1
      packages/ckeditor5-engine/src/view/observer/observer.js
  95. 1 1
      packages/ckeditor5-engine/src/view/observer/selectionobserver.js
  96. 2 4
      packages/ckeditor5-engine/src/view/placeholder.js
  97. 9 7
      packages/ckeditor5-engine/src/view/position.js
  98. 4 4
      packages/ckeditor5-engine/src/view/range.js
  99. 3 3
      packages/ckeditor5-engine/src/view/renderer.js
  100. 1 1
      packages/ckeditor5-engine/src/view/rooteditableelement.js

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

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

+ 6 - 7
packages/ckeditor5-engine/.travis.yml

@@ -1,12 +1,8 @@
 sudo: required
 dist: trusty
 addons:
+  chrome: stable
   firefox: latest
-  apt:
-    sources:
-    - google-chrome
-    packages:
-    - google-chrome-stable
 language: node_js
 node_js:
 - '8'
@@ -16,6 +12,7 @@ branches:
   except:
   - stable
 before_install:
+- export START_TIME=$( date +%s )
 - export DISPLAY=:99.0
 - sh -e /etc/init.d/xvfb start
 - npm i -g yarn
@@ -26,7 +23,9 @@ script:
 - ckeditor5-dev-tests-travis
 after_success:
 - ckeditor5-dev-tests-save-revision
+after_script:
+- export END_TIME=$( date +%s )
+- ckeditor5-dev-tests-notify-travis-status
 env:
   global:
-  - secure: CSFKXnTb2oQW8YIjh+SWXYSYzx1jcIg1wGcl6jWrbaOU06JHEO77YLKlhMxLfRniFtilBEksK4ugIcn+lihykhHleMxI27+kifILdLLZKvwWlZiDrtpJSzLk08SpRNDwwthSlHVKrxbHVFE1K9SYpX4s3qTpxullDMPgc13OP4c=
-  - secure: WGdwDEavTOIE/yw8pdpWmx3Cehm2/GN8gZMimuot8sWPbOZRuPh+2lnVWpASLe25qJUKKeLWYLRHccpMLKbyweiordSEBwzNrOGOFyUuaDPITNZZrp5ekh2vHBp1+mwhRpRekdyWdzzVsm01RkFrnJqQlaXxX9i/r9Tu4EsK54M=
+  - secure: GMesvAe0tAa8QfdhGApzmzjhf9mpfjL6jMx8aFqpRli5tuyX9JsjNjFELnU/MMlNDyySPwXAuQVrvcWkCFy9a3n705tGt/VdmStCrTdwV2oRNmNKqGgFovmiK3an+RAIts2PX2iguDjwiogqkAW0FstWAMdG0Yk4AVUxKC1niIo=

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

@@ -1,6 +1,62 @@
 Changelog
 =========
 
+## [13.2.1](https://github.com/ckeditor/ckeditor5-engine/compare/v13.2.0...v13.2.1) (2019-07-10)
+
+Internal changes only (updated dependencies, documentation, etc.).
+
+
+## [13.2.0](https://github.com/ckeditor/ckeditor5-engine/compare/v13.1.1...v13.2.0) (2019-07-04)
+
+### Features
+
+* Added `view.Document#event:beforeinput`. ([c74c3d6](https://github.com/ckeditor/ckeditor5-engine/commit/c74c3d6))
+* Introduced the `type` parameter to the `model.createBatch()` method. ([389b72e](https://github.com/ckeditor/ckeditor5-engine/commit/389b72e))
+
+### Bug fixes
+
+* `model.Writer#insert()` will no longer crash when the data to set contains markers that are already in the editor content. Closes [#1721](https://github.com/ckeditor/ckeditor5-engine/issues/1721). ([4ff0656](https://github.com/ckeditor/ckeditor5-engine/commit/4ff0656))
+* Selection will not change during forbidden copy-paste operation inside table cell. Closes [ckeditor/ckeditor5#1380](https://github.com/ckeditor/ckeditor5/issues/1380). ([ab15b17](https://github.com/ckeditor/ckeditor5-engine/commit/ab15b17))
+
+### Other changes
+
+* Changed how ` `s are generated on the view->DOM rendering. Closes [#1747](https://github.com/ckeditor/ckeditor5-engine/issues/1747). ([da5670a](https://github.com/ckeditor/ckeditor5-engine/commit/da5670a))
+
+
+## [13.1.1](https://github.com/ckeditor/ckeditor5-engine/compare/v13.1.0...v13.1.1) (2019-06-05)
+
+### Bug fixes
+
+* Prevented from losing selection attributes between operations (fixes a bug with text composition). Closes https://github.com/ckeditor/ckeditor5-typing/issues/188. ([42dcb25](https://github.com/ckeditor/ckeditor5-engine/commit/42dcb25))
+
+### Other changes
+
+* Added more cases of affected markers on merging in `model.Writer`. Closes [#1738](https://github.com/ckeditor/ckeditor5-engine/issues/1738). ([01ff6e6](https://github.com/ckeditor/ckeditor5-engine/commit/01ff6e6))
+
+
+## [13.1.0](https://github.com/ckeditor/ckeditor5-engine/compare/v13.0.0...v13.1.0) (2019-04-10)
+
+### Features
+
+* `Model#insertContent()` will return a range affected by the insertion. ([f4e4644](https://github.com/ckeditor/ckeditor5-engine/commit/f4e4644))
+
+  In `Model#deleteContent()`, added `doNotAutoparagraph` flag to `options`.
+  `Position` and `LivePosition` static creators should handle `stickiness` param.
+* Added possibility to refresh the marker with no changes through `Writer#updateMarker()` method. Closes [#1649](https://github.com/ckeditor/ckeditor5-engine/issues/1649). ([cf56d90](https://github.com/ckeditor/ckeditor5-engine/commit/cf56d90))
+* Introduced `Schema#setAttributeProperties()` and `Schema#getAttributeProperties()` methods. Closes [ckeditor/ckeditor5#1659](https://github.com/ckeditor/ckeditor5/issues/1659). ([1c6f83a](https://github.com/ckeditor/ckeditor5-engine/commit/1c6f83a))
+* Introduced `UpcastConversionApi#getSplitParts()`. Also, provided a way to set upcast conversion helper fired for every view element. Closes https://github.com/ckeditor/ckeditor5/issues/1580. Closes https://github.com/ckeditor/ckeditor5/issues/1581. ([d0ee3f4](https://github.com/ckeditor/ckeditor5-engine/commit/d0ee3f4))
+
+### Bug fixes
+
+* `view.DowncastWriter` will now correctly wrap and unwrap nested attribute elements. Closes [#1716](https://github.com/ckeditor/ckeditor5-engine/issues/1716). Closes [ckeditor/ckeditor5-font#30](https://github.com/ckeditor/ckeditor5-font/issues/30). ([4126359](https://github.com/ckeditor/ckeditor5-engine/commit/4126359))
+* Attribute and remove change on intersecting ranges done in the same change block will be correctly saved in `Differ` and downcasted. Closes [ckeditor/ckeditor5#1645](https://github.com/ckeditor/ckeditor5/issues/1645). ([b2a9d86](https://github.com/ckeditor/ckeditor5-engine/commit/b2a9d86))
+* Editor will no longer crash during undo in some pasting+remove scenarios. Closes [#1701](https://github.com/ckeditor/ckeditor5-engine/issues/1701). ([ca619e7](https://github.com/ckeditor/ckeditor5-engine/commit/ca619e7))
+* Made sure that `Schema#getAttributeProperties()` always returns an object. Closes [#1717](https://github.com/ckeditor/ckeditor5-engine/issues/1717). ([b3f5da3](https://github.com/ckeditor/ckeditor5-engine/commit/b3f5da3))
+* Markers should be now correctly upcasted inside any element. Closes [#1697](https://github.com/ckeditor/ckeditor5-engine/issues/1697). ([3706324](https://github.com/ckeditor/ckeditor5-engine/commit/3706324))
+* `Model#deleteContent()` will not throw anymore if the passed selection is in the graveyard root. Closes [#1706](https://github.com/ckeditor/ckeditor5-engine/issues/1706). ([bd875c7](https://github.com/ckeditor/ckeditor5-engine/commit/bd875c7))
+* The editor will not throw an error when updating an empty fake selection container. Closes [#1714](https://github.com/ckeditor/ckeditor5-engine/issues/1714). ([c48f5a4](https://github.com/ckeditor/ckeditor5-engine/commit/c48f5a4))
+
+
 ## [13.0.0](https://github.com/ckeditor/ckeditor5-engine/compare/v12.0.0...v13.0.0) (2019-02-28)
 
 ### Features

+ 1 - 2
packages/ckeditor5-engine/README.md

@@ -4,7 +4,6 @@ CKEditor 5 editing engine
 [![Join the chat at https://gitter.im/ckeditor/ckeditor5](https://badges.gitter.im/ckeditor/ckeditor5.svg)](https://gitter.im/ckeditor/ckeditor5?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
 [![npm version](https://badge.fury.io/js/%40ckeditor%2Fckeditor5-engine.svg)](https://www.npmjs.com/package/@ckeditor/ckeditor5-engine)
 [![Build Status](https://travis-ci.org/ckeditor/ckeditor5-engine.svg?branch=master)](https://travis-ci.org/ckeditor/ckeditor5-engine)
-[![BrowserStack Status](https://automate.browserstack.com/automate/badge.svg?badge_key=d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)](https://automate.browserstack.com/public-build/d3hvenZqQVZERFQ5d09FWXdyT0ozVXhLaVltRFRjTTUyZGpvQWNmWVhUUT0tLUZqNlJ1YWRUd0RvdEVOaEptM1B2Q0E9PQ==--c9d3dee40b9b4471ff3fb516d9ecf8d09292c7e0)
 [![Coverage Status](https://coveralls.io/repos/github/ckeditor/ckeditor5-engine/badge.svg?branch=master)](https://coveralls.io/github/ckeditor/ckeditor5-engine?branch=master)
 <br>
 [![Dependency Status](https://david-dm.org/ckeditor/ckeditor5-engine/status.svg)](https://david-dm.org/ckeditor/ckeditor5-engine)
@@ -32,4 +31,4 @@ Additionally, refer to the [`@ckeditor/ckeditor5-engine` package](https://ckedit
 
 ## License
 
-Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file.
+Licensed under the terms of [GNU General Public License Version 2 or later](http://www.gnu.org/licenses/gpl.html). For full details about the license, please check the `LICENSE.md` file or [https://ckeditor.com/legal/ckeditor-oss-license](https://ckeditor.com/legal/ckeditor-oss-license).

+ 17 - 16
packages/ckeditor5-engine/package.json

@@ -1,6 +1,6 @@
 {
   "name": "@ckeditor/ckeditor5-engine",
-  "version": "13.0.0",
+  "version": "13.2.1",
   "description": "The editing engine of CKEditor 5 – the best browser-based rich text editor.",
   "keywords": [
     "wysiwyg",
@@ -21,25 +21,26 @@
     "ckeditor 5"
   ],
   "dependencies": {
-    "@ckeditor/ckeditor5-utils": "^12.0.0",
+    "@ckeditor/ckeditor5-utils": "^13.0.1",
     "lodash-es": "^4.17.10"
   },
   "devDependencies": {
-    "@ckeditor/ckeditor5-basic-styles": "^11.0.0",
-    "@ckeditor/ckeditor5-block-quote": "^11.0.0",
-    "@ckeditor/ckeditor5-core": "^12.0.0",
-    "@ckeditor/ckeditor5-editor-classic": "^12.0.0",
-    "@ckeditor/ckeditor5-enter": "^11.0.0",
-    "@ckeditor/ckeditor5-essentials": "^11.0.0",
-    "@ckeditor/ckeditor5-heading": "^11.0.0",
-    "@ckeditor/ckeditor5-link": "^11.0.0",
-    "@ckeditor/ckeditor5-list": "^12.0.0",
-    "@ckeditor/ckeditor5-paragraph": "^11.0.0",
-    "@ckeditor/ckeditor5-typing": "^12.0.0",
-    "@ckeditor/ckeditor5-undo": "^11.0.0",
-    "@ckeditor/ckeditor5-widget": "^11.0.0",
+    "@ckeditor/ckeditor5-basic-styles": "^11.1.3",
+    "@ckeditor/ckeditor5-block-quote": "^11.1.2",
+    "@ckeditor/ckeditor5-core": "^12.2.1",
+    "@ckeditor/ckeditor5-editor-classic": "^12.1.3",
+    "@ckeditor/ckeditor5-enter": "^11.0.4",
+    "@ckeditor/ckeditor5-essentials": "^11.0.4",
+    "@ckeditor/ckeditor5-heading": "^11.0.4",
+    "@ckeditor/ckeditor5-link": "^11.1.1",
+    "@ckeditor/ckeditor5-list": "^12.0.4",
+    "@ckeditor/ckeditor5-paragraph": "^11.0.4",
+    "@ckeditor/ckeditor5-theme-lark": "^14.1.1",
+    "@ckeditor/ckeditor5-typing": "^12.1.1",
+    "@ckeditor/ckeditor5-undo": "^11.0.4",
+    "@ckeditor/ckeditor5-widget": "^11.0.4",
     "eslint": "^5.5.0",
-    "eslint-config-ckeditor5": "^1.0.11",
+    "eslint-config-ckeditor5": "^2.0.0",
     "husky": "^1.3.1",
     "lint-staged": "^7.0.0"
   },

+ 6 - 5
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -142,7 +142,7 @@ export default class DataController {
 			 *
 			 * @error datacontroller-get-non-existent-root
 			 */
-			throw new CKEditorError( 'datacontroller-get-non-existent-root: Attempting to get data from a non-existing root.' );
+			throw new CKEditorError( 'datacontroller-get-non-existent-root: Attempting to get data from a non-existing root.', this );
 		}
 
 		const root = this.model.document.getRoot( rootName );
@@ -240,7 +240,7 @@ export default class DataController {
 			 *
 			 * @error datacontroller-init-document-not-empty
 			 */
-			throw new CKEditorError( 'datacontroller-init-document-not-empty: Trying to set initial data to not empty document.' );
+			throw new CKEditorError( 'datacontroller-init-document-not-empty: Trying to set initial data to not empty document.', this );
 		}
 
 		let initialData = {};
@@ -262,7 +262,7 @@ export default class DataController {
 			 *
 			 * @error datacontroller-init-non-existent-root
 			 */
-			throw new CKEditorError( 'datacontroller-init-non-existent-root: Attempting to init data on a non-existing root.' );
+			throw new CKEditorError( 'datacontroller-init-non-existent-root: Attempting to init data on a non-existing root.', this );
 		}
 
 		this.model.enqueueChange( 'transparent', writer => {
@@ -297,6 +297,7 @@ export default class DataController {
 	 */
 	set( data ) {
 		let newData = {};
+
 		if ( typeof data === 'string' ) {
 			newData.main = data; // Default root is 'main'. To set data on a different root, object should be passed.
 		} else {
@@ -315,7 +316,7 @@ export default class DataController {
 			 *
 			 * @error datacontroller-set-non-existent-root
 			 */
-			throw new CKEditorError( 'datacontroller-set-non-existent-root: Attempting to set data on a non-existing root.' );
+			throw new CKEditorError( 'datacontroller-set-non-existent-root: Attempting to set data on a non-existing root.', this );
 		}
 
 		this.model.enqueueChange( 'transparent', writer => {

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

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

+ 8 - 5
packages/ckeditor5-engine/src/conversion/conversion.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -105,8 +105,11 @@ export default class Conversion {
 			 *
 			 * @error conversion-add-alias-dispatcher-not-registered
 			 */
-			throw new CKEditorError( 'conversion-add-alias-dispatcher-not-registered: ' +
-				'Trying to register and alias for a dispatcher that nas not been registered.' );
+			throw new CKEditorError(
+				'conversion-add-alias-dispatcher-not-registered: ' +
+				'Trying to register and alias for a dispatcher that nas not been registered.',
+				this
+			);
 		}
 
 		this._createConversionHelpers( { name: alias, dispatchers: [ dispatcher ], isDowncast } );
@@ -180,7 +183,7 @@ export default class Conversion {
 			 *
 			 * @error conversion-for-unknown-group
 			 */
-			throw new CKEditorError( 'conversion-for-unknown-group: Trying to add a converter to an unknown dispatchers group.' );
+			throw new CKEditorError( 'conversion-for-unknown-group: Trying to add a converter to an unknown dispatchers group.', this );
 		}
 
 		return this._helpers.get( groupName );
@@ -585,7 +588,7 @@ export default class Conversion {
 			 *
 			 * @error conversion-group-exists
 			 */
-			throw new CKEditorError( 'conversion-group-exists: Trying to register a group name that has already been registered.' );
+			throw new CKEditorError( 'conversion-group-exists: Trying to register a group name that has already been registered.', this );
 		}
 
 		const helpers = isDowncast ? new DowncastHelpers( dispatchers ) : new UpcastHelpers( dispatchers );

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

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

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

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

+ 4 - 5
packages/ckeditor5-engine/src/conversion/downcasthelpers.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 import ModelRange from '../model/range';
@@ -730,9 +730,8 @@ function removeUIElement() {
 			return;
 		}
 
-		conversionApi.mapper.unbindElementsFromMarkerName( data.markerName );
-
 		for ( const element of elements ) {
+			conversionApi.mapper.unbindElementFromMarkerName( element, data.markerName );
 			conversionApi.writer.clear( conversionApi.writer.createRangeOn( element ), element );
 		}
 
@@ -1030,9 +1029,9 @@ function removeHighlight( highlightDescriptor ) {
 			return;
 		}
 
-		conversionApi.mapper.unbindElementsFromMarkerName( data.markerName );
-
 		for ( const element of elements ) {
+			conversionApi.mapper.unbindElementFromMarkerName( element, data.markerName );
+
 			if ( element.is( 'attributeElement' ) ) {
 				conversionApi.writer.unwrap( conversionApi.writer.createRangeOn( element ), viewHighlightElement );
 			} else {

+ 17 - 5
packages/ckeditor5-engine/src/conversion/mapper.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -21,7 +21,10 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  * Maps elements, positions and markers between {@link module:engine/view/document~Document the view} and
  * {@link module:engine/model/model the model}.
  *
- * Mapper use bound elements to find corresponding elements and positions, so, to get proper results,
+ * The instance of the Mapper used for the editing pipeline is available in
+ * {@link module:engine/controller/editingcontroller~EditingController#mapper `editor.editing.mapper`}.
+ *
+ * Mapper uses bound elements to find corresponding elements and positions, so, to get proper results,
  * all model elements should be {@link module:engine/conversion/mapper~Mapper#bindElements bound}.
  *
  * To map complex model to/from view relations, you may provide custom callbacks for
@@ -171,12 +174,21 @@ export default class Mapper {
 	}
 
 	/**
-	 * Unbinds all elements from given marker name.
+	 * Unbinds an element from given marker name.
 	 *
+	 * @param {module:engine/view/element~Element} element Element to unbind.
 	 * @param {String} name Marker name.
 	 */
-	unbindElementsFromMarkerName( name ) {
-		this._markerNameToElements.delete( name );
+	unbindElementFromMarkerName( element, name ) {
+		const elements = this._markerNameToElements.get( name );
+
+		if ( elements ) {
+			elements.delete( element );
+
+			if ( elements.size == 0 ) {
+				this._markerNameToElements.delete( name );
+			}
+		}
 	}
 
 	/**

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -41,7 +41,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  * Examples of providing callbacks for `UpcastDispatcher`:
  *
  *		// Converter for links (<a>).
- *		upcastDispatcher.on( 'element:a', ( evt, data, conversionApi ) => {
+ *		editor.data.upcastDispatcher.on( 'element:a', ( evt, data, conversionApi ) => {
  *			if ( conversionApi.consumable.consume( data.viewItem, { name: true, attributes: [ 'href' ] } ) ) {
  *				// <a> element is inline and is represented by an attribute in the model.
  *				// This is why we need to convert only children.
@@ -56,7 +56,7 @@ import mix from '@ckeditor/ckeditor5-utils/src/mix';
  *		} );
  *
  *		// Convert all elements which have no custom converter into paragraph (autoparagraphing).
- *  	data.viewToModel.on( 'element', ( evt, data, conversionApi ) => {
+ *  	editor.data.upcastDispatcher.on( 'element', ( evt, data, conversionApi ) => {
  *  	 	// When element is already consumed by higher priority converters then do nothing.
  *  	 	if ( conversionApi.consumable.test( data.viewItem, { name: data.viewItem.name } ) ) {
  *  	 			const paragraph = conversionApi.writer.createElement( 'paragraph' );
@@ -231,7 +231,7 @@ export default class UpcastDispatcher {
 			 *
 			 * @error view-conversion-dispatcher-incorrect-result
 			 */
-			throw new CKEditorError( 'view-conversion-dispatcher-incorrect-result: Incorrect conversion result was dropped.' );
+			throw new CKEditorError( 'view-conversion-dispatcher-incorrect-result: Incorrect conversion result was dropped.', this );
 		}
 
 		return { modelRange: data.modelRange, modelCursor: data.modelCursor };

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

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

+ 2 - 2
packages/ckeditor5-engine/src/conversion/viewconsumable.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -503,7 +503,7 @@ class ViewElementConsumables {
 				 *
 				 * @error viewconsumable-invalid-attribute
 				 */
-				throw new CKEditorError( 'viewconsumable-invalid-attribute: Classes and styles should be handled separately.' );
+				throw new CKEditorError( 'viewconsumable-invalid-attribute: Classes and styles should be handled separately.', this );
 			}
 
 			consumables.set( name, true );

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -110,6 +110,32 @@ export default class Differ {
 		return this._changesInElement.size == 0 && this._changedMarkers.size == 0;
 	}
 
+	/**
+	 * Marks given `item` in differ to be "refreshed". It means that the item will be marked as removed and inserted in the differ changes
+	 * set, so it will be effectively re-converted when differ changes will be handled by a dispatcher.
+	 *
+	 * @param {module:engine/model/item~Item} item Item to refresh.
+	 */
+	refreshItem( item ) {
+		if ( this._isInInsertedElement( item.parent ) ) {
+			return;
+		}
+
+		this._markRemove( item.parent, item.startOffset, item.offsetSize );
+		this._markInsert( item.parent, item.startOffset, item.offsetSize );
+
+		const range = Range._createOn( item );
+
+		for ( const marker of this._markerCollection.getMarkersIntersectingRange( range ) ) {
+			const markerRange = marker.getRange();
+
+			this.bufferMarkerChange( marker.name, markerRange, markerRange, marker.affectsData );
+		}
+
+		// Clear cache after each buffered operation as it is no longer valid.
+		this._cachedChanges = null;
+	}
+
 	/**
 	 * Buffers the given operation. An operation has to be buffered before it is executed.
 	 *

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -127,6 +127,7 @@ export default class Document {
 				 */
 				throw new CKEditorError(
 					'model-document-applyOperation-wrong-version: Only operations with matching versions can be applied.',
+					this,
 					{ operation }
 				);
 			}
@@ -201,6 +202,7 @@ export default class Document {
 			 */
 			throw new CKEditorError(
 				'model-document-createRoot-name-exists: Root with specified name already exists.',
+				this,
 				{ name: rootName }
 			);
 		}
@@ -307,12 +309,19 @@ export default class Document {
 		if ( this._hasDocumentChangedFromTheLastChangeBlock() ) {
 			this._callPostFixers( writer );
 
+			// Refresh selection attributes according to the final position in the model after the change.
+			this.selection.refresh();
+
 			if ( this.differ.hasDataChanges() ) {
 				this.fire( 'change:data', writer.batch );
 			} else {
 				this.fire( 'change', writer.batch );
 			}
 
+			// Theoretically, it is not necessary to refresh selection after change event because
+			// post-fixers are the last who should change the model, but just in case...
+			this.selection.refresh();
+
 			this.differ.reset();
 		}
 
@@ -391,6 +400,14 @@ export default class Document {
 
 		do {
 			for ( const callback of this._postFixers ) {
+				// Ensure selection attributes are up to date before each post-fixer.
+				// https://github.com/ckeditor/ckeditor5-engine/issues/1673.
+				//
+				// It might be good to refresh the selection after each operation but at the moment it leads
+				// to losing attributes for composition or and spell checking
+				// https://github.com/ckeditor/ckeditor5-typing/issues/188
+				this.selection.refresh();
+
 				wasFixed = callback( writer );
 
 				if ( wasFixed ) {

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

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

+ 36 - 37
packages/ckeditor5-engine/src/model/documentselection.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -359,6 +359,14 @@ export default class DocumentSelection {
 		return this._selection.hasAttribute( key );
 	}
 
+	/**
+	 * Refreshes selection attributes and markers according to the current position in the model.
+	 */
+	refresh() {
+		this._selection._updateMarkers();
+		this._selection._updateAttributes( false );
+	}
+
 	/**
 	 * Checks whether object is of given type following the convention set by
 	 * {@link module:engine/model/node~Node#is `Node#is()`}.
@@ -598,6 +606,26 @@ class LiveSelection extends Selection {
 		// @type {Set}
 		this._overriddenGravityRegister = new Set();
 
+		// Ensure selection is correct after each operation.
+		this.listenTo( this._model, 'applyOperation', ( evt, args ) => {
+			const operation = args[ 0 ];
+
+			if ( !operation.isDocumentOperation || operation.type == 'marker' || operation.type == 'rename' || operation.type == 'noop' ) {
+				return;
+			}
+
+			while ( this._fixGraveyardRangesData.length ) {
+				const { liveRange, sourcePosition } = this._fixGraveyardRangesData.shift();
+
+				this._fixGraveyardSelection( liveRange, sourcePosition );
+			}
+
+			if ( this._hasChangedRange ) {
+				this._hasChangedRange = false;
+				this.fire( 'change:range', { directChange: false } );
+			}
+		}, { priority: 'lowest' } );
+
 		// Ensure selection is correct and up to date after each range change.
 		this.on( 'change:range', () => {
 			for ( const range of this.getRanges() ) {
@@ -611,42 +639,17 @@ class LiveSelection extends Selection {
 					 */
 					throw new CKEditorError(
 						'document-selection-wrong-position: Range from document selection starts or ends at incorrect position.',
+						this,
 						{ range }
 					);
 				}
 			}
-
-			this._updateMarkers();
-			this._updateAttributes( false );
 		} );
 
 		// Update markers data stored by the selection after each marker change.
 		this.listenTo( this._model.markers, 'update', () => this._updateMarkers() );
 
-		// Ensure selection is correct and up to date after each operation.
-		this.listenTo( this._model, 'applyOperation', ( evt, args ) => {
-			const operation = args[ 0 ];
-
-			if ( !operation.isDocumentOperation || operation.type == 'marker' || operation.type == 'rename' || operation.type == 'noop' ) {
-				return;
-			}
-
-			while ( this._fixGraveyardRangesData.length ) {
-				const { liveRange, sourcePosition } = this._fixGraveyardRangesData.shift();
-
-				this._fixGraveyardSelection( liveRange, sourcePosition );
-			}
-
-			if ( this._hasChangedRange ) {
-				this._hasChangedRange = false;
-				this.fire( 'change:range', { directChange: false } );
-			}
-
-			this._updateMarkers();
-			this._updateAttributes( false );
-		}, { priority: 'lowest' } );
-
-		// Clear selection attributes from element if no longer empty.
+		// Ensure selection is up to date after each change block.
 		this.listenTo( this._document, 'change', ( evt, batch ) => {
 			clearAttributesStoredInElement( this._model, batch );
 		} );
@@ -715,12 +718,12 @@ class LiveSelection extends Selection {
 
 	setTo( selectable, optionsOrPlaceOrOffset, options ) {
 		super.setTo( selectable, optionsOrPlaceOrOffset, options );
-		this._refreshAttributes();
+		this._updateAttributes( true );
 	}
 
 	setFocus( itemOrPosition, offset ) {
 		super.setFocus( itemOrPosition, offset );
-		this._refreshAttributes();
+		this._updateAttributes( true );
 	}
 
 	setAttribute( key, value ) {
@@ -747,7 +750,7 @@ class LiveSelection extends Selection {
 		this._overriddenGravityRegister.add( overrideUid );
 
 		if ( this._overriddenGravityRegister.size === 1 ) {
-			this._refreshAttributes();
+			this._updateAttributes( true );
 		}
 
 		return overrideUid;
@@ -765,6 +768,7 @@ class LiveSelection extends Selection {
 			 */
 			throw new CKEditorError(
 				'document-selection-gravity-wrong-restore: Attempting to restore the selection gravity for an unknown UID.',
+				this,
 				{ uid }
 			);
 		}
@@ -773,15 +777,10 @@ class LiveSelection extends Selection {
 
 		// Restore gravity only when all overriding have been restored.
 		if ( !this.isGravityOverridden ) {
-			this._refreshAttributes();
+			this._updateAttributes( true );
 		}
 	}
 
-	// Removes all attributes from the selection and sets attributes according to the surrounding nodes.
-	_refreshAttributes() {
-		this._updateAttributes( true );
-	}
-
 	_popRange() {
 		this._ranges.pop().detach();
 	}

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

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

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

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

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

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

+ 3 - 2
packages/ckeditor5-engine/src/model/liveposition.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -46,7 +46,8 @@ export default class LivePosition extends Position {
 			 * @error liveposition-root-not-rootelement
 			 */
 			throw new CKEditorError(
-				'model-liveposition-root-not-rootelement: LivePosition\'s root has to be an instance of RootElement.'
+				'model-liveposition-root-not-rootelement: LivePosition\'s root has to be an instance of RootElement.',
+				root
 			);
 		}
 

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -167,7 +167,7 @@ export default class MarkerCollection {
 		const marker = this._markers.get( markerName );
 
 		if ( !marker ) {
-			throw new CKEditorError( 'markercollection-refresh-marker-not-exists: Marker with provided name does not exists.' );
+			throw new CKEditorError( 'markercollection-refresh-marker-not-exists: Marker with provided name does not exists.', this );
 		}
 
 		const range = marker.getRange();
@@ -383,7 +383,7 @@ class Marker {
 	 */
 	get managedUsingOperations() {
 		if ( !this._liveRange ) {
-			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.' );
+			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.', this );
 		}
 
 		return this._managedUsingOperations;
@@ -396,7 +396,7 @@ class Marker {
 	 */
 	get affectsData() {
 		if ( !this._liveRange ) {
-			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.' );
+			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.', this );
 		}
 
 		return this._affectsData;
@@ -409,7 +409,7 @@ class Marker {
 	 */
 	getStart() {
 		if ( !this._liveRange ) {
-			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.' );
+			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.', this );
 		}
 
 		return this._liveRange.start.clone();
@@ -422,7 +422,7 @@ class Marker {
 	 */
 	getEnd() {
 		if ( !this._liveRange ) {
-			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.' );
+			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.', this );
 		}
 
 		return this._liveRange.end.clone();
@@ -442,7 +442,7 @@ class Marker {
 	 */
 	getRange() {
 		if ( !this._liveRange ) {
-			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.' );
+			throw new CKEditorError( 'marker-destroyed: Cannot use a destroyed marker instance.', this );
 		}
 
 		return this._liveRange.toRange();

+ 4 - 3
packages/ckeditor5-engine/src/model/model.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -702,10 +702,11 @@ export default class Model {
 	 * * {@link #change `change()`},
 	 * * {@link #enqueueChange `enqueueChange()`}.
 	 *
+	 * @param {'transparent'|'default'} [type='default'] The type of the batch.
 	 * @returns {module:engine/model/batch~Batch}
 	 */
-	createBatch() {
-		return new Batch();
+	createBatch( type ) {
+		return new Batch( type );
 	}
 
 	/**

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -88,7 +88,7 @@ export default class Node {
 		}
 
 		if ( ( pos = this.parent.getChildIndex( this ) ) === null ) {
-			throw new CKEditorError( 'model-node-not-found-in-parent: The node\'s parent does not contain this node.' );
+			throw new CKEditorError( 'model-node-not-found-in-parent: The node\'s parent does not contain this node.', this );
 		}
 
 		return pos;
@@ -112,7 +112,7 @@ export default class Node {
 		}
 
 		if ( ( pos = this.parent.getChildStartOffset( this ) ) === null ) {
-			throw new CKEditorError( 'model-node-not-found-in-parent: The node\'s parent does not contain this node.' );
+			throw new CKEditorError( 'model-node-not-found-in-parent: The node\'s parent does not contain this node.', this );
 		}
 
 		return pos;

+ 13 - 7
packages/ckeditor5-engine/src/model/nodelist.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -124,7 +124,7 @@ export default class NodeList {
 			 *
 			 * @error nodelist-index-out-of-bounds
 			 */
-			throw new CKEditorError( 'model-nodelist-index-out-of-bounds: Given index cannot be found in the node list.' );
+			throw new CKEditorError( 'model-nodelist-index-out-of-bounds: Given index cannot be found in the node list.', this );
 		}
 
 		return this.getNodeStartOffset( node );
@@ -158,10 +158,13 @@ export default class NodeList {
 			 * @param {Number} offset
 			 * @param {module:engine/model/nodelist~NodeList} nodeList Stringified node list.
 			 */
-			throw new CKEditorError( 'model-nodelist-offset-out-of-bounds: Given offset cannot be found in the node list.', {
-				offset,
-				nodeList: this
-			} );
+			throw new CKEditorError( 'model-nodelist-offset-out-of-bounds: Given offset cannot be found in the node list.',
+				this,
+				{
+					offset,
+					nodeList: this
+				}
+			);
 		}
 
 		return this.length;
@@ -183,7 +186,10 @@ export default class NodeList {
 				 *
 				 * @error nodelist-insertNodes-not-node
 				 */
-				throw new CKEditorError( 'model-nodelist-insertNodes-not-node: Trying to insert an object which is not a Node instance.' );
+				throw new CKEditorError(
+					'model-nodelist-insertNodes-not-node: Trying to insert an object which is not a Node instance.',
+					this
+				);
 			}
 		}
 

+ 4 - 2
packages/ckeditor5-engine/src/model/operation/attributeoperation.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -128,7 +128,7 @@ export default class AttributeOperation extends Operation {
 			 *
 			 * @error attribute-operation-range-not-flat
 			 */
-			throw new CKEditorError( 'attribute-operation-range-not-flat: The range to change is not flat.' );
+			throw new CKEditorError( 'attribute-operation-range-not-flat: The range to change is not flat.', this );
 		}
 
 		for ( const item of this.range.getItems( { shallow: true } ) ) {
@@ -144,6 +144,7 @@ export default class AttributeOperation extends Operation {
 				throw new CKEditorError(
 					'attribute-operation-wrong-old-value: Changed node has different attribute value than operation\'s ' +
 					'old attribute value.',
+					this,
 					{ item, key: this.key, value: this.oldValue }
 				);
 			}
@@ -158,6 +159,7 @@ export default class AttributeOperation extends Operation {
 				 */
 				throw new CKEditorError(
 					'attribute-operation-attribute-exists: The attribute with given key already exists.',
+					this,
 					{ node: item, key: this.key }
 				);
 			}

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -73,7 +73,7 @@ export default class DetachOperation extends Operation {
 			 *
 			 * @error detach-operation-on-document-node
 			 */
-			throw new CKEditorError( 'detach-operation-on-document-node: Cannot detach document node.' );
+			throw new CKEditorError( 'detach-operation-on-document-node: Cannot detach document node.', this );
 		}
 	}
 

+ 3 - 2
packages/ckeditor5-engine/src/model/operation/insertoperation.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -115,7 +115,8 @@ export default class InsertOperation extends Operation {
 			 * @error insert-operation-position-invalid
 			 */
 			throw new CKEditorError(
-				'insert-operation-position-invalid: Insertion position is invalid.'
+				'insert-operation-position-invalid: Insertion position is invalid.',
+				this
 			);
 		}
 	}

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -147,21 +147,21 @@ export default class MergeOperation extends Operation {
 			 *
 			 * @error merge-operation-source-position-invalid
 			 */
-			throw new CKEditorError( 'merge-operation-source-position-invalid: Merge source position is invalid.' );
+			throw new CKEditorError( 'merge-operation-source-position-invalid: Merge source position is invalid.', this );
 		} else if ( !targetElement || !targetElement.is( 'element' ) || !targetElement.parent ) {
 			/**
 			 * Merge target position is invalid.
 			 *
 			 * @error merge-operation-target-position-invalid
 			 */
-			throw new CKEditorError( 'merge-operation-target-position-invalid: Merge target position is invalid.' );
+			throw new CKEditorError( 'merge-operation-target-position-invalid: Merge target position is invalid.', this );
 		} else if ( this.howMany != sourceElement.maxOffset ) {
 			/**
 			 * Merge operation specifies wrong number of nodes to move.
 			 *
 			 * @error merge-operation-how-many-invalid
 			 */
-			throw new CKEditorError( 'merge-operation-how-many-invalid: Merge operation specifies wrong number of nodes to move.' );
+			throw new CKEditorError( 'merge-operation-how-many-invalid: Merge operation specifies wrong number of nodes to move.', this );
 		}
 	}
 

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -130,7 +130,7 @@ export default class MoveOperation extends Operation {
 			 * @error move-operation-position-invalid
 			 */
 			throw new CKEditorError(
-				'move-operation-position-invalid: Source position or target position is invalid.'
+				'move-operation-position-invalid: Source position or target position is invalid.', this
 			);
 		} else if ( sourceOffset + this.howMany > sourceElement.maxOffset ) {
 			/**
@@ -139,7 +139,7 @@ export default class MoveOperation extends Operation {
 			 * @error move-operation-nodes-do-not-exist
 			 */
 			throw new CKEditorError(
-				'move-operation-nodes-do-not-exist: The nodes which should be moved do not exist.'
+				'move-operation-nodes-do-not-exist: The nodes which should be moved do not exist.', this
 			);
 		} else if ( sourceElement === targetElement && sourceOffset < targetOffset && targetOffset < sourceOffset + this.howMany ) {
 			/**
@@ -148,7 +148,7 @@ export default class MoveOperation extends Operation {
 			 * @error move-operation-range-into-itself
 			 */
 			throw new CKEditorError(
-				'move-operation-range-into-itself: Trying to move a range of nodes to the inside of that range.'
+				'move-operation-range-into-itself: Trying to move a range of nodes to the inside of that range.', this
 			);
 		} else if ( this.sourcePosition.root == this.targetPosition.root ) {
 			if ( compareArrays( this.sourcePosition.getParentPath(), this.targetPosition.getParentPath() ) == 'prefix' ) {
@@ -161,7 +161,7 @@ export default class MoveOperation extends Operation {
 					 * @error move-operation-node-into-itself
 					 */
 					throw new CKEditorError(
-						'move-operation-node-into-itself: Trying to move a range of nodes into one of nodes from that range.'
+						'move-operation-node-into-itself: Trying to move a range of nodes into one of nodes from that range.', this
 					);
 				}
 			}

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

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

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

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

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -94,7 +94,8 @@ export default class RenameOperation extends Operation {
 			 * @error rename-operation-wrong-position
 			 */
 			throw new CKEditorError(
-				'rename-operation-wrong-position: Given position is invalid or node after it is not an instance of Element.'
+				'rename-operation-wrong-position: Given position is invalid or node after it is not an instance of Element.',
+				this
 			);
 		} else if ( element.name !== this.oldName ) {
 			/**
@@ -103,7 +104,8 @@ export default class RenameOperation extends Operation {
 			 * @error rename-operation-wrong-name
 			 */
 			throw new CKEditorError(
-				'rename-operation-wrong-name: Element to change has different name than operation\'s old name.'
+				'rename-operation-wrong-name: Element to change has different name than operation\'s old name.',
+				this
 			);
 		}
 	}

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -116,6 +116,7 @@ export default class RootAttributeOperation extends Operation {
 			 */
 			throw new CKEditorError(
 				'rootattribute-operation-not-a-root: The element to change is not a root element.',
+				this,
 				{ root: this.root, key: this.key }
 			);
 		}
@@ -132,6 +133,7 @@ export default class RootAttributeOperation extends Operation {
 			throw new CKEditorError(
 				'rootattribute-operation-wrong-old-value: Changed node has different attribute value than operation\'s ' +
 				'old attribute value.',
+				this,
 				{ root: this.root, key: this.key }
 			);
 		}
@@ -146,6 +148,7 @@ export default class RootAttributeOperation extends Operation {
 			 */
 			throw new CKEditorError(
 				'rootattribute-operation-attribute-exists: The attribute with given key already exists.',
+				this,
 				{ root: this.root, key: this.key }
 			);
 		}
@@ -197,6 +200,7 @@ export default class RootAttributeOperation extends Operation {
 			 */
 			throw new CKEditorError(
 				'rootattribute-operation-fromjson-no-root: Cannot create RootAttributeOperation. Root with specified name does not exist.',
+				this,
 				{ rootName: json.root }
 			);
 		}

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -149,28 +149,28 @@ export default class SplitOperation extends Operation {
 			 *
 			 * @error split-operation-position-invalid
 			 */
-			throw new CKEditorError( 'split-operation-position-invalid: Split position is invalid.' );
+			throw new CKEditorError( 'split-operation-position-invalid: Split position is invalid.', this );
 		} else if ( !element.parent ) {
 			/**
 			 * Cannot split root element.
 			 *
 			 * @error split-operation-split-in-root
 			 */
-			throw new CKEditorError( 'split-operation-split-in-root: Cannot split root element.' );
+			throw new CKEditorError( 'split-operation-split-in-root: Cannot split root element.', this );
 		} else if ( this.howMany != element.maxOffset - this.splitPosition.offset ) {
 			/**
 			 * Split operation specifies wrong number of nodes to move.
 			 *
 			 * @error split-operation-how-many-invalid
 			 */
-			throw new CKEditorError( 'split-operation-how-many-invalid: Split operation specifies wrong number of nodes to move.' );
+			throw new CKEditorError( 'split-operation-how-many-invalid: Split operation specifies wrong number of nodes to move.', this );
 		} else if ( this.graveyardPosition && !this.graveyardPosition.nodeAfter ) {
 			/**
 			 * Graveyard position invalid.
 			 *
 			 * @error split-operation-graveyard-position-invalid
 			 */
-			throw new CKEditorError( 'split-operation-graveyard-position-invalid: Graveyard position invalid.' );
+			throw new CKEditorError( 'split-operation-graveyard-position-invalid: Graveyard position invalid.', this );
 		}
 	}
 

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -69,8 +69,11 @@ export function _remove( range ) {
 		 *
 		 * @error operation-utils-remove-range-not-flat
 		 */
-		throw new CKEditorError( 'operation-utils-remove-range-not-flat: ' +
-			'Trying to remove a range which starts and ends in different element.' );
+		throw new CKEditorError(
+			'operation-utils-remove-range-not-flat: ' +
+			'Trying to remove a range which starts and ends in different element.',
+			this
+		);
 	}
 
 	const parent = range.start.parent;
@@ -105,8 +108,11 @@ export function _move( sourceRange, targetPosition ) {
 		 *
 		 * @error operation-utils-move-range-not-flat
 		 */
-		throw new CKEditorError( 'operation-utils-move-range-not-flat: ' +
-			'Trying to move a range which starts and ends in different element.' );
+		throw new CKEditorError(
+			'operation-utils-move-range-not-flat: ' +
+			'Trying to move a range which starts and ends in different element.',
+			this
+		);
 	}
 
 	const nodes = _remove( sourceRange );

+ 28 - 7
packages/ckeditor5-engine/src/model/position.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -61,7 +61,10 @@ export default class Position {
 			 *
 			 * @error model-position-root-invalid
 			 */
-			throw new CKEditorError( 'model-position-root-invalid: Position root invalid.' );
+			throw new CKEditorError(
+				'model-position-root-invalid: Position root invalid.',
+				root
+			);
 		}
 
 		if ( !( path instanceof Array ) || path.length === 0 ) {
@@ -71,7 +74,11 @@ export default class Position {
 			 * @error model-position-path-incorrect
 			 * @param path
 			 */
-			throw new CKEditorError( 'model-position-path-incorrect: Position path must be an array with at least one item.', { path } );
+			throw new CKEditorError(
+				'model-position-path-incorrect: Position path must be an array with at least one item.',
+				root,
+				{ path }
+			);
 		}
 
 		// Normalize the root and path (if element was passed).
@@ -858,7 +865,9 @@ export default class Position {
 				 */
 				throw new CKEditorError(
 					'model-createPositionAt-offset-required: ' +
-					'Model#createPositionAt() requires the offset when the first parameter is a model item.' );
+					'Model#createPositionAt() requires the offset when the first parameter is a model item.',
+					[ this, itemOrPosition ]
+				);
 			}
 
 			if ( !node.is( 'element' ) && !node.is( 'documentFragment' ) ) {
@@ -867,7 +876,10 @@ export default class Position {
 				 *
 				 * @error model-position-parent-incorrect
 				 */
-				throw new CKEditorError( 'model-position-parent-incorrect: Position parent have to be a element or document fragment.' );
+				throw new CKEditorError(
+					'model-position-parent-incorrect: Position parent have to be a element or document fragment.',
+					[ this, itemOrPosition ]
+				);
 			}
 
 			const path = node.getPath();
@@ -894,7 +906,11 @@ export default class Position {
 			 * @error model-position-after-root
 			 * @param {module:engine/model/item~Item} root
 			 */
-			throw new CKEditorError( 'model-position-after-root: You cannot make a position after root.', { root: item } );
+			throw new CKEditorError(
+				'model-position-after-root: You cannot make a position after root.',
+				[ this, item ],
+				{ root: item }
+			);
 		}
 
 		return this._createAt( item.parent, item.endOffset, stickiness );
@@ -916,7 +932,11 @@ export default class Position {
 			 * @error model-position-before-root
 			 * @param {module:engine/model/item~Item} root
 			 */
-			throw new CKEditorError( 'model-position-before-root: You cannot make a position before root.', { root: item } );
+			throw new CKEditorError(
+				'model-position-before-root: You cannot make a position before root.',
+				item,
+				{ root: item }
+			);
 		}
 
 		return this._createAt( item.parent, item.startOffset, stickiness );
@@ -946,6 +966,7 @@ export default class Position {
 			 */
 			throw new CKEditorError(
 				'model-position-fromjson-no-root: Cannot create position for document. Root with specified name does not exist.',
+				doc,
 				{ rootName: json.root }
 			);
 		}

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -336,6 +336,16 @@ export default class Range {
 	/**
 	 * Creates a {@link module:engine/model/treewalker~TreeWalker TreeWalker} instance with this range as a boundary.
 	 *
+	 * For example, to iterate over all items in the entire document root:
+	 *
+	 *		// Create a range spanning over the entire root content:
+	 *		const range = editor.model.createRangeIn( editor.model.document.getRoot() );
+	 *
+	 *		// Iterate over all items in this range:
+	 *		for ( const value of range.getWalker() ) {
+	 *			console.log( value.item );
+	 *		}
+	 *
 	 * @param {Object} options Object with configuration options. See {@link module:engine/model/treewalker~TreeWalker}.
 	 * @param {module:engine/model/position~Position} [options.startPosition]
 	 * @param {Boolean} [options.singleCharacters=false]
@@ -867,7 +877,10 @@ export default class Range {
 			 *
 			 * @error range-create-from-ranges-empty-array
 			 */
-			throw new CKEditorError( 'range-create-from-ranges-empty-array: At least one range has to be passed.' );
+			throw new CKEditorError(
+				'range-create-from-ranges-empty-array: At least one range has to be passed.',
+				null
+			);
 		} else if ( ranges.length == 1 ) {
 			return ranges[ 0 ].clone();
 		}

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

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

+ 20 - 9
packages/ckeditor5-engine/src/model/schema.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -91,9 +91,13 @@ export default class Schema {
 			 * @param itemName The name of the model element that is being registered twice.
 			 * @error schema-cannot-register-item-twice
 			 */
-			throw new CKEditorError( 'schema-cannot-register-item-twice: A single item cannot be registered twice in the schema.', {
-				itemName
-			} );
+			throw new CKEditorError(
+				'schema-cannot-register-item-twice: A single item cannot be registered twice in the schema.',
+				this,
+				{
+					itemName
+				}
+			);
 		}
 
 		this._sourceDefinitions[ itemName ] = [
@@ -136,9 +140,9 @@ export default class Schema {
 			 * {@link #register registered} yet.
 			 *
 			 * @param itemName The name of the model element which is being extended.
-			 * @error schema-cannot-register-item-twice
+			 * @error schema-cannot-extend-missing-item
 			 */
-			throw new CKEditorError( 'schema-cannot-extend-missing-item: Cannot extend an item which was not registered yet.', {
+			throw new CKEditorError( 'schema-cannot-extend-missing-item: Cannot extend an item which was not registered yet.', this, {
 				itemName
 			} );
 		}
@@ -352,7 +356,10 @@ export default class Schema {
 				 *
 				 * @error schema-check-merge-no-element-before
 				 */
-				throw new CKEditorError( 'schema-check-merge-no-element-before: The node before the merge position must be an element.' );
+				throw new CKEditorError(
+					'schema-check-merge-no-element-before: The node before the merge position must be an element.',
+					this
+				);
 			}
 
 			if ( !( nodeAfter instanceof Element ) ) {
@@ -361,7 +368,10 @@ export default class Schema {
 				 *
 				 * @error schema-check-merge-no-element-after
 				 */
-				throw new CKEditorError( 'schema-check-merge-no-element-after: The node after the merge position must be an element.' );
+				throw new CKEditorError(
+					'schema-check-merge-no-element-after: The node after the merge position must be an element.',
+					this
+				);
 			}
 
 			return this.checkMerge( nodeBefore, nodeAfter );
@@ -1018,7 +1028,7 @@ mix( Schema, ObservableMixin );
  * (paragraphs, lists items, headings, images) which, in turn, may contain text inside.
  *
  * By inheriting from the generic items you can define new items which will get extended by other editor features.
- * Read more about generic types in the {@linkTODO Defining schema} guide.
+ * Read more about generic types in the {@glink framework/guides/deep-dive/schema Defining schema} guide.
  *
  * # Example definitions
  *
@@ -1353,6 +1363,7 @@ export class SchemaContext {
  * @typedef {Object} module:engine/model/schema~AttributeProperties
  * @property {Boolean} [isFormatting] Indicates that the attribute should be considered as a visual formatting, like `bold`, `italic` or
  * `fontSize` rather than semantic attribute (such as `src`, `listType`, etc.). For example, it is used by the "Remove format" feature.
+ * @property {Boolean} [copyOnEnter] Indicates that given text attribute should be copied to the next block when enter is pressed.
  */
 
 function compileBaseItemRule( sourceItemRules, itemName ) {

+ 13 - 5
packages/ckeditor5-engine/src/model/selection.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -395,7 +395,9 @@ export default class Selection {
 				 */
 				throw new CKEditorError(
 					'model-selection-setTo-required-second-parameter: ' +
-					'selection.setTo requires the second parameter when the first parameter is a node.' );
+					'selection.setTo requires the second parameter when the first parameter is a node.',
+					[ this, selectable ]
+				);
 			}
 
 			this._setRanges( [ range ], backward );
@@ -415,7 +417,10 @@ export default class Selection {
 			 *
 			 * @error model-selection-setTo-not-selectable
 			 */
-			throw new CKEditorError( 'model-selection-setTo-not-selectable: Cannot set the selection to the given place.' );
+			throw new CKEditorError(
+				'model-selection-setTo-not-selectable: Cannot set the selection to the given place.',
+				[ this, selectable ]
+			);
 		}
 	}
 
@@ -449,7 +454,8 @@ export default class Selection {
 				 */
 				throw new CKEditorError(
 					'model-selection-set-ranges-not-range: ' +
-					'Selection range set to an object that is not an instance of model.Range.'
+					'Selection range set to an object that is not an instance of model.Range.',
+					[ this, newRanges ]
 				);
 			}
 
@@ -493,7 +499,8 @@ export default class Selection {
 			 * @error model-selection-setFocus-no-ranges
 			 */
 			throw new CKEditorError(
-				'model-selection-setFocus-no-ranges: Cannot set selection focus if there are no ranges in selection.'
+				'model-selection-setFocus-no-ranges: Cannot set selection focus if there are no ranges in selection.',
+				[ this, itemOrPosition ]
 			);
 		}
 
@@ -763,6 +770,7 @@ export default class Selection {
 				 */
 				throw new CKEditorError(
 					'model-selection-range-intersects: Trying to add a range that intersects with another range in the selection.',
+					[ this, range ],
 					{ addedRange: range, intersectingRange: this._ranges[ i ] }
 				);
 			}

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

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

+ 3 - 3
packages/ckeditor5-engine/src/model/textproxy.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -64,7 +64,7 @@ export default class TextProxy {
 			 *
 			 * @error model-textproxy-wrong-offsetintext
 			 */
-			throw new CKEditorError( 'model-textproxy-wrong-offsetintext: Given offsetInText value is incorrect.' );
+			throw new CKEditorError( 'model-textproxy-wrong-offsetintext: Given offsetInText value is incorrect.', this );
 		}
 
 		if ( length < 0 || offsetInText + length > textNode.offsetSize ) {
@@ -73,7 +73,7 @@ export default class TextProxy {
 			 *
 			 * @error model-textproxy-wrong-length
 			 */
-			throw new CKEditorError( 'model-textproxy-wrong-length: Given length value is incorrect.' );
+			throw new CKEditorError( 'model-textproxy-wrong-length: Given length value is incorrect.', this );
 		}
 
 		/**

+ 6 - 2
packages/ckeditor5-engine/src/model/treewalker.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -42,7 +42,10 @@ export default class TreeWalker {
 			 *
 			 * @error model-tree-walker-no-start-position
 			 */
-			throw new CKEditorError( 'model-tree-walker-no-start-position: Neither boundaries nor starting position have been defined.' );
+			throw new CKEditorError(
+				'model-tree-walker-no-start-position: Neither boundaries nor starting position have been defined.',
+				null
+			);
 		}
 
 		const direction = options.direction || 'forward';
@@ -50,6 +53,7 @@ export default class TreeWalker {
 		if ( direction != 'forward' && direction != 'backward' ) {
 			throw new CKEditorError(
 				'model-tree-walker-unknown-direction: Only `backward` and `forward` direction allowed.',
+				options,
 				{ direction }
 			);
 		}

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

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

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -50,7 +50,7 @@ export default function insertContent( model, content, selectable, placeOrOffset
 			selection = writer.createSelection( selectable, placeOrOffset );
 		}
 
-		const insertionPosition = selection.anchor.clone();
+		const insertionPosition = selection.getFirstPosition();
 
 		if ( !selection.isCollapsed ) {
 			model.deleteContent( selection, { doNotAutoparagraph: true } );
@@ -561,8 +561,15 @@ class Insertion {
 				this.position = this.writer.createPositionBefore( parent );
 
 				// Special case – parent is empty (<p>^</p>).
+				//
+				// 1. parent.isEmpty
 				// We can remove the element after moving insertion position out of it.
-				if ( parent.isEmpty ) {
+				//
+				// 2. parent.parent === allowedIn
+				// However parent should remain in place when allowed element is above limit element in document tree.
+				// For example there shouldn't be allowed to remove empty paragraph from tableCell, when is pasted
+				// content allowed in $root.
+				if ( parent.isEmpty && parent.parent === allowedIn ) {
 					this.writer.remove( parent );
 				}
 			} else if ( this.position.isAtEnd ) {

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

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

+ 70 - 27
packages/ckeditor5-engine/src/model/writer.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -177,8 +177,18 @@ export default class Writer {
 			// If it isn't the same root.
 			else {
 				if ( item.root.document ) {
-					// It is forbidden to move a node that was already in a document outside of it.
-					throw new Error( 'model-writer-insert-forbidden-move: Cannot move a node from a document to a different tree.' );
+					/**
+					 * Cannot move a node from a document to a different tree.
+					 * It is forbidden to move a node that was already in a document outside of it.
+					 *
+					 * @error model-writer-insert-forbidden-move
+					 */
+					throw new CKEditorError(
+						'model-writer-insert-forbidden-move: ' +
+						'Cannot move a node from a document to a different tree. ' +
+						'It is forbidden to move a node that was already in a document outside of it.',
+						this
+					);
 				} else {
 					// Move between two different document fragments or from document fragment to a document is possible.
 					// In that case, remove the item from it's original parent.
@@ -208,7 +218,13 @@ export default class Writer {
 					markerRange.end._getCombined( rangeRootPosition, position )
 				);
 
-				this.addMarker( markerName, { range, usingOperation: true, affectsData: true } );
+				const options = { range, usingOperation: true, affectsData: true };
+
+				if ( this.model.markers.has( markerName ) ) {
+					this.updateMarker( markerName, options );
+				} else {
+					this.addMarker( markerName, options );
+				}
 			}
 		}
 	}
@@ -456,7 +472,7 @@ export default class Writer {
 			 *
 			 * @error writer-move-invalid-range
 			 */
-			throw new CKEditorError( 'writer-move-invalid-range: Invalid range to move.' );
+			throw new CKEditorError( 'writer-move-invalid-range: Invalid range to move.', this );
 		}
 
 		if ( !range.isFlat ) {
@@ -465,7 +481,7 @@ export default class Writer {
 			 *
 			 * @error writer-move-range-not-flat
 			 */
-			throw new CKEditorError( 'writer-move-range-not-flat: Range to move is not flat.' );
+			throw new CKEditorError( 'writer-move-range-not-flat: Range to move is not flat.', this );
 		}
 
 		const position = Position._createAt( itemOrPosition, offset );
@@ -485,7 +501,7 @@ export default class Writer {
 			 *
 			 * @error writer-move-different-document
 			 */
-			throw new CKEditorError( 'writer-move-different-document: Range is going to be moved between different documents.' );
+			throw new CKEditorError( 'writer-move-different-document: Range is going to be moved between different documents.', this );
 		}
 
 		const version = range.root.document ? range.root.document.version : null;
@@ -537,7 +553,7 @@ export default class Writer {
 			 *
 			 * @error writer-merge-no-element-before
 			 */
-			throw new CKEditorError( 'writer-merge-no-element-before: Node before merge position must be an element.' );
+			throw new CKEditorError( 'writer-merge-no-element-before: Node before merge position must be an element.', this );
 		}
 
 		if ( !( nodeAfter instanceof Element ) ) {
@@ -546,7 +562,7 @@ export default class Writer {
 			 *
 			 * @error writer-merge-no-element-after
 			 */
-			throw new CKEditorError( 'writer-merge-no-element-after: Node after merge position must be an element.' );
+			throw new CKEditorError( 'writer-merge-no-element-after: Node after merge position must be an element.', this );
 		}
 
 		if ( !position.root.document ) {
@@ -696,7 +712,8 @@ export default class Writer {
 			 * @error writer-rename-not-element-instance
 			 */
 			throw new CKEditorError(
-				'writer-rename-not-element-instance: Trying to rename an object which is not an instance of Element.'
+				'writer-rename-not-element-instance: Trying to rename an object which is not an instance of Element.',
+				this
 			);
 		}
 
@@ -732,7 +749,7 @@ export default class Writer {
 			 *
 			 * @error writer-split-element-no-parent
 			 */
-			throw new CKEditorError( 'writer-split-element-no-parent: Element with no parent can not be split.' );
+			throw new CKEditorError( 'writer-split-element-no-parent: Element with no parent can not be split.', this );
 		}
 
 		// When limit element is not defined lets set splitElement parent as limit.
@@ -741,7 +758,7 @@ export default class Writer {
 		}
 
 		if ( !position.parent.getAncestors( { includeSelf: true } ).includes( limitElement ) ) {
-			throw new CKEditorError( 'writer-split-invalid-limit-element: Limit element is not a position ancestor.' );
+			throw new CKEditorError( 'writer-split-invalid-limit-element: Limit element is not a position ancestor.', this );
 		}
 
 		// We need to cache elements that will be created as a result of the first split because
@@ -791,7 +808,7 @@ export default class Writer {
 			 *
 			 * @error writer-wrap-range-not-flat
 			 */
-			throw new CKEditorError( 'writer-wrap-range-not-flat: Range to wrap is not flat.' );
+			throw new CKEditorError( 'writer-wrap-range-not-flat: Range to wrap is not flat.', this );
 		}
 
 		const element = elementOrString instanceof Element ? elementOrString : new Element( elementOrString );
@@ -802,7 +819,7 @@ export default class Writer {
 			 *
 			 * @error writer-wrap-element-not-empty
 			 */
-			throw new CKEditorError( 'writer-wrap-element-not-empty: Element to wrap with is not empty.' );
+			throw new CKEditorError( 'writer-wrap-element-not-empty: Element to wrap with is not empty.', this );
 		}
 
 		if ( element.parent !== null ) {
@@ -811,7 +828,7 @@ export default class Writer {
 			 *
 			 * @error writer-wrap-element-attached
 			 */
-			throw new CKEditorError( 'writer-wrap-element-attached: Element to wrap with is already attached to tree model.' );
+			throw new CKEditorError( 'writer-wrap-element-attached: Element to wrap with is already attached to tree model.', this );
 		}
 
 		this.insert( element, range.start );
@@ -837,7 +854,7 @@ export default class Writer {
 			 *
 			 * @error writer-unwrap-element-no-parent
 			 */
-			throw new CKEditorError( 'writer-unwrap-element-no-parent: Trying to unwrap an element which has no parent.' );
+			throw new CKEditorError( 'writer-unwrap-element-no-parent: Trying to unwrap an element which has no parent.', this );
 		}
 
 		this.move( Range._createIn( element ), this.createPositionAfter( element ) );
@@ -893,7 +910,8 @@ export default class Writer {
 			 * @error writer-addMarker-no-usingOperation
 			 */
 			throw new CKEditorError(
-				'writer-addMarker-no-usingOperation: The options.usingOperation parameter is required when adding a new marker.'
+				'writer-addMarker-no-usingOperation: The options.usingOperation parameter is required when adding a new marker.',
+				this
 			);
 		}
 
@@ -907,7 +925,7 @@ export default class Writer {
 			 *
 			 * @error writer-addMarker-marker-exists
 			 */
-			throw new CKEditorError( 'writer-addMarker-marker-exists: Marker with provided name already exists.' );
+			throw new CKEditorError( 'writer-addMarker-marker-exists: Marker with provided name already exists.', this );
 		}
 
 		if ( !range ) {
@@ -916,7 +934,10 @@ export default class Writer {
 			 *
 			 * @error writer-addMarker-no-range
 			 */
-			throw new CKEditorError( 'writer-addMarker-no-range: Range parameter is required when adding a new marker.' );
+			throw new CKEditorError(
+				'writer-addMarker-no-range: Range parameter is required when adding a new marker.',
+				this
+			);
 		}
 
 		if ( !usingOperation ) {
@@ -1009,7 +1030,7 @@ export default class Writer {
 			 *
 			 * @error writer-updateMarker-marker-not-exists
 			 */
-			throw new CKEditorError( 'writer-updateMarker-marker-not-exists: Marker with provided name does not exists.' );
+			throw new CKEditorError( 'writer-updateMarker-marker-not-exists: Marker with provided name does not exists.', this );
 		}
 
 		if ( !options ) {
@@ -1031,7 +1052,8 @@ export default class Writer {
 			 * @error writer-updateMarker-wrong-options
 			 */
 			throw new CKEditorError(
-				'writer-updateMarker-wrong-options: One of the options is required - provide range, usingOperations or affectsData.'
+				'writer-updateMarker-wrong-options: One of the options is required - provide range, usingOperations or affectsData.',
+				this
 			);
 		}
 
@@ -1082,7 +1104,7 @@ export default class Writer {
 			 *
 			 * @error writer-removeMarker-no-marker
 			 */
-			throw new CKEditorError( 'writer-removeMarker-no-marker: Trying to remove marker which does not exist.' );
+			throw new CKEditorError( 'writer-removeMarker-no-marker: Trying to remove marker which does not exist.', this );
 		}
 
 		const marker = this.model.markers.get( name );
@@ -1315,7 +1337,7 @@ export default class Writer {
 		 * @error writer-incorrect-use
 		 */
 		if ( this.model._currentWriter !== this ) {
-			throw new CKEditorError( 'writer-incorrect-use: Trying to use a writer outside the change() block.' );
+			throw new CKEditorError( 'writer-incorrect-use: Trying to use a writer outside the change() block.', this );
 		}
 	}
 
@@ -1349,10 +1371,31 @@ export default class Writer {
 				const elementBefore = positionOrRange.nodeBefore;
 				const elementAfter = positionOrRange.nodeAfter;
 
-				const affectedOnLeft = markerRange.start.parent == elementBefore && markerRange.start.isAtEnd;
-				const affectedOnRight = markerRange.end.parent == elementAfter && markerRange.end.offset == 0;
-
-				isAffected = affectedOnLeft || affectedOnRight;
+				//               Start:  <p>Foo[</p><p>Bar]</p>
+				//         After merge:  <p>Foo[Bar]</p>
+				// After undoing split:  <p>Foo</p><p>[Bar]</p>     <-- incorrect, needs remembering for undo.
+				//
+				const affectedInLeftElement = markerRange.start.parent == elementBefore && markerRange.start.isAtEnd;
+
+				//               Start:  <p>[Foo</p><p>]Bar</p>
+				//         After merge:  <p>[Foo]Bar</p>
+				// After undoing split:  <p>[Foo]</p><p>Bar</p>     <-- incorrect, needs remembering for undo.
+				//
+				const affectedInRightElement = markerRange.end.parent == elementAfter && markerRange.end.offset == 0;
+
+				//               Start:  <p>[Foo</p>]<p>Bar</p>
+				//         After merge:  <p>[Foo]Bar</p>
+				// After undoing split:  <p>[Foo]</p><p>Bar</p>     <-- incorrect, needs remembering for undo.
+				//
+				const affectedAfterLeftElement = markerRange.end.nodeAfter == elementAfter;
+
+				//               Start:  <p>Foo</p>[<p>Bar]</p>
+				//         After merge:  <p>Foo[Bar]</p>
+				// After undoing split:  <p>Foo</p><p>[Bar]</p>     <-- incorrect, needs remembering for undo.
+				//
+				const affectedBeforeRightElement = markerRange.start.nodeAfter == elementAfter;
+
+				isAffected = affectedInLeftElement || affectedInRightElement || affectedAfterLeftElement || affectedBeforeRightElement;
 			}
 
 			if ( isAffected ) {

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

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

+ 3 - 2
packages/ckeditor5-engine/src/view/attributeelement.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -116,7 +116,8 @@ export default class AttributeElement extends Element {
 			 */
 			throw new CKEditorError(
 				'attribute-element-get-elements-with-same-id-no-id: ' +
-				'Cannot get elements with the same id for an attribute element without id.'
+				'Cannot get elements with the same id for an attribute element without id.',
+				this
 			);
 		}
 

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

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

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

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

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

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

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

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

+ 31 - 20
packages/ckeditor5-engine/src/view/domconverter.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -29,6 +29,8 @@ import { isElement } from 'lodash-es';
  * DomConverter is a set of tools to do transformations between DOM nodes and view nodes. It also handles
  * {@link module:engine/view/domconverter~DomConverter#bindElements binding} these nodes.
  *
+ * The instance of DOMConverter is available in {@link module:engine/view/view~View#domConverter `editor.editing.view.domConverter`}.
+ *
  * DomConverter does not check which nodes should be rendered (use {@link module:engine/view/renderer~Renderer}), does not keep a
  * state of a tree nor keeps synchronization between tree view and DOM tree (use {@link module:engine/view/document~Document}).
  *
@@ -887,7 +889,8 @@ export default class DomConverter {
 	 *
 	 * * a space at the beginning is changed to `&nbsp;` if this is the first text node in its container
 	 * element or if a previous text node ends with a space character,
-	 * * space at the end of the text node is changed to `&nbsp;` if this is the last text node in its container,
+	 * * space at the end of the text node is changed to `&nbsp;` if there are two spaces at the end of a node or if next node
+	 * starts with a space or if it is the last text node in its container,
 	 * * remaining spaces are replaced to a chain of spaces and `&nbsp;` (e.g. `'x   x'` becomes `'x &nbsp; x'`).
 	 *
 	 * Content of {@link #preElements} is not processed.
@@ -916,15 +919,24 @@ export default class DomConverter {
 			}
 		}
 
-		// 2. Replace the last space with a nbsp if this is the last text node (container element boundary).
+		// 2. Replace the last space with nbsp if there are two spaces at the end or if the next node starts with space or there is no
+		// next node (container element boundary).
+		//
+		// Keep in mind that Firefox prefers $nbsp; before tag, not inside it:
+		//
+		// Foo <span>&nbsp;bar</span>  <-- bad.
+		// Foo&nbsp;<span> bar</span>  <-- good.
+		//
+		// More here: https://github.com/ckeditor/ckeditor5-engine/issues/1747.
 		if ( data.charAt( data.length - 1 ) == ' ' ) {
 			const nextNode = this._getTouchingViewTextNode( node, true );
 
-			if ( !nextNode ) {
+			if ( data.charAt( data.length - 2 ) == ' ' || !nextNode || nextNode.data.charAt( 0 ) == ' ' ) {
 				data = data.substr( 0, data.length - 1 ) + '\u00A0';
 			}
 		}
 
+		// 3. Create space+nbsp pairs.
 		return data.replace( / {2}/g, ' \u00A0' );
 	}
 
@@ -953,7 +965,9 @@ export default class DomConverter {
 	 * * multiple whitespaces are replaced to a single space,
 	 * * space at the beginning of a text node is removed if it is the first text node in its container
 	 * element or if the previous text node ends with a space character,
-	 * * space at the end of the text node is removed, if it is the last text node in its container.
+	 * * space at the end of the text node is removed if there are two spaces at the end of a node or if next node
+	 * starts with a space or if it is the last text node in its container
+	 * * nbsps are converted to spaces.
 	 *
 	 * @param {Node} node DOM text node to process.
 	 * @returns {String} Processed data.
@@ -996,28 +1010,25 @@ export default class DomConverter {
 		data = getDataWithoutFiller( new Text( data ) );
 
 		// At this point we should have removed all whitespaces from DOM text data.
-
-		// Now we have to change &nbsp; chars, that were in DOM text data because of rendering reasons, to spaces.
-		// First, change all ` \u00A0` pairs (space + &nbsp;) to two spaces. DOM converter changes two spaces from model/view as
-		// ` \u00A0` to ensure proper rendering. Since here we convert back, we recognize those pairs and change them
-		// to `  ` which is what we expect to have in model/view.
+		//
+		// Now, We will reverse the process that happens in `_processDataFromViewText`.
+		//
+		// We have to change &nbsp; chars, that were in DOM text data because of rendering reasons, to spaces.
+		// First, change all ` \u00A0` pairs (space + &nbsp;) to two spaces. DOM converter changes two spaces from model/view to
+		// ` \u00A0` to ensure proper rendering. Since here we convert back, we recognize those pairs and change them back to `  `.
 		data = data.replace( / \u00A0/g, '  ' );
 
+		// Then, let's change the last nbsp to a space.
+		if ( /( |\u00A0)\u00A0$/.test( data ) || !nextNode || ( nextNode.data && nextNode.data.charAt( 0 ) == ' ' ) ) {
+			data = data.replace( /\u00A0$/, ' ' );
+		}
+
 		// Then, change &nbsp; character that is at the beginning of the text node to space character.
-		// As above, that &nbsp; was created for rendering reasons but it's real meaning is just a space character.
 		// We do that replacement only if this is the first node or the previous node ends on whitespace character.
 		if ( shouldLeftTrim ) {
 			data = data.replace( /^\u00A0/, ' ' );
 		}
 
-		// Since input text data could be: `x_ _`, we would not replace the first &nbsp; after `x` character.
-		// We have to fix it. Since we already change all ` &nbsp;`, we will have something like this at the end of text data:
-		// `x_ _ _` -> `x_    `. Find &nbsp; at the end of string (can be followed only by spaces).
-		// We do that replacement only if this is the last node or the next node starts with &nbsp; or is a <br>.
-		if ( isText( nextNode ) ? nextNode.data.charAt( 0 ) == '\u00A0' : true ) {
-			data = data.replace( /\u00A0( *)$/, ' $1' );
-		}
-
 		// At this point, all whitespaces should be removed and all &nbsp; created for rendering reasons should be
 		// changed to normal space. All left &nbsp; are &nbsp; inserted intentionally.
 		return data;
@@ -1046,7 +1057,7 @@ export default class DomConverter {
 	 * be trimmed from the right side.
 	 *
 	 * @param {Node} node
-	 * @param {Node} prevNode
+	 * @param {Node} nextNode
 	 */
 	_checkShouldRightTrimDomText( node, nextNode ) {
 		if ( nextNode ) {

+ 54 - 49
packages/ckeditor5-engine/src/view/downcastwriter.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -444,7 +444,8 @@ export default class DowncastWriter {
 			 * @error view-writer-break-non-container-element
 			 */
 			throw new CKEditorError(
-				'view-writer-break-non-container-element: Trying to break an element which is not a container element.'
+				'view-writer-break-non-container-element: Trying to break an element which is not a container element.',
+				this.document
 			);
 		}
 
@@ -454,7 +455,7 @@ export default class DowncastWriter {
 			 *
 			 * @error view-writer-break-root
 			 */
-			throw new CKEditorError( 'view-writer-break-root: Trying to break root element.' );
+			throw new CKEditorError( 'view-writer-break-root: Trying to break root element.', this.document );
 		}
 
 		if ( position.isAtStart ) {
@@ -577,7 +578,7 @@ export default class DowncastWriter {
 			 * @error view-writer-merge-containers-invalid-position
 			 */
 			throw new CKEditorError( 'view-writer-merge-containers-invalid-position: ' +
-				'Element before and after given position cannot be merged.' );
+				'Element before and after given position cannot be merged.', this.document );
 		}
 
 		const lastChild = prev.getChild( prev.childCount - 1 );
@@ -612,7 +613,7 @@ export default class DowncastWriter {
 		nodes = isIterable( nodes ) ? [ ...nodes ] : [ nodes ];
 
 		// Check if nodes to insert are instances of AttributeElements, ContainerElements, EmptyElements, UIElements or Text.
-		validateNodesToInsert( nodes );
+		validateNodesToInsert( nodes, this.document );
 
 		const container = getParentContainer( position );
 
@@ -622,7 +623,7 @@ export default class DowncastWriter {
 			 *
 			 * @error view-writer-invalid-position-container
 			 */
-			throw new CKEditorError( 'view-writer-invalid-position-container' );
+			throw new CKEditorError( 'view-writer-invalid-position-container', this.document );
 		}
 
 		const insertionPosition = this._breakAttributes( position, true );
@@ -665,7 +666,7 @@ export default class DowncastWriter {
 	remove( rangeOrItem ) {
 		const range = rangeOrItem instanceof Range ? rangeOrItem : Range._createOn( rangeOrItem );
 
-		validateRangeContainer( range );
+		validateRangeContainer( range, this.document );
 
 		// If range is collapsed - nothing to remove.
 		if ( range.isCollapsed ) {
@@ -705,7 +706,7 @@ export default class DowncastWriter {
 	 * @param {module:engine/view/element~Element} element Element to remove.
 	 */
 	clear( range, element ) {
-		validateRangeContainer( range );
+		validateRangeContainer( range, this.document );
 
 		// Create walker on given range.
 		// We walk backward because when we remove element during walk it modifies range end position.
@@ -809,10 +810,10 @@ export default class DowncastWriter {
     */
 	wrap( range, attribute ) {
 		if ( !( attribute instanceof AttributeElement ) ) {
-			throw new CKEditorError( 'view-writer-wrap-invalid-attribute' );
+			throw new CKEditorError( 'view-writer-wrap-invalid-attribute', this.document );
 		}
 
-		validateRangeContainer( range );
+		validateRangeContainer( range, this.document );
 
 		if ( !range.isCollapsed ) {
 			// Non-collapsed range. Wrap it with the attribute element.
@@ -854,10 +855,10 @@ export default class DowncastWriter {
 			 *
 			 * @error view-writer-unwrap-invalid-attribute
 			 */
-			throw new CKEditorError( 'view-writer-unwrap-invalid-attribute' );
+			throw new CKEditorError( 'view-writer-unwrap-invalid-attribute', this.document );
 		}
 
-		validateRangeContainer( range );
+		validateRangeContainer( range, this.document );
 
 		// If range is collapsed - nothing to unwrap.
 		if ( range.isCollapsed ) {
@@ -1093,7 +1094,7 @@ export default class DowncastWriter {
 			// <p><span class="bar">abc</span></p>  -->  <p><span class="foo bar">abc</span></p>
 			//
 			if ( isAttribute && this._wrapAttributeElement( wrapElement, child ) ) {
-				wrapPositions.push(	new Position( parent, i ) );
+				wrapPositions.push( new Position( parent, i ) );
 			}
 			//
 			// Wrap the child if it is not an attribute element or if it is an attribute element that should be inside
@@ -1113,7 +1114,7 @@ export default class DowncastWriter {
 				parent._insertChild( i, newAttribute );
 				this._addToClonedElementsGroup( newAttribute );
 
-				wrapPositions.push(	new Position( parent, i ) );
+				wrapPositions.push( new Position( parent, i ) );
 			}
 			//
 			// If other nested attribute is found and it wasn't wrapped (see above), continue wrapping inside it.
@@ -1489,7 +1490,7 @@ export default class DowncastWriter {
 		const rangeStart = range.start;
 		const rangeEnd = range.end;
 
-		validateRangeContainer( range );
+		validateRangeContainer( range, this.document );
 
 		// Break at the collapsed position. Return new collapsed range.
 		if ( range.isCollapsed ) {
@@ -1534,7 +1535,7 @@ export default class DowncastWriter {
 			 *
 			 * @error view-writer-cannot-break-empty-element
 			 */
-			throw new CKEditorError( 'view-writer-cannot-break-empty-element' );
+			throw new CKEditorError( 'view-writer-cannot-break-empty-element', this.document );
 		}
 
 		// If position is placed inside UIElement - throw an exception as we cannot break inside.
@@ -1544,7 +1545,7 @@ export default class DowncastWriter {
 			 *
 			 * @error view-writer-cannot-break-ui-element
 			 */
-			throw new CKEditorError( 'view-writer-cannot-break-ui-element' );
+			throw new CKEditorError( 'view-writer-cannot-break-ui-element', this.document );
 		}
 
 		// There are no attributes to break and text nodes breaking is not forced.
@@ -1571,40 +1572,41 @@ export default class DowncastWriter {
 			const newPosition = new Position( positionParent.parent, positionParent.index + 1 );
 
 			return this._breakAttributes( newPosition, forceSplitText );
-		} else
-		// <p>foo<b><u>{}bar</u></b></p>
-		// <p>foo<b>[]<u>bar</u></b></p>
-		// <p>foo{}<b><u>bar</u></b></p>
-		if ( positionOffset === 0 ) {
-			const newPosition = new Position( positionParent.parent, positionParent.index );
+		} else {
+			// <p>foo<b><u>{}bar</u></b></p>
+			// <p>foo<b>[]<u>bar</u></b></p>
+			// <p>foo{}<b><u>bar</u></b></p>
+			if ( positionOffset === 0 ) {
+				const newPosition = new Position( positionParent.parent, positionParent.index );
 
-			return this._breakAttributes( newPosition, forceSplitText );
-		}
-		// <p>foo<b><u>b{}ar</u></b></p>
-		// <p>foo<b><u>b[]ar</u></b></p>
-		// <p>foo<b><u>b</u>[]<u>ar</u></b></p>
-		// <p>foo<b><u>b</u></b>[]<b><u>ar</u></b></p>
-		else {
-			const offsetAfter = positionParent.index + 1;
+				return this._breakAttributes( newPosition, forceSplitText );
+			}
+			// <p>foo<b><u>b{}ar</u></b></p>
+			// <p>foo<b><u>b[]ar</u></b></p>
+			// <p>foo<b><u>b</u>[]<u>ar</u></b></p>
+			// <p>foo<b><u>b</u></b>[]<b><u>ar</u></b></p>
+			else {
+				const offsetAfter = positionParent.index + 1;
 
-			// Break element.
-			const clonedNode = positionParent._clone();
+				// Break element.
+				const clonedNode = positionParent._clone();
 
-			// Insert cloned node to position's parent node.
-			positionParent.parent._insertChild( offsetAfter, clonedNode );
-			this._addToClonedElementsGroup( clonedNode );
+				// Insert cloned node to position's parent node.
+				positionParent.parent._insertChild( offsetAfter, clonedNode );
+				this._addToClonedElementsGroup( clonedNode );
 
-			// Get nodes to move.
-			const count = positionParent.childCount - positionOffset;
-			const nodesToMove = positionParent._removeChildren( positionOffset, count );
+				// Get nodes to move.
+				const count = positionParent.childCount - positionOffset;
+				const nodesToMove = positionParent._removeChildren( positionOffset, count );
 
-			// Move nodes to cloned node.
-			clonedNode._appendChild( nodesToMove );
+				// Move nodes to cloned node.
+				clonedNode._appendChild( nodesToMove );
 
-			// Create new position to work on.
-			const newPosition = new Position( positionParent.parent, offsetAfter );
+				// Create new position to work on.
+				const newPosition = new Position( positionParent.parent, offsetAfter );
 
-			return this._breakAttributes( newPosition, forceSplitText );
+				return this._breakAttributes( newPosition, forceSplitText );
+			}
 		}
 	}
 
@@ -1825,7 +1827,8 @@ function mergeTextNodes( t1, t2 ) {
 //
 // @param Iterable.<module:engine/view/text~Text|module:engine/view/attributeelement~AttributeElement
 // |module:engine/view/containerelement~ContainerElement> nodes
-function validateNodesToInsert( nodes ) {
+// @param {Object} errorContext
+function validateNodesToInsert( nodes, errorContext ) {
 	for ( const node of nodes ) {
 		if ( !validNodesToInsert.some( ( validNode => node instanceof validNode ) ) ) { // eslint-disable-line no-use-before-define
 			/**
@@ -1836,11 +1839,11 @@ function validateNodesToInsert( nodes ) {
 			 *
 			 * @error view-writer-insert-invalid-node
 			 */
-			throw new CKEditorError( 'view-writer-insert-invalid-node' );
+			throw new CKEditorError( 'view-writer-insert-invalid-node', errorContext );
 		}
 
 		if ( !node.is( 'text' ) ) {
-			validateNodesToInsert( node.getChildren() );
+			validateNodesToInsert( node.getChildren(), errorContext );
 		}
 	}
 }
@@ -1860,7 +1863,8 @@ function isContainerOrFragment( node ) {
 // Throws {@link module:utils/ckeditorerror~CKEditorError CKEditorError} `view-writer-invalid-range-container` when validation fails.
 //
 // @param {module:engine/view/range~Range} range
-function validateRangeContainer( range ) {
+// @param {Object} errorContext
+function validateRangeContainer( range, errorContext ) {
 	const startContainer = getParentContainer( range.start );
 	const endContainer = getParentContainer( range.end );
 
@@ -1872,7 +1876,8 @@ function validateRangeContainer( range ) {
 		 *
 		 * @error view-writer-invalid-range-container
 		 */
-		throw new CKEditorError( 'view-writer-invalid-range-container' );
+
+		throw new CKEditorError( 'view-writer-invalid-range-container', errorContext );
 	}
 }
 

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -104,7 +104,7 @@ export default class EditableElement extends ContainerElement {
 			 *
 			 * @error view-editableelement-document-already-set
 			 */
-			throw new CKEditorError( 'view-editableelement-document-already-set: View document is already set.' );
+			throw new CKEditorError( 'view-editableelement-document-already-set: View document is already set.', this );
 		}
 
 		this._setCustomProperty( documentSymbol, document );

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

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

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

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

+ 5 - 2
packages/ckeditor5-engine/src/view/emptyelement.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -68,7 +68,10 @@ export default class EmptyElement extends Element {
 			 *
 			 * @error view-emptyelement-cannot-add
 			 */
-			throw new CKEditorError( 'view-emptyelement-cannot-add: Cannot add child nodes to EmptyElement instance.' );
+			throw new CKEditorError(
+				'view-emptyelement-cannot-add: Cannot add child nodes to EmptyElement instance.',
+				[ this, nodes ]
+			);
 		}
 	}
 }

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

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

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

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

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

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

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

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -62,7 +62,7 @@ export default class Node {
 			 *
 			 * @error view-node-not-found-in-parent
 			 */
-			throw new CKEditorError( 'view-node-not-found-in-parent: The node\'s parent does not contain this node.' );
+			throw new CKEditorError( 'view-node-not-found-in-parent: The node\'s parent does not contain this node.', this );
 		}
 
 		return pos;

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

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

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

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

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

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

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

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

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

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

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

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

+ 44 - 0
packages/ckeditor5-engine/src/view/observer/inputobserver.js

@@ -0,0 +1,44 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module engine/view/observer/inputobserver
+ */
+
+import DomEventObserver from './domeventobserver';
+
+/**
+ * Observer for events connected with data input.
+ *
+ * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
+ *
+ * @extends module:engine/view/observer/domeventobserver~DomEventObserver
+ */
+export default class InputObserver extends DomEventObserver {
+	constructor( view ) {
+		super( view );
+
+		this.domEventType = [ 'beforeinput' ];
+	}
+
+	onDomEvent( domEvent ) {
+		this.fire( domEvent.type, domEvent );
+	}
+}
+
+/**
+ * Fired before browser inputs (or deletes) some data.
+ *
+ * This event is available only on browsers which support DOM `beforeinput` event.
+ *
+ * Introduced by {@link module:engine/view/observer/inputobserver~InputObserver}.
+ *
+ * Note that because {@link module:engine/view/observer/inputobserver~InputObserver} is attached by the
+ * {@link module:engine/view/view~View} this event is available by default.
+ *
+ * @see module:engine/view/observer/inputobserver~InputObserver
+ * @event module:engine/view/document~Document#event:beforeinput
+ * @param {module:engine/view/observer/domeventdata~DomEventData} data Event data.
+ */

+ 2 - 2
packages/ckeditor5-engine/src/view/observer/keyobserver.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -11,7 +11,7 @@ import DomEventObserver from './domeventobserver';
 import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
 
 /**
- * {@link module:engine/view/document~Document#event:keydown Key down} event observer.
+ * Observer for events connected with pressing keyboard keys.
  *
  * Note that this observer is attached by the {@link module:engine/view/view~View} and is available by default.
  *

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

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

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

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

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

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

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

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

+ 2 - 4
packages/ckeditor5-engine/src/view/placeholder.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -140,9 +140,7 @@ export function hidePlaceholder( writer, element ) {
  * {@link module:engine/view/placeholder~enablePlaceholder `enablePlaceholder()`} in that case or make
  * sure the correct element is passed to the helper.
  *
- * @param {module:engine/view/downcastwriter~DowncastWriter} writer
  * @param {module:engine/view/element~Element} element
- * @param {String} text
  * @returns {Boolean}
  */
 export function needsPlaceholder( element ) {
@@ -176,7 +174,7 @@ export function needsPlaceholder( element ) {
 // Updates all placeholders associated with a document in a post–fixer callback.
 //
 // @private
-// @param { module:engine/model/document~Document} doc
+// @param { module:engine/view/document~Document} doc
 // @param {module:engine/view/downcastwriter~DowncastWriter} writer
 // @returns {Boolean} True if any changes were made to the view document.
 function updateDocumentPlaceholders( doc, writer ) {

+ 9 - 7
packages/ckeditor5-engine/src/view/position.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -21,9 +21,9 @@ import '@ckeditor/ckeditor5-utils/src/version';
  *
  * In order to create a new position instance use the `createPosition*()` factory methods available in:
  *
- * * {@module:engine/view/view~View}
- * * {@module:engine/view/downcastwriter~DowncastWriter}
- * * {@module:engine/view/upcastwriter~UpcastWriter}
+ * * {@link module:engine/view/view~View}
+ * * {@link module:engine/view/downcastwriter~DowncastWriter}
+ * * {@link module:engine/view/upcastwriter~UpcastWriter}
  */
 export default class Position {
 	/**
@@ -341,7 +341,9 @@ export default class Position {
 				 */
 				throw new CKEditorError(
 					'view-createPositionAt-offset-required: ' +
-					'View#createPositionAt() requires the offset when the first parameter is a view item.' );
+					'View#createPositionAt() requires the offset when the first parameter is a view item.',
+					node
+				);
 			}
 
 			return new Position( node, offset );
@@ -368,7 +370,7 @@ export default class Position {
 			 * @error view-position-after-root
 			 * @param {module:engine/view/node~Node} root
 			 */
-			throw new CKEditorError( 'view-position-after-root: You can not make position after root.', { root: item } );
+			throw new CKEditorError( 'view-position-after-root: You can not make position after root.', item, { root: item } );
 		}
 
 		return new Position( item.parent, item.index + 1 );
@@ -394,7 +396,7 @@ export default class Position {
 			 * @error view-position-before-root
 			 * @param {module:engine/view/node~Node} root
 			 */
-			throw new CKEditorError( 'view-position-before-root: You can not make position before root.', { root: item } );
+			throw new CKEditorError( 'view-position-before-root: You can not make position before root.', item, { root: item } );
 		}
 
 		return new Position( item.parent, item.index );

+ 4 - 4
packages/ckeditor5-engine/src/view/range.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /**
@@ -15,9 +15,9 @@ import TreeWalker from './treewalker';
  *
  * In order to create a new position instance use the `createPosition*()` factory methods available in:
  *
- * * {@module:engine/view/view~View}
- * * {@module:engine/view/downcastwriter~DowncastWriter}
- * * {@module:engine/view/upcastwriter~UpcastWriter}
+ * * {@link module:engine/view/view~View}
+ * * {@link module:engine/view/downcastwriter~DowncastWriter}
+ * * {@link module:engine/view/upcastwriter~UpcastWriter}
  */
 export default class Range {
 	/**

+ 3 - 3
packages/ckeditor5-engine/src/view/renderer.js

@@ -1,6 +1,6 @@
 /**
  * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
  */
 
 /* globals Node */
@@ -152,7 +152,7 @@ export default class Renderer {
 				 *
 				 * @error renderer-unknown-type
 				 */
-				throw new CKEditorError( 'view-renderer-unknown-type: Unknown type passed to Renderer.markToSync.' );
+				throw new CKEditorError( 'view-renderer-unknown-type: Unknown type passed to Renderer.markToSync.', this );
 			}
 		}
 	}
@@ -387,7 +387,7 @@ export default class Renderer {
 			 *
 			 * @error view-renderer-filler-was-lost
 			 */
-			throw new CKEditorError( 'view-renderer-filler-was-lost: The inline filler node was lost.' );
+			throw new CKEditorError( 'view-renderer-filler-was-lost: The inline filler node was lost.', this );
 		}
 
 		if ( isInlineFiller( domFillerNode ) ) {

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

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

Beberapa file tidak ditampilkan karena terlalu banyak file yang berubah dalam diff ini