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

Merge branch 'master' into unification

Maciej Bukowski 5 лет назад
Родитель
Сommit
579c4c9470
100 измененных файлов с 4009 добавлено и 574 удалено
  1. 1 1
      packages/ckeditor5-adapter-ckfinder/lang/translations/ko.po
  2. 4 4
      packages/ckeditor5-alignment/lang/translations/ko.po
  3. 1 1
      packages/ckeditor5-alignment/lang/translations/zh.po
  4. 2 2
      packages/ckeditor5-autoformat/tests/blockautoformatediting.js
  5. 1 1
      packages/ckeditor5-autosave/lang/translations/ko.po
  6. 1 1
      packages/ckeditor5-basic-styles/lang/translations/ko.po
  7. 37 0
      packages/ckeditor5-ckfinder/lang/translations/ko.po
  8. 23 0
      packages/ckeditor5-clipboard/src/clipboard.js
  9. 110 0
      packages/ckeditor5-clipboard/tests/clipboard.js
  10. 25 0
      packages/ckeditor5-code-block/lang/translations/ko.po
  11. 25 0
      packages/ckeditor5-code-block/lang/translations/zh.po
  12. 1 1
      packages/ckeditor5-core/lang/translations/ko.po
  13. 2 1
      packages/ckeditor5-core/src/editor/utils/dataapimixin.js
  14. 304 3
      packages/ckeditor5-engine/docs/framework/guides/deep-dive/schema.md
  15. 24 7
      packages/ckeditor5-engine/src/controller/datacontroller.js
  16. 7 2
      packages/ckeditor5-engine/src/conversion/downcastdispatcher.js
  17. 55 35
      packages/ckeditor5-engine/src/conversion/downcasthelpers.js
  18. 22 11
      packages/ckeditor5-engine/src/conversion/upcastdispatcher.js
  19. 56 36
      packages/ckeditor5-engine/src/conversion/upcasthelpers.js
  20. 3 3
      packages/ckeditor5-engine/src/dev-utils/model.js
  21. 15 8
      packages/ckeditor5-engine/src/model/model.js
  22. 81 2
      packages/ckeditor5-engine/src/model/schema.js
  23. 77 0
      packages/ckeditor5-engine/src/model/utils/autoparagraphing.js
  24. 16 13
      packages/ckeditor5-engine/src/model/utils/modifyselection.js
  25. 12 13
      packages/ckeditor5-engine/src/model/utils/selection-post-fixer.js
  26. 126 2
      packages/ckeditor5-engine/tests/controller/datacontroller.js
  27. 40 64
      packages/ckeditor5-engine/tests/conversion/downcasthelpers.js
  28. 37 0
      packages/ckeditor5-engine/tests/conversion/upcastdispatcher.js
  29. 51 7
      packages/ckeditor5-engine/tests/conversion/upcasthelpers.js
  30. 25 3
      packages/ckeditor5-engine/tests/model/model.js
  31. 113 0
      packages/ckeditor5-engine/tests/model/schema.js
  32. 1 1
      packages/ckeditor5-engine/tests/model/selection.js
  33. 8 3
      packages/ckeditor5-engine/tests/model/utils/deletecontent.js
  34. 70 0
      packages/ckeditor5-engine/tests/model/utils/modifyselection.js
  35. 30 4
      packages/ckeditor5-engine/tests/model/utils/selection-post-fixer.js
  36. 1 1
      packages/ckeditor5-enter/src/shiftenter.js
  37. 4 4
      packages/ckeditor5-font/lang/translations/ko.po
  38. 1 1
      packages/ckeditor5-font/lang/translations/zh.po
  39. 1 1
      packages/ckeditor5-font/src/fontfamily/fontfamilyediting.js
  40. 1 1
      packages/ckeditor5-font/src/fontsize/fontsizeediting.js
  41. 1 1
      packages/ckeditor5-font/src/utils.js
  42. 1 1
      packages/ckeditor5-font/tests/utils.js
  43. 7 7
      packages/ckeditor5-heading/lang/translations/ko.po
  44. 2 2
      packages/ckeditor5-heading/lang/translations/zh.po
  45. 53 0
      packages/ckeditor5-highlight/lang/translations/ko.po
  46. 1 1
      packages/ckeditor5-highlight/lang/translations/zh.po
  47. 21 0
      packages/ckeditor5-horizontal-line/lang/translations/zh.po
  48. 9 9
      packages/ckeditor5-horizontal-line/src/horizontallineediting.js
  49. 1 1
      packages/ckeditor5-horizontal-line/tests/horizontallinecommand.js
  50. 7 7
      packages/ckeditor5-image/lang/translations/de.po
  51. 13 13
      packages/ckeditor5-image/lang/translations/ko.po
  52. 5 5
      packages/ckeditor5-image/lang/translations/sr-latn.po
  53. 5 5
      packages/ckeditor5-image/lang/translations/sr.po
  54. 6 6
      packages/ckeditor5-image/lang/translations/zh.po
  55. 3 3
      packages/ckeditor5-image/src/image/imageediting.js
  56. 3 3
      packages/ckeditor5-image/tests/image/converters.js
  57. 1 1
      packages/ckeditor5-image/tests/image/imageinsertcommand.js
  58. 1 1
      packages/ckeditor5-image/tests/image/utils.js
  59. 1 1
      packages/ckeditor5-image/tests/imagecaption/imagecaptionediting.js
  60. 1 1
      packages/ckeditor5-image/tests/imageupload/imageuploadcommand.js
  61. 2 2
      packages/ckeditor5-indent/lang/translations/ko.po
  62. 1 1
      packages/ckeditor5-link/lang/translations/ko.po
  63. 1 1
      packages/ckeditor5-link/lang/translations/zh.po
  64. 3 3
      packages/ckeditor5-link/src/linkediting.js
  65. 2 1
      packages/ckeditor5-link/src/utils.js
  66. 4 2
      packages/ckeditor5-link/tests/utils.js
  67. 3 3
      packages/ckeditor5-list/lang/translations/ko.po
  68. 1 1
      packages/ckeditor5-list/lang/translations/zh.po
  69. 2 2
      packages/ckeditor5-list/tests/todolistediting.js
  70. 4 4
      packages/ckeditor5-media-embed/lang/translations/ko.po
  71. 6 6
      packages/ckeditor5-media-embed/lang/translations/zh.po
  72. 9 9
      packages/ckeditor5-media-embed/src/mediaembedediting.js
  73. 2 2
      packages/ckeditor5-mention/docs/_snippets/examples/chat-with-mentions.js
  74. 2 2
      packages/ckeditor5-mention/docs/_snippets/features/mention-customization.js
  75. 2 2
      packages/ckeditor5-mention/docs/examples/chat-with-mentions.md
  76. 4 4
      packages/ckeditor5-mention/docs/features/mentions.md
  77. 3 3
      packages/ckeditor5-mention/src/mentionediting.js
  78. 2 2
      packages/ckeditor5-mention/tests/manual/mention-custom-view.js
  79. 7 7
      packages/ckeditor5-mention/tests/manual/mention.js
  80. 4 4
      packages/ckeditor5-mention/tests/mentionediting.js
  81. 1 1
      packages/ckeditor5-mention/tests/mentionui.js
  82. 21 0
      packages/ckeditor5-page-break/lang/translations/zh.po
  83. 14 14
      packages/ckeditor5-page-break/src/pagebreakediting.js
  84. 1 1
      packages/ckeditor5-page-break/tests/pagebreakcommand.js
  85. 5 89
      packages/ckeditor5-paragraph/src/paragraph.js
  86. 18 5
      packages/ckeditor5-paragraph/tests/paragraph.js
  87. 37 0
      packages/ckeditor5-restricted-editing/lang/translations/ko.po
  88. 37 0
      packages/ckeditor5-restricted-editing/lang/translations/zh.po
  89. 4 4
      packages/ckeditor5-restricted-editing/src/restrictededitingmodeediting.js
  90. 2 2
      packages/ckeditor5-restricted-editing/src/standardeditingmodeediting.js
  91. 21 0
      packages/ckeditor5-select-all/lang/translations/zh.po
  92. 1037 0
      packages/ckeditor5-special-characters/lang/translations/ko.po
  93. 1037 0
      packages/ckeditor5-special-characters/lang/translations/zh.po
  94. 40 40
      packages/ckeditor5-table/lang/translations/zh.po
  95. 2 2
      packages/ckeditor5-table/src/tableediting.js
  96. 42 0
      packages/ckeditor5-table/tests/converters/upcasttable.js
  97. 10 0
      packages/ckeditor5-table/tests/table-integration.js
  98. 2 1
      packages/ckeditor5-table/tests/tableediting.js
  99. 57 48
      packages/ckeditor5-table/tests/tablekeyboard.js
  100. 1 1
      packages/ckeditor5-typing/tests/utils/inlinehighlight.js

+ 1 - 1
packages/ckeditor5-adapter-ckfinder/lang/translations/ko.po

@@ -18,4 +18,4 @@ msgstr ""
 
 
 msgctxt "A generic error message displayed on upload failure. The file name is concatenated to this text."
 msgctxt "A generic error message displayed on upload failure. The file name is concatenated to this text."
 msgid "Cannot upload file:"
 msgid "Cannot upload file:"
-msgstr "파일 업로드 불가"
+msgstr "파일 업로드할 수 없음: "

+ 4 - 4
packages/ckeditor5-alignment/lang/translations/ko.po

@@ -18,19 +18,19 @@ msgstr ""
 
 
 msgctxt "Toolbar button tooltip for aligning the text to the left."
 msgctxt "Toolbar button tooltip for aligning the text to the left."
 msgid "Align left"
 msgid "Align left"
-msgstr "왼쪽 맞춤"
+msgstr "왼쪽 정렬"
 
 
 msgctxt "Toolbar button tooltip for aligning the text to the right."
 msgctxt "Toolbar button tooltip for aligning the text to the right."
 msgid "Align right"
 msgid "Align right"
-msgstr "오른쪽 맞춤"
+msgstr "오른쪽 정렬"
 
 
 msgctxt "Toolbar button tooltip for aligning the text to center."
 msgctxt "Toolbar button tooltip for aligning the text to center."
 msgid "Align center"
 msgid "Align center"
-msgstr "가운데 맞춤"
+msgstr "가운데 정렬"
 
 
 msgctxt "Toolbar button tooltip for making the text justified."
 msgctxt "Toolbar button tooltip for making the text justified."
 msgid "Justify"
 msgid "Justify"
-msgstr "양쪽 맞춤"
+msgstr "양쪽 정렬"
 
 
 msgctxt "Dropdown button tooltip for the text alignment feature."
 msgctxt "Dropdown button tooltip for the text alignment feature."
 msgid "Text alignment"
 msgid "Text alignment"

+ 1 - 1
packages/ckeditor5-alignment/lang/translations/zh.po

@@ -38,4 +38,4 @@ msgstr "文字對齊"
 
 
 msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
 msgctxt "Label used by assistive technologies describing the text alignment feature toolbar."
 msgid "Text alignment toolbar"
 msgid "Text alignment toolbar"
-msgstr ""
+msgstr "文字對齊"

+ 2 - 2
packages/ckeditor5-autoformat/tests/blockautoformatediting.js

@@ -167,7 +167,7 @@ describe( 'blockAutoformatEditing', () => {
 			editor.conversion.for( 'downcast' )
 			editor.conversion.for( 'downcast' )
 				.elementToElement( {
 				.elementToElement( {
 					model: 'softBreak',
 					model: 'softBreak',
-					view: ( modelElement, viewWriter ) => viewWriter.createEmptyElement( 'br' )
+					view: ( modelElement, { writer } ) => writer.createEmptyElement( 'br' )
 				} );
 				} );
 
 
 			const spy = testUtils.sinon.spy();
 			const spy = testUtils.sinon.spy();
@@ -207,7 +207,7 @@ describe( 'blockAutoformatEditing', () => {
 			editor.conversion.for( 'downcast' )
 			editor.conversion.for( 'downcast' )
 				.elementToElement( {
 				.elementToElement( {
 					model: 'softBreak',
 					model: 'softBreak',
-					view: ( modelElement, viewWriter ) => viewWriter.createEmptyElement( 'br' )
+					view: ( modelElement, { writer } ) => writer.createEmptyElement( 'br' )
 				} );
 				} );
 
 
 			const spy = testUtils.sinon.spy();
 			const spy = testUtils.sinon.spy();

+ 1 - 1
packages/ckeditor5-autosave/lang/translations/ko.po

@@ -18,4 +18,4 @@ msgstr ""
 
 
 msgctxt "A message that the data is being saved."
 msgctxt "A message that the data is being saved."
 msgid "Saving changes"
 msgid "Saving changes"
-msgstr "변경사항 저장"
+msgstr "변경된 내용을 저장하고 있습니다"

+ 1 - 1
packages/ckeditor5-basic-styles/lang/translations/ko.po

@@ -30,7 +30,7 @@ msgstr "밑줄"
 
 
 msgctxt "Toolbar button tooltip for the Code feature."
 msgctxt "Toolbar button tooltip for the Code feature."
 msgid "Code"
 msgid "Code"
-msgstr "소스"
+msgstr "코드"
 
 
 msgctxt "Toolbar button tooltip for the Strikethrough feature."
 msgctxt "Toolbar button tooltip for the Strikethrough feature."
 msgid "Strikethrough"
 msgid "Strikethrough"

+ 37 - 0
packages/ckeditor5-ckfinder/lang/translations/ko.po

@@ -0,0 +1,37 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Korean (https://www.transifex.com/ckeditor/teams/11143/ko/)\n"
+"Language: ko\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Toolbar button tooltip for inserting an image or file via a CKFinder file browser."
+msgid "Insert image or file"
+msgstr "사진이나 파일을 삽입"
+
+msgctxt "Error message displayed when inserting a resized version of an image failed."
+msgid "Could not obtain resized image URL."
+msgstr "크기가 조절된 사진의 URL을 가져오지 못했습니다."
+
+msgctxt "Title of a notification displayed when inserting a resized version of an image failed."
+msgid "Selecting resized image failed"
+msgstr "크기가 조절된 이미지 선택 실패"
+
+msgctxt "Error message displayed when an image cannot be inserted at the current position."
+msgid "Could not insert image at the current position."
+msgstr "현재 위치에 사진을 삽입할 수 없습니다."
+
+msgctxt "Title of a notification displayed when an image cannot be inserted at the current position."
+msgid "Inserting image failed"
+msgstr "사진 삽입 실패"

+ 23 - 0
packages/ckeditor5-clipboard/src/clipboard.js

@@ -103,6 +103,15 @@ export default class Clipboard extends Plugin {
 					return;
 					return;
 				}
 				}
 
 
+				// While pasting plain text, apply selection attributes on the text.
+				if ( isPlainText( modelFragment ) ) {
+					const node = modelFragment.getChild( 0 );
+
+					model.change( writer => {
+						writer.setAttributes( modelDocument.selection.getAttributes(), node );
+					} );
+				}
+
 				model.insertContent( modelFragment );
 				model.insertContent( modelFragment );
 				evt.stop();
 				evt.stop();
 			}
 			}
@@ -198,3 +207,17 @@ export default class Clipboard extends Plugin {
  *
  *
  * @member {'copy'|'cut'} module:clipboard/clipboard~ClipboardOutputEventData#method
  * @member {'copy'|'cut'} module:clipboard/clipboard~ClipboardOutputEventData#method
  */
  */
+
+// Returns true if specified `documentFragment` represents a plain text.
+//
+// @param {module:engine/view/documentfragment~DocumentFragment} documentFragment
+// @returns {Boolean}
+function isPlainText( documentFragment ) {
+	if ( documentFragment.childCount > 1 ) {
+		return false;
+	}
+
+	const child = documentFragment.getChild( 0 );
+
+	return [ ...child.getAttributeKeys() ].length == 0;
+}

+ 110 - 0
packages/ckeditor5-clipboard/tests/clipboard.js

@@ -343,6 +343,116 @@ describe( 'Clipboard feature', () => {
 			expect( spy.callCount ).to.equal( 1 );
 			expect( spy.callCount ).to.equal( 1 );
 		} );
 		} );
 
 
+		// https://github.com/ckeditor/ckeditor5/issues/1006
+		describe( 'pasting plain text', () => {
+			let model;
+
+			beforeEach( () => {
+				model = editor.model;
+
+				model.schema.extend( '$text', { allowAttributes: 'bold' } );
+			} );
+
+			it( 'should inherit selection attributes (collapsed selection)', () => {
+				const insertContent = model.insertContent.bind( model );
+				let insertedNode;
+
+				sinon.stub( model, 'insertContent' ).callsFake( documentFragment => {
+					insertedNode = documentFragment.getChild( 0 );
+
+					return insertContent( documentFragment );
+				} );
+
+				setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );
+
+				const dataTransferMock = createDataTransfer( { 'text/plain': 'foo' } );
+
+				viewDocument.fire( 'paste', {
+					dataTransfer: dataTransferMock,
+					stopPropagation() {},
+					preventDefault() {}
+				} );
+
+				expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
+				expect( insertedNode.getAttribute( 'bold' ) ).to.equal( true );
+			} );
+
+			it( 'should inherit selection attributes (non-collapsed selection)', () => {
+				const insertContent = model.insertContent.bind( model );
+				let insertedNode;
+
+				sinon.stub( model, 'insertContent' ).callsFake( documentFragment => {
+					insertedNode = documentFragment.getChild( 0 );
+
+					return insertContent( documentFragment );
+				} );
+
+				setModelData( model, '<paragraph><$text bold="true">Bolded [text.]</$text></paragraph>' );
+
+				const dataTransferMock = createDataTransfer( { 'text/plain': 'foo' } );
+
+				viewDocument.fire( 'paste', {
+					dataTransfer: dataTransferMock,
+					stopPropagation() {},
+					preventDefault() {}
+				} );
+
+				expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]</$text></paragraph>' );
+				expect( insertedNode.getAttribute( 'bold' ) ).to.equal( true );
+			} );
+
+			it( 'should inherit selection attributes while pasting a plain text as text/html', () => {
+				setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );
+
+				const dataTransferMock = createDataTransfer( {
+					'text/html': 'foo',
+					'text/plain': 'foo'
+				} );
+
+				viewDocument.fire( 'paste', {
+					dataTransfer: dataTransferMock,
+					stopPropagation() {},
+					preventDefault() {}
+				} );
+
+				expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
+			} );
+
+			it( 'should inherit selection attributes while pasting a plain text as text/html (Chrome style)', () => {
+				setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );
+
+				const dataTransferMock = createDataTransfer( {
+					'text/html': '<meta http-equiv="content-type" content="text/html; charset=utf-8">foo',
+					'text/plain': 'foo'
+				} );
+
+				viewDocument.fire( 'paste', {
+					dataTransfer: dataTransferMock,
+					stopPropagation() {},
+					preventDefault() {}
+				} );
+
+				expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
+			} );
+
+			it( 'should inherit selection attributes while pasting HTML with unsupported attributes', () => {
+				setModelData( model, '<paragraph><$text bold="true">Bolded []text.</$text></paragraph>' );
+
+				const dataTransferMock = createDataTransfer( {
+					'text/html': '<i>foo</i>',
+					'text/plain': 'foo'
+				} );
+
+				viewDocument.fire( 'paste', {
+					dataTransfer: dataTransferMock,
+					stopPropagation() {},
+					preventDefault() {}
+				} );
+
+				expect( getModelData( model ) ).to.equal( '<paragraph><$text bold="true">Bolded foo[]text.</$text></paragraph>' );
+			} );
+		} );
+
 		function createDataTransfer( data ) {
 		function createDataTransfer( data ) {
 			return {
 			return {
 				getData( type ) {
 				getData( type ) {

+ 25 - 0
packages/ckeditor5-code-block/lang/translations/ko.po

@@ -0,0 +1,25 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Korean (https://www.transifex.com/ckeditor/teams/11143/ko/)\n"
+"Language: ko\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "A label of the button that allows inserting a new code block into the editor content."
+msgid "Insert code block"
+msgstr "코드 블럭 삽입"
+
+msgctxt "A language of the code block in the editor content when no specific programming language is associated with it."
+msgid "Plain text"
+msgstr "평문"

+ 25 - 0
packages/ckeditor5-code-block/lang/translations/zh.po

@@ -0,0 +1,25 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Chinese (Taiwan) (https://www.transifex.com/ckeditor/teams/11143/zh_TW/)\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "A label of the button that allows inserting a new code block into the editor content."
+msgid "Insert code block"
+msgstr "插入程式碼區塊"
+
+msgctxt "A language of the code block in the editor content when no specific programming language is associated with it."
+msgid "Plain text"
+msgstr "純文字"

+ 1 - 1
packages/ckeditor5-core/lang/translations/ko.po

@@ -26,4 +26,4 @@ msgstr "취소"
 
 
 msgctxt "The label used by a button next to the color palette in the color picker that removes the color (resets it to an empty value, example usages in font color or table properties)."
 msgctxt "The label used by a button next to the color palette in the color picker that removes the color (resets it to an empty value, example usages in font color or table properties)."
 msgid "Remove color"
 msgid "Remove color"
-msgstr "색상 지우기"
+msgstr "색깔 제거"

+ 2 - 1
packages/ckeditor5-core/src/editor/utils/dataapimixin.js

@@ -71,7 +71,8 @@ export default DataApiMixin;
  * the right format for you.
  * the right format for you.
  *
  *
  * @method #getData
  * @method #getData
- * @param {Object} [options]
+ * @param {Object} [options] Additional configuration for the retrieved data.
+ * Editor features may introduce more configuration options that can be set through this parameter.
  * @param {String} [options.rootName='main'] Root name.
  * @param {String} [options.rootName='main'] Root name.
  * @param {String} [options.trim='empty'] Whether returned data should be trimmed. This option is set to `'empty'` by default,
  * @param {String} [options.trim='empty'] Whether returned data should be trimmed. This option is set to `'empty'` by default,
  * which means that whenever editor content is considered empty, an empty string is returned. To turn off trimming
  * which means that whenever editor content is considered empty, an empty string is returned. To turn off trimming

+ 304 - 3
packages/ckeditor5-engine/docs/framework/guides/deep-dive/schema.md

@@ -1,5 +1,6 @@
 ---
 ---
 category: framework-deep-dive
 category: framework-deep-dive
+classes: schema-deep-dive
 ---
 ---
 
 
 # Schema
 # Schema
@@ -48,6 +49,222 @@ While this would be incorrect:
 
 
 In addition to setting allowed structures, the schema can also define additional traits of model elements. By using the `is*` properties, a feature author may declare how a certain element should be treated by other features and the engine.
 In addition to setting allowed structures, the schema can also define additional traits of model elements. By using the `is*` properties, a feature author may declare how a certain element should be treated by other features and the engine.
 
 
+Here is a table listing various model elements and their properties registered in the schema:
+
+<table>
+	<thead>
+		<tr>
+			<th rowspan="2">Schema entry</th>
+			<th colspan="6">Properties in the <a href="#defining-allowed-structures">definition</a></th>
+		</tr>
+		<tr>
+			<th><a href="#block-elements"><code>isBlock</code></a></th>
+			<th><a href="#limit-elements"><code>isLimit</code></a></th>
+			<th><a href="#object-elements"><code>isObject</code></a></th>
+			<th><a href="#inline-elements"><code>isInline</code></a></th>
+			<th><a href="#selectable-elements"><code>isSelectable</code></a></th>
+			<th><a href="#content-elements"><code>isContent</code></a></th>
+		</tr>
+	</thead>
+	<tbody>
+		<tr>
+			<td><code>$block</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>$clipboardHolder</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>$marker</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>$root</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>$text</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+		</tr>
+		<tr>
+			<td><code>blockQuote</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>caption</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>codeBlock</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>heading1</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>heading2</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>heading3</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>horizontalLine</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited1"><sup>[1]</sup></a></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited2"><sup>[2]</sup></a></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited3"><sup>[3]</sup></a></td>
+		</tr>
+		<tr>
+			<td><code>image</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited1"><sup>[1]</sup></a></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited2"><sup>[2]</sup></a></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited3"><sup>[3]</sup></a></td>
+		</tr>
+		<tr>
+			<td><code>listItem</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>media</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited1"><sup>[1]</sup></a></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited2"><sup>[2]</sup></a></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited3"><sup>[3]</sup></a></td>
+		</tr>
+		<tr>
+			<td><code>pageBreak</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited1"><sup>[1]</sup></a></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited2"><sup>[2]</sup></a></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited3"><sup>[3]</sup></a></td>
+		</tr>
+		<tr>
+			<td><code>paragraph</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>softBreak</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>table</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited1"><sup>[1]</sup></a></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited2"><sup>[2]</sup></a></td>
+			<td class="value_positive_inherited"><code>true</code><a href="#inherited3"><sup>[3]</sup></a></td>
+		</tr>
+		<tr>
+			<td><code>tableRow</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+		<tr>
+			<td><code>tableCell</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_negative"><code>false</code></td>
+			<td class="value_positive"><code>true</code></td>
+			<td class="value_negative"><code>false</code></td>
+		</tr>
+	</tbody>
+</table>
+
+<info-box>
+	* <span id="inherited1">[1]</span> The value of `isLimit` is `true` for this element because all [objects](#object-elements) are automatically [limit elements](#limit-elements),
+	* <span id="inherited2">[2]</span> The value of `isSelectable` is `true` for this element because all [objects](#object-elements) are automatically [selectable elements](#selectable-elements),
+	* <span id="inherited3">[3]</span> The value of `isContent` is `true` for this element because all [objects](#object-elements) are automatically [content elements](#content-elements).
+</info-box>
+
 ### Limit elements
 ### Limit elements
 
 
 Consider a feature like an image caption. The caption text area should construct a boundary to some internal actions:
 Consider a feature like an image caption. The caption text area should construct a boundary to some internal actions:
@@ -86,9 +303,11 @@ schema.register( 'myImage', {
 The {@link module:engine/model/schema~Schema#isObject `Schema#isObject()`} can later be used to check this property.
 The {@link module:engine/model/schema~Schema#isObject `Schema#isObject()`} can later be used to check this property.
 
 
 <info-box>
 <info-box>
-	Every "object" is also a "limit" element.
+	Every "object" is automatically also:
 
 
-	It means that for every element with `isObject` set to `true`, {@link module:engine/model/schema~Schema#isLimit `Schema#isLimit( element )`} will always return `true`.
+	* a [limit element](#limit-elements) – for every element with `isObject` set `true`, {@link module:engine/model/schema~Schema#isLimit `Schema#isLimit( element )`} will always return `true`.
+	* a [selectable element](#selectable-elements) – for every element with `isObject` set `true`, {@link module:engine/model/schema~Schema#isSelectable `Schema#isSelectable( element )`} will always return `true`.
+	* a [content element](#content-elements) – for every element with `isObject` set `true`, {@link module:engine/model/schema~Schema#isContent `Schema#isContent( element )`} will always return `true`.
 </info-box>
 </info-box>
 
 
 ### Block elements
 ### Block elements
@@ -109,6 +328,42 @@ Currently, the {@link module:engine/model/schema~SchemaItemDefinition#isInline `
 
 
 The support for inline elements in CKEditor 5 is so far limited to self-contained elements. Because of this, all elements marked with `isInline` should also be marked with `isObject`.
 The support for inline elements in CKEditor 5 is so far limited to self-contained elements. Because of this, all elements marked with `isInline` should also be marked with `isObject`.
 
 
+### Selectable elements
+
+Elements that users can select as a whole (with all their internals) and then, for instance, copy them or apply formatting, are marked with the {@link module:engine/model/schema~SchemaItemDefinition#isSelectable `isSelectable`} property in the schema:
+
+```js
+schema.register( 'mySelectable', {
+	isSelectable: true
+} );
+```
+
+The {@link module:engine/model/schema~Schema#isSelectable `Schema#isSelectable()`} method can later be used to check this property.
+
+<info-box>
+	All [object elements](#object-elements) are selectable by default. There are other selectable elements registered in the editor, though. For instance, there is also the `tableCell` model element (rendered as a `<td>` in the editing view) that is selectable while **not** registered as an object. The {@link features/table#table-selection table selection} plugin takes advantage of this fact and allows users create rectangular selections made of multiple table cells.
+</info-box>
+
+### Content elements
+
+You can tell content model elements from other elements by looking at their representation in the editor data (you can use {@link module:editor-classic/classiceditor~ClassicEditor#getData `editor.getData()`} or {@link module:engine/model/model~Model#hasContent Model#hasContent()} to check this out).
+
+Elements such as images or media will **always** find their way into editor data and this is what makes them content elements. They are marked with the {@link module:engine/model/schema~SchemaItemDefinition#isContent `isContent`} property in the schema:
+
+```js
+schema.register( 'myImage', {
+	isContent: true
+} );
+```
+
+The {@link module:engine/model/schema~Schema#isContent `Schema#isContent()`} method can later be used to check this property.
+
+At the same time, elements like paragraphs, list items, or headings **are not** content elements because they are skipped in the editor output when they are empty. From the data perspective they are transparent unless they contain other content elements (an empty paragraph is as good as no paragraph).
+
+<info-box>
+	[Object elements](#object-elements) and [`$text`](#generic-items) are content by default.
+</info-box>
+
 ## Generic items
 ## Generic items
 
 
 There are three basic generic items: `$root`, `$block` and `$text`. They are defined as follows:
 There are three basic generic items: `$root`, `$block` and `$text`. They are defined as follows:
@@ -267,4 +522,50 @@ Finally, the schema plays a crucial role during the conversion from the view to
 	Some features may miss schema checks. If you happen to find such a scenario, do not hesitate to [report it to us](https://github.com/ckeditor/ckeditor5/issues).
 	Some features may miss schema checks. If you happen to find such a scenario, do not hesitate to [report it to us](https://github.com/ckeditor/ckeditor5/issues).
 </info-box>
 </info-box>
 
 
-
+<style>
+.schema-deep-dive table {
+	text-align: center;
+}
+
+.schema-deep-dive table td,
+.schema-deep-dive table th {
+	border-color: hsl(72deg 6% 16%);
+}
+
+.schema-deep-dive table thead th {
+	font-weight: bold;
+	vertical-align: middle;
+}
+
+.schema-deep-dive table thead th code {
+	white-space: nowrap;
+}
+
+.schema-deep-dive table tbody td.value_negative {
+	background: hsl(354deg, 100%, 90%);
+}
+
+.schema-deep-dive table tbody td.value_positive {
+	background: hsl(88deg, 50%, 60%);
+}
+
+.schema-deep-dive table tbody td.value_negative code,
+.schema-deep-dive table tbody td.value_positive code,
+.schema-deep-dive table tbody td.value_positive_inherited code {
+	background: none;
+	text-shadow: 0px 0px 2px hsl(0deg, 0%, 100%);
+}
+
+.schema-deep-dive table tbody td.value_positive_inherited {
+	background-image: linear-gradient(45deg, hsl(88deg, 50%, 60%) 25%, hsl(89deg, 58% ,71%) 25%, hsl(89deg, 58%, 71%) 50%, hsl(88deg, 50%, 60%) 50%, hsl(88deg, 50%, 60%) 75%, hsl(89deg, 58%, 71%) 75%, hsl(89deg, 58%, 71%) 100%);
+	background-size: 3px 3px;
+}
+
+.schema-deep-dive table tbody td sup {
+	top: -0.5em;
+	position: relative;
+	font-size: 75%;
+	line-height: 0;
+	vertical-align: baseline;
+}
+</style>

+ 24 - 7
packages/ckeditor5-engine/src/controller/datacontroller.js

@@ -24,6 +24,7 @@ import ViewDocument from '../view/document';
 import ViewDowncastWriter from '../view/downcastwriter';
 import ViewDowncastWriter from '../view/downcastwriter';
 
 
 import ModelRange from '../model/range';
 import ModelRange from '../model/range';
+import { autoParagraphEmptyRoots } from '../model/utils/autoparagraphing';
 
 
 /**
 /**
  * Controller for the data pipeline. The data pipeline controls how data is retrieved from the document
  * Controller for the data pipeline. The data pipeline controls how data is retrieved from the document
@@ -140,21 +141,28 @@ export default class DataController {
 		this.on( 'init', () => {
 		this.on( 'init', () => {
 			this.fire( 'ready' );
 			this.fire( 'ready' );
 		}, { priority: 'lowest' } );
 		}, { priority: 'lowest' } );
+
+		// Fix empty roots after DataController is 'ready' (note that init method could be decorated and stopped).
+		// We need to handle this event because initial data could be empty and post-fixer would not get triggered.
+		this.on( 'ready', () => {
+			this.model.enqueueChange( 'transparent', autoParagraphEmptyRoots );
+		}, { priority: 'lowest' } );
 	}
 	}
 
 
 	/**
 	/**
 	 * Returns the model's data converted by downcast dispatchers attached to {@link #downcastDispatcher} and
 	 * Returns the model's data converted by downcast dispatchers attached to {@link #downcastDispatcher} and
 	 * formatted by the {@link #processor data processor}.
 	 * formatted by the {@link #processor data processor}.
 	 *
 	 *
-	 * @param {Object} [options]
+	 * @param {Object} [options] Additional configuration for the retrieved data. `DataController` provides two optional
+	 * properties: `rootName` and `trim`. Other properties of this object are specified by various editor features.
 	 * @param {String} [options.rootName='main'] Root name.
 	 * @param {String} [options.rootName='main'] Root name.
 	 * @param {String} [options.trim='empty'] Whether returned data should be trimmed. This option is set to `empty` by default,
 	 * @param {String} [options.trim='empty'] Whether returned data should be trimmed. This option is set to `empty` by default,
 	 * which means whenever editor content is considered empty, an empty string will be returned. To turn off trimming completely
 	 * which means whenever editor content is considered empty, an empty string will be returned. To turn off trimming completely
 	 * use `'none'`. In such cases exact content will be returned (for example `<p>&nbsp;</p>` for an empty editor).
 	 * use `'none'`. In such cases exact content will be returned (for example `<p>&nbsp;</p>` for an empty editor).
 	 * @returns {String} Output data.
 	 * @returns {String} Output data.
 	 */
 	 */
-	get( options ) {
-		const { rootName = 'main', trim = 'empty' } = options || {};
+	get( options = {} ) {
+		const { rootName = 'main', trim = 'empty' } = options;
 
 
 		if ( !this._checkIfRootsExists( [ rootName ] ) ) {
 		if ( !this._checkIfRootsExists( [ rootName ] ) ) {
 			/**
 			/**
@@ -177,7 +185,7 @@ export default class DataController {
 			return '';
 			return '';
 		}
 		}
 
 
-		return this.stringify( root );
+		return this.stringify( root, options );
 	}
 	}
 
 
 	/**
 	/**
@@ -187,11 +195,12 @@ export default class DataController {
 	 *
 	 *
 	 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
 	 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
 	 * Element whose content will be stringified.
 	 * Element whose content will be stringified.
+	 * @param {Object} [options] Additional configuration passed to the conversion process.
 	 * @returns {String} Output data.
 	 * @returns {String} Output data.
 	 */
 	 */
-	stringify( modelElementOrFragment ) {
+	stringify( modelElementOrFragment, options ) {
 		// Model -> view.
 		// Model -> view.
-		const viewDocumentFragment = this.toView( modelElementOrFragment );
+		const viewDocumentFragment = this.toView( modelElementOrFragment, options );
 
 
 		// View -> data.
 		// View -> data.
 		return this.processor.toData( viewDocumentFragment );
 		return this.processor.toData( viewDocumentFragment );
@@ -205,9 +214,11 @@ export default class DataController {
 	 *
 	 *
 	 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
 	 * @param {module:engine/model/element~Element|module:engine/model/documentfragment~DocumentFragment} modelElementOrFragment
 	 * Element or document fragment whose content will be converted.
 	 * Element or document fragment whose content will be converted.
+	 * @param {Object} [options] Additional configuration that will be available through
+	 * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi#options} during the conversion process.
 	 * @returns {module:engine/view/documentfragment~DocumentFragment} Output view DocumentFragment.
 	 * @returns {module:engine/view/documentfragment~DocumentFragment} Output view DocumentFragment.
 	 */
 	 */
-	toView( modelElementOrFragment ) {
+	toView( modelElementOrFragment, options ) {
 		const viewDocument = this.viewDocument;
 		const viewDocument = this.viewDocument;
 		const viewWriter = this._viewWriter;
 		const viewWriter = this._viewWriter;
 
 
@@ -220,6 +231,9 @@ export default class DataController {
 
 
 		this.mapper.bindElements( modelElementOrFragment, viewDocumentFragment );
 		this.mapper.bindElements( modelElementOrFragment, viewDocumentFragment );
 
 
+		// Make additional options available during conversion process through `conversionApi`.
+		this.downcastDispatcher.conversionApi.options = options;
+
 		// We have no view controller and rendering to DOM in DataController so view.change() block is not used here.
 		// We have no view controller and rendering to DOM in DataController so view.change() block is not used here.
 		this.downcastDispatcher.convertInsert( modelRange, viewWriter );
 		this.downcastDispatcher.convertInsert( modelRange, viewWriter );
 
 
@@ -233,6 +247,9 @@ export default class DataController {
 			}
 			}
 		}
 		}
 
 
+		// Clean `conversionApi`.
+		delete this.downcastDispatcher.conversionApi.options;
+
 		return viewDocumentFragment;
 		return viewDocumentFragment;
 	}
 	}
 
 

+ 7 - 2
packages/ckeditor5-engine/src/conversion/downcastdispatcher.js

@@ -11,7 +11,6 @@ import Consumable from './modelconsumable';
 import Range from '../model/range';
 import Range from '../model/range';
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
 import mix from '@ckeditor/ckeditor5-utils/src/mix';
-import { extend } from 'lodash-es';
 
 
 /**
 /**
  * Downcast dispatcher is a central point of downcasting (conversion from the model to the view), which is a process of reacting to changes
  * Downcast dispatcher is a central point of downcasting (conversion from the model to the view), which is a process of reacting to changes
@@ -115,7 +114,7 @@ export default class DowncastDispatcher {
 		 *
 		 *
 		 * @member {module:engine/conversion/downcastdispatcher~DowncastConversionApi}
 		 * @member {module:engine/conversion/downcastdispatcher~DowncastConversionApi}
 		 */
 		 */
-		this.conversionApi = extend( { dispatcher: this }, conversionApi );
+		this.conversionApi = Object.assign( { dispatcher: this }, conversionApi );
 	}
 	}
 
 
 	/**
 	/**
@@ -669,3 +668,9 @@ function shouldMarkerChangeBeConverted( modelPosition, marker, mapper ) {
  *
  *
  * @member {module:engine/view/downcastwriter~DowncastWriter} #writer
  * @member {module:engine/view/downcastwriter~DowncastWriter} #writer
  */
  */
+
+/**
+ * An object with an additional configuration which can be used during conversion process. Available only for data downcast conversion.
+ *
+ * @member {Object} #options
+ */

+ 55 - 35
packages/ckeditor5-engine/src/conversion/downcasthelpers.js

@@ -52,8 +52,10 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *
 	 *
 	 *		editor.conversion.for( 'downcast' ).elementToElement( {
 	 *		editor.conversion.for( 'downcast' ).elementToElement( {
 	 *			model: 'heading',
 	 *			model: 'heading',
-	 *			view: ( modelElement, viewWriter ) => {
-	 *				return viewWriter.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) )
+	 *			view: ( modelElement, conversionApi ) => {
+	 *				const { writer } = conversionApi;
+	 *
+	 *				return writer.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) );
 	 *			}
 	 *			}
 	 *		} );
 	 *		} );
 	 *
 	 *
@@ -64,7 +66,7 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 * @param {Object} config Conversion configuration.
 	 * @param {Object} config Conversion configuration.
 	 * @param {String} config.model The name of the model element to convert.
 	 * @param {String} config.model The name of the model element to convert.
 	 * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function
 	 * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function
-	 * that takes the model element and {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer}
+	 * that takes the model element and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
 	 * as parameters and returns a view container element.
 	 * as parameters and returns a view container element.
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 */
 	 */
@@ -120,8 +122,10 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *
 	 *
 	 *		editor.conversion.for( 'downcast' ).attributeToElement( {
 	 *		editor.conversion.for( 'downcast' ).attributeToElement( {
 	 *			model: 'bold',
 	 *			model: 'bold',
-	 *			view: ( modelAttributeValue, viewWriter ) => {
-	 *				return viewWriter.createAttributeElement( 'span', {
+	 *			view: ( modelAttributeValue, conversionApi ) => {
+	 *				const { writer } = conversionApi;
+	 *
+	 *				return writer.createAttributeElement( 'span', {
 	 *					style: 'font-weight:' + modelAttributeValue
 	 *					style: 'font-weight:' + modelAttributeValue
 	 *				} );
 	 *				} );
 	 *			}
 	 *			}
@@ -132,8 +136,10 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *				key: 'color',
 	 *				key: 'color',
 	 *				name: '$text'
 	 *				name: '$text'
 	 *			},
 	 *			},
-	 *			view: ( modelAttributeValue, viewWriter ) => {
-	 *				return viewWriter.createAttributeElement( 'span', {
+	 *			view: ( modelAttributeValue, conversionApi ) => {
+	 *				const { writer } = conversionApi;
+	 *
+	 *				return writer.createAttributeElement( 'span', {
 	 *					style: 'color:' + modelAttributeValue
 	 *					style: 'color:' + modelAttributeValue
 	 *				} );
 	 *				} );
 	 *			}
 	 *			}
@@ -147,9 +153,10 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values }` object. `values` is an array
 	 * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values }` object. `values` is an array
 	 * of `String`s with possible values if the model attribute is an enumerable.
 	 * of `String`s with possible values if the model attribute is an enumerable.
 	 * @param {module:engine/view/elementdefinition~ElementDefinition|Function|Object} config.view A view element definition or a function
 	 * @param {module:engine/view/elementdefinition~ElementDefinition|Function|Object} config.view A view element definition or a function
-	 * that takes the model attribute value and {@link module:engine/view/downcastwriter~DowncastWriter view downcast writer}
-	 * as parameters and returns a view attribute element. If `config.model.values` is
-	 * given, `config.view` should be an object assigning values from `config.model.values` to view element definitions or functions.
+	 * that takes the model attribute value and
+	 * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as parameters and returns a view
+	 * attribute element. If `config.model.values` is given, `config.view` should be an object assigning values from `config.model.values`
+	 * to view element definitions or functions.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 */
 	 */
@@ -201,7 +208,10 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *
 	 *
 	 *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
 	 *		editor.conversion.for( 'downcast' ).attributeToAttribute( {
 	 *			model: 'styled',
 	 *			model: 'styled',
-	 *			view: modelAttributeValue => ( { key: 'class', value: 'styled-' + modelAttributeValue } )
+	 *			view: modelAttributeValue => ( {
+	 *				key: 'class',
+	 *				value: 'styled-' + modelAttributeValue
+	 *			} )
 	 *		} );
 	 *		} );
 	 *
 	 *
 	 * **Note**: Downcasting to a style property requires providing `value` as an object:
 	 * **Note**: Downcasting to a style property requires providing `value` as an object:
@@ -225,7 +235,8 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values, [ name ] }` object describing
 	 * @param {String|Object} config.model The key of the attribute to convert from or a `{ key, values, [ name ] }` object describing
 	 * the attribute key, possible values and, optionally, an element name to convert from.
 	 * the attribute key, possible values and, optionally, an element name to convert from.
 	 * @param {String|Object|Function} config.view A view attribute key, or a `{ key, value }` object or a function that takes
 	 * @param {String|Object|Function} config.view A view attribute key, or a `{ key, value }` object or a function that takes
-	 * the model attribute value and returns a `{ key, value }` object. If `key` is `'class'`, `value` can be a `String` or an
+	 * the model attribute value and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
+	 * as parameters and returns a `{ key, value }` object. If `key` is `'class'`, `value` can be a `String` or an
 	 * array of `String`s. If `key` is `'style'`, `value` is an object with key-value pairs. In other cases, `value` is a `String`.
 	 * array of `String`s. If `key` is `'style'`, `value` is an object with key-value pairs. In other cases, `value` is a `String`.
 	 * If `config.model.values` is set, `config.view` should be an object assigning values from `config.model.values` to
 	 * If `config.model.values` is set, `config.view` should be an object assigning values from `config.model.values` to
 	 * `{ key, value }` objects or a functions.
 	 * `{ key, value }` objects or a functions.
@@ -269,8 +280,10 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *
 	 *
 	 *		editor.conversion.for( 'editingDowncast' ).markerToElement( {
 	 *		editor.conversion.for( 'editingDowncast' ).markerToElement( {
 	 *			model: 'search',
 	 *			model: 'search',
-	 *			view: ( markerData, viewWriter ) => {
-	 *				return viewWriter.createUIElement( 'span', {
+	 *			view: ( markerData, conversionApi ) => {
+	 *				const { writer } = conversionApi;
+	 *
+	 *				return writer.createUIElement( 'span', {
 	 *					'data-marker': 'search',
 	 *					'data-marker': 'search',
 	 *					'data-start': markerData.isOpening
 	 *					'data-start': markerData.isOpening
 	 *				} );
 	 *				} );
@@ -278,7 +291,8 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *		} );
 	 *		} );
 	 *
 	 *
 	 * If a function is passed as the `config.view` parameter, it will be used to generate both boundary elements. The function
 	 * If a function is passed as the `config.view` parameter, it will be used to generate both boundary elements. The function
-	 * receives the `data` object as a parameter and should return an instance of the
+	 * receives the `data` object and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
+	 * as a parameters and should return an instance of the
 	 * {@link module:engine/view/uielement~UIElement view UI element}. The `data` object and
 	 * {@link module:engine/view/uielement~UIElement view UI element}. The `data` object and
 	 * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi `conversionApi`} are passed from
 	 * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi `conversionApi`} are passed from
 	 * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}. Additionally,
 	 * {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}. Additionally,
@@ -291,8 +305,9 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 * @method #markerToElement
 	 * @method #markerToElement
 	 * @param {Object} config Conversion configuration.
 	 * @param {Object} config Conversion configuration.
 	 * @param {String} config.model The name of the model marker (or model marker group) to convert.
 	 * @param {String} config.model The name of the model marker (or model marker group) to convert.
-	 * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function
-	 * that takes the model marker data as a parameter and returns a view UI element.
+	 * @param {module:engine/view/elementdefinition~ElementDefinition|Function} config.view A view element definition or a function that
+	 * takes the model marker data and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
+	 * as a parameters and returns a view UI element.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 */
 	 */
@@ -329,7 +344,7 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *
 	 *
 	 *		editor.conversion.for( 'downcast' ).markerToHighlight( {
 	 *		editor.conversion.for( 'downcast' ).markerToHighlight( {
 	 *			model: 'comment',
 	 *			model: 'comment',
-	 *			view: data => {
+	 *			view: ( data, converstionApi ) => {
 	 *				// Assuming that the marker name is in a form of comment:commentType.
 	 *				// Assuming that the marker name is in a form of comment:commentType.
 	 *				const commentType = data.markerName.split( ':' )[ 1 ];
 	 *				const commentType = data.markerName.split( ':' )[ 1 ];
 	 *
 	 *
@@ -340,7 +355,8 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 *		} );
 	 *		} );
 	 *
 	 *
 	 * If a function is passed as the `config.view` parameter, it will be used to generate the highlight descriptor. The function
 	 * If a function is passed as the `config.view` parameter, it will be used to generate the highlight descriptor. The function
-	 * receives the `data` object as a parameter and should return a
+	 * receives the `data` object and {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API}
+	 * as a parameters and should return a
 	 * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor}.
 	 * {@link module:engine/conversion/downcasthelpers~HighlightDescriptor highlight descriptor}.
 	 * The `data` object properties are passed from {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}.
 	 * The `data` object properties are passed from {@link module:engine/conversion/downcastdispatcher~DowncastDispatcher#event:addMarker}.
 	 *
 	 *
@@ -351,7 +367,9 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 * @param {Object} config Conversion configuration.
 	 * @param {Object} config Conversion configuration.
 	 * @param {String} config.model The name of the model marker (or model marker group) to convert.
 	 * @param {String} config.model The name of the model marker (or model marker group) to convert.
 	 * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} config.view A highlight descriptor
 	 * @param {module:engine/conversion/downcasthelpers~HighlightDescriptor|Function} config.view A highlight descriptor
-	 * that will be used for highlighting or a function that takes the model marker data as a parameter and returns a highlight descriptor.
+	 * that will be used for highlighting or a function that takes the model marker data and
+	 * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as a parameters
+	 * and returns a highlight descriptor.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 */
 	 */
@@ -464,8 +482,9 @@ export default class DowncastHelpers extends ConversionHelpers {
 	 * @method #markerToData
 	 * @method #markerToData
 	 * @param {Object} config Conversion configuration.
 	 * @param {Object} config Conversion configuration.
 	 * @param {String} config.model The name of the model marker (or model marker group) to convert.
 	 * @param {String} config.model The name of the model marker (or model marker group) to convert.
-	 * @param {Function} [config.view] A function that takes the model marker name as a parameter and returns an object with the `group`
-	 * and `name` properties.
+	 * @param {Function} [config.view] A function that takes the model marker name and
+	 * {@link module:engine/conversion/downcastdispatcher~DowncastConversionApi downcast conversion API} as a parameters
+	 * and returns an object with the `group` and `name` properties.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 * @returns {module:engine/conversion/downcasthelpers~DowncastHelpers}
 	 */
 	 */
@@ -703,10 +722,10 @@ export function wrap( elementCreator ) {
 	return ( evt, data, conversionApi ) => {
 	return ( evt, data, conversionApi ) => {
 		// Recreate current wrapping node. It will be used to unwrap view range if the attribute value has changed
 		// Recreate current wrapping node. It will be used to unwrap view range if the attribute value has changed
 		// or the attribute was removed.
 		// or the attribute was removed.
-		const oldViewElement = elementCreator( data.attributeOldValue, conversionApi.writer );
+		const oldViewElement = elementCreator( data.attributeOldValue, conversionApi );
 
 
 		// Create node to wrap with.
 		// Create node to wrap with.
-		const newViewElement = elementCreator( data.attributeNewValue, conversionApi.writer );
+		const newViewElement = elementCreator( data.attributeNewValue, conversionApi );
 
 
 		if ( !oldViewElement && !newViewElement ) {
 		if ( !oldViewElement && !newViewElement ) {
 			return;
 			return;
@@ -766,7 +785,7 @@ export function wrap( elementCreator ) {
  */
  */
 export function insertElement( elementCreator ) {
 export function insertElement( elementCreator ) {
 	return ( evt, data, conversionApi ) => {
 	return ( evt, data, conversionApi ) => {
-		const viewElement = elementCreator( data.item, conversionApi.writer );
+		const viewElement = elementCreator( data.item, conversionApi );
 
 
 		if ( !viewElement ) {
 		if ( !viewElement ) {
 			return;
 			return;
@@ -803,10 +822,10 @@ export function insertUIElement( elementCreator ) {
 		// Create two view elements. One will be inserted at the beginning of marker, one at the end.
 		// Create two view elements. One will be inserted at the beginning of marker, one at the end.
 		// If marker is collapsed, only "opening" element will be inserted.
 		// If marker is collapsed, only "opening" element will be inserted.
 		data.isOpening = true;
 		data.isOpening = true;
-		const viewStartElement = elementCreator( data, conversionApi.writer );
+		const viewStartElement = elementCreator( data, conversionApi );
 
 
 		data.isOpening = false;
 		data.isOpening = false;
-		const viewEndElement = elementCreator( data, conversionApi.writer );
+		const viewEndElement = elementCreator( data, conversionApi );
 
 
 		if ( !viewStartElement || !viewEndElement ) {
 		if ( !viewStartElement || !viewEndElement ) {
 			return;
 			return;
@@ -880,7 +899,7 @@ function removeUIElement() {
 // @returns {Function} Add marker converter.
 // @returns {Function} Add marker converter.
 function insertMarkerData( viewCreator ) {
 function insertMarkerData( viewCreator ) {
 	return ( evt, data, conversionApi ) => {
 	return ( evt, data, conversionApi ) => {
-		const viewMarkerData = viewCreator( data.markerName );
+		const viewMarkerData = viewCreator( data.markerName, conversionApi );
 
 
 		if ( !viewMarkerData ) {
 		if ( !viewMarkerData ) {
 			return;
 			return;
@@ -961,7 +980,7 @@ function insertMarkerAsElement( position, isStart, conversionApi, data, viewMark
 // @returns {Function} Remove marker converter.
 // @returns {Function} Remove marker converter.
 function removeMarkerData( viewCreator ) {
 function removeMarkerData( viewCreator ) {
 	return ( evt, data, conversionApi ) => {
 	return ( evt, data, conversionApi ) => {
-		const viewData = viewCreator( data.markerName );
+		const viewData = viewCreator( data.markerName, conversionApi );
 
 
 		if ( !viewData ) {
 		if ( !viewData ) {
 			return;
 			return;
@@ -1036,8 +1055,8 @@ function removeMarkerData( viewCreator ) {
 // @returns {Function} Set/change attribute converter.
 // @returns {Function} Set/change attribute converter.
 function changeAttribute( attributeCreator ) {
 function changeAttribute( attributeCreator ) {
 	return ( evt, data, conversionApi ) => {
 	return ( evt, data, conversionApi ) => {
-		const oldAttribute = attributeCreator( data.attributeOldValue, data );
-		const newAttribute = attributeCreator( data.attributeNewValue, data );
+		const oldAttribute = attributeCreator( data.attributeOldValue, conversionApi );
+		const newAttribute = attributeCreator( data.attributeNewValue, conversionApi );
 
 
 		if ( !oldAttribute && !newAttribute ) {
 		if ( !oldAttribute && !newAttribute ) {
 			return;
 			return;
@@ -1487,7 +1506,7 @@ function normalizeToElementConfig( view, viewElementType ) {
 		return view;
 		return view;
 	}
 	}
 
 
-	return ( modelData, viewWriter ) => createViewElementFromDefinition( view, viewWriter, viewElementType );
+	return ( modelData, conversionApi ) => createViewElementFromDefinition( view, conversionApi, viewElementType );
 }
 }
 
 
 // Creates a view element instance from the provided {@link module:engine/view/elementdefinition~ElementDefinition} and class.
 // Creates a view element instance from the provided {@link module:engine/view/elementdefinition~ElementDefinition} and class.
@@ -1496,13 +1515,14 @@ function normalizeToElementConfig( view, viewElementType ) {
 // @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
 // @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
 // @param {'container'|'attribute'|'ui'} viewElementType
 // @param {'container'|'attribute'|'ui'} viewElementType
 // @returns {module:engine/view/element~Element}
 // @returns {module:engine/view/element~Element}
-function createViewElementFromDefinition( viewElementDefinition, viewWriter, viewElementType ) {
+function createViewElementFromDefinition( viewElementDefinition, conversionApi, viewElementType ) {
 	if ( typeof viewElementDefinition == 'string' ) {
 	if ( typeof viewElementDefinition == 'string' ) {
 		// If `viewElementDefinition` is given as a `String`, normalize it to an object with `name` property.
 		// If `viewElementDefinition` is given as a `String`, normalize it to an object with `name` property.
 		viewElementDefinition = { name: viewElementDefinition };
 		viewElementDefinition = { name: viewElementDefinition };
 	}
 	}
 
 
 	let element;
 	let element;
+	const viewWriter = conversionApi.writer;
 	const attributes = Object.assign( {}, viewElementDefinition.attributes );
 	const attributes = Object.assign( {}, viewElementDefinition.attributes );
 
 
 	if ( viewElementType == 'container' ) {
 	if ( viewElementType == 'container' ) {
@@ -1543,11 +1563,11 @@ function createViewElementFromDefinition( viewElementDefinition, viewWriter, vie
 
 
 function getFromAttributeCreator( config ) {
 function getFromAttributeCreator( config ) {
 	if ( config.model.values ) {
 	if ( config.model.values ) {
-		return ( modelAttributeValue, viewWriter ) => {
+		return ( modelAttributeValue, conversionApi ) => {
 			const view = config.view[ modelAttributeValue ];
 			const view = config.view[ modelAttributeValue ];
 
 
 			if ( view ) {
 			if ( view ) {
-				return view( modelAttributeValue, viewWriter );
+				return view( modelAttributeValue, conversionApi );
 			}
 			}
 
 
 			return null;
 			return null;

+ 22 - 11
packages/ckeditor5-engine/src/conversion/upcastdispatcher.js

@@ -11,6 +11,7 @@ import ViewConsumable from './viewconsumable';
 import ModelRange from '../model/range';
 import ModelRange from '../model/range';
 import ModelPosition from '../model/position';
 import ModelPosition from '../model/position';
 import { SchemaContext } from '../model/schema';
 import { SchemaContext } from '../model/schema';
+import { isParagraphable, wrapInParagraph } from '../model/utils/autoparagraphing';
 
 
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
 import EmitterMixin from '@ckeditor/ckeditor5-utils/src/emittermixin';
@@ -349,22 +350,32 @@ export default class UpcastDispatcher {
 	 * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#splitToAllowedParent
 	 * @see module:engine/conversion/upcastdispatcher~UpcastConversionApi#splitToAllowedParent
 	 */
 	 */
 	_splitToAllowedParent( node, modelCursor ) {
 	_splitToAllowedParent( node, modelCursor ) {
+		const { schema, writer } = this.conversionApi;
+
 		// Try to find allowed parent.
 		// Try to find allowed parent.
-		const allowedParent = this.conversionApi.schema.findAllowedParent( modelCursor, node );
+		let allowedParent = schema.findAllowedParent( modelCursor, node );
 
 
-		// When there is no parent that allows to insert node then return `null`.
-		if ( !allowedParent ) {
-			return null;
-		}
+		if ( allowedParent ) {
+			// When current position parent allows to insert node then return this position.
+			if ( allowedParent === modelCursor.parent ) {
+				return { position: modelCursor };
+			}
 
 
-		// When current position parent allows to insert node then return this position.
-		if ( allowedParent === modelCursor.parent ) {
-			return { position: modelCursor };
+			// When allowed parent is in context tree (it's outside the converted tree).
+			if ( this._modelCursor.parent.getAncestors().includes( allowedParent ) ) {
+				allowedParent = null;
+			}
 		}
 		}
 
 
-		// When allowed parent is in context tree.
-		if ( this._modelCursor.parent.getAncestors().includes( allowedParent ) ) {
-			return null;
+		if ( !allowedParent ) {
+			// Check if the node wrapped with a paragraph would be accepted by the schema.
+			if ( !isParagraphable( modelCursor, node, schema ) ) {
+				return null;
+			}
+
+			return {
+				position: wrapInParagraph( modelCursor, writer )
+			};
 		}
 		}
 
 
 		// Split element to allowed parent.
 		// Split element to allowed parent.

+ 56 - 36
packages/ckeditor5-engine/src/conversion/upcasthelpers.js

@@ -10,6 +10,7 @@ import { cloneDeep } from 'lodash-es';
 import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import { attachLinkToDocumentation } from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 
 import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
 import priorities from '@ckeditor/ckeditor5-utils/src/priorities';
+import { isParagraphable, wrapInParagraph } from '../model/utils/autoparagraphing';
 
 
 /* global console */
 /* global console */
 
 
@@ -59,7 +60,9 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 *				name: 'p',
 	 *				name: 'p',
 	 *				classes: 'heading'
 	 *				classes: 'heading'
 	 * 			},
 	 * 			},
-	 * 			model: ( viewElement, modelWriter ) => {
+	 * 			model: ( viewElement, conversionApi ) => {
+	 * 				const modelWriter = conversionApi.writer;
+	 *
 	 * 				return modelWriter.createElement( 'heading', { level: viewElement.getAttribute( 'data-level' ) } );
 	 * 				return modelWriter.createElement( 'heading', { level: viewElement.getAttribute( 'data-level' ) } );
 	 * 			}
 	 * 			}
 	 * 		} );
 	 * 		} );
@@ -71,8 +74,9 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 * @param {Object} config Conversion configuration.
 	 * @param {Object} config Conversion configuration.
 	 * @param {module:engine/view/matcher~MatcherPattern} [config.view] Pattern matching all view elements which should be converted. If not
 	 * @param {module:engine/view/matcher~MatcherPattern} [config.view] Pattern matching all view elements which should be converted. If not
 	 * set, the converter will fire for every view element.
 	 * set, the converter will fire for every view element.
-	 * @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element
-	 * instance or a function that takes a view element and returns a model element. The model element will be inserted in the model.
+	 * @param {String|module:engine/model/element~Element|Function} config.model Name of the model element, a model element instance or a
+	 * function that takes a view element and {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API}
+	 * and returns a model element. The model element will be inserted in the model.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
 	 */
 	 */
@@ -135,7 +139,7 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 *			},
 	 *			},
 	 *			model: {
 	 *			model: {
 	 *				key: 'fontSize',
 	 *				key: 'fontSize',
-	 *				value: viewElement => {
+	 *				value: ( viewElement, conversionApi ) => {
 	 *					const fontSize = viewElement.getStyle( 'font-size' );
 	 *					const fontSize = viewElement.getStyle( 'font-size' );
 	 *					const value = fontSize.substr( 0, fontSize.length - 2 );
 	 *					const value = fontSize.substr( 0, fontSize.length - 2 );
 	 *
 	 *
@@ -157,7 +161,8 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 * @param {Object} config Conversion configuration.
 	 * @param {Object} config Conversion configuration.
 	 * @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.
 	 * @param {module:engine/view/matcher~MatcherPattern} config.view Pattern matching all view elements which should be converted.
 	 * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
 	 * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
-	 * the model attribute. `value` property may be set as a function that takes a view element and returns the value.
+	 * the model attribute. `value` property may be set as a function that takes a view element and
+	 * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.
 	 * If `String` is given, the model attribute value will be set to `true`.
 	 * If `String` is given, the model attribute value will be set to `true`.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
@@ -231,7 +236,7 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 *			},
 	 *			},
 	 *			model: {
 	 *			model: {
 	 *				key: 'styled'
 	 *				key: 'styled'
-	 *				value: viewElement => {
+	 *				value: ( viewElement, conversionApi ) => {
 	 *					const regexp = /styled-([\S]+)/;
 	 *					const regexp = /styled-([\S]+)/;
 	 *					const match = viewElement.getAttribute( 'class' ).match( regexp );
 	 *					const match = viewElement.getAttribute( 'class' ).match( regexp );
 	 *
 	 *
@@ -263,7 +268,7 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 *			},
 	 *			},
 	 *			model: {
 	 *			model: {
 	 *				key: 'lineHeight',
 	 *				key: 'lineHeight',
-	 *				value: viewElement => viewElement.getStyle( 'line-height' )
+	 *				value: ( viewElement, conversionApi ) => viewElement.getStyle( 'line-height' )
 	 *			}
 	 *			}
 	 *		} );
 	 *		} );
 	 *
 	 *
@@ -278,7 +283,8 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 * property specifying a view element name from/on which the attribute should be converted. `value` can be given as a `String`,
 	 * property specifying a view element name from/on which the attribute should be converted. `value` can be given as a `String`,
 	 * a `RegExp` or a function callback, that takes view attribute value as the only parameter and returns `Boolean`.
 	 * a `RegExp` or a function callback, that takes view attribute value as the only parameter and returns `Boolean`.
 	 * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
 	 * @param {String|Object} config.model Model attribute key or an object with `key` and `value` properties, describing
-	 * the model attribute. `value` property may be set as a function that takes a view element and returns the value.
+	 * the model attribute. `value` property may be set as a function that takes a view element and
+	 * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the value.
 	 * If `String` is given, the model attribute value will be same as view attribute value.
 	 * If `String` is given, the model attribute value will be same as view attribute value.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='low'] Converter priority.
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
@@ -310,7 +316,7 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 *
 	 *
 	 *		editor.conversion.for( 'upcast' ).elementToMarker( {
 	 *		editor.conversion.for( 'upcast' ).elementToMarker( {
 	 *			view: 'marker-search',
 	 *			view: 'marker-search',
-	 *			model: viewElement => 'comment:' + viewElement.getAttribute( 'data-comment-id' )
+	 *			model: ( viewElement, conversionApi ) => 'comment:' + viewElement.getAttribute( 'data-comment-id' )
 	 *		} );
 	 *		} );
 	 *
 	 *
 	 *		editor.conversion.for( 'upcast' ).elementToMarker( {
 	 *		editor.conversion.for( 'upcast' ).elementToMarker( {
@@ -400,13 +406,13 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 *		// Using a custom function which is the same as the default conversion:
 	 *		// Using a custom function which is the same as the default conversion:
 	 *		editor.conversion.for( 'upcast' ).dataToMarker( {
 	 *		editor.conversion.for( 'upcast' ).dataToMarker( {
 	 *			view: 'comment',
 	 *			view: 'comment',
-	 *			model: name => 'comment:' + name,
+	 *			model: ( name, conversionApi ) => 'comment:' + name,
 	 *		} );
 	 *		} );
 	 *
 	 *
 	 *		// Using the converter priority:
 	 *		// Using the converter priority:
 	 *		editor.conversion.for( 'upcast' ).dataToMarker( {
 	 *		editor.conversion.for( 'upcast' ).dataToMarker( {
 	 *			view: 'comment',
 	 *			view: 'comment',
-	 *			model: name => 'comment:' + name,
+	 *			model: ( name, conversionApi ) => 'comment:' + name,
 	 *			converterPriority: 'high'
 	 *			converterPriority: 'high'
 	 *		} );
 	 *		} );
 	 *
 	 *
@@ -416,8 +422,8 @@ export default class UpcastHelpers extends ConversionHelpers {
 	 * @method #dataToMarker
 	 * @method #dataToMarker
 	 * @param {Object} config Conversion configuration.
 	 * @param {Object} config Conversion configuration.
 	 * @param {String} config.view The marker group name to convert.
 	 * @param {String} config.view The marker group name to convert.
-	 * @param {Function} [config.model] A function that takes the `name` part from the view element or attribute and returns the marker
-	 * name.
+	 * @param {Function} [config.model] A function that takes the `name` part from the view element or attribute and
+	 * {@link module:engine/conversion/upcastdispatcher~UpcastConversionApi upcast conversion API} and returns the marker name.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @param {module:utils/priorities~PriorityString} [config.converterPriority='normal'] Converter priority.
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
 	 * @returns {module:engine/conversion/upcasthelpers~UpcastHelpers}
 	 */
 	 */
@@ -459,20 +465,33 @@ export function convertToModelFragment() {
  * @returns {Function} {@link module:engine/view/text~Text View text} converter.
  * @returns {Function} {@link module:engine/view/text~Text View text} converter.
  */
  */
 export function convertText() {
 export function convertText() {
-	return ( evt, data, conversionApi ) => {
-		if ( conversionApi.schema.checkChild( data.modelCursor, '$text' ) ) {
-			if ( conversionApi.consumable.consume( data.viewItem ) ) {
-				const text = conversionApi.writer.createText( data.viewItem.data );
+	return ( evt, data, { schema, consumable, writer } ) => {
+		let position = data.modelCursor;
 
 
-				conversionApi.writer.insert( text, data.modelCursor );
+		// When node is already converted then do nothing.
+		if ( !consumable.test( data.viewItem ) ) {
+			return;
+		}
 
 
-				data.modelRange = conversionApi.writer.createRange(
-					data.modelCursor,
-					data.modelCursor.getShiftedBy( text.offsetSize )
-				);
-				data.modelCursor = data.modelRange.end;
+		if ( !schema.checkChild( position, '$text' ) ) {
+			if ( !isParagraphable( position, '$text', schema ) ) {
+				return;
 			}
 			}
+
+			position = wrapInParagraph( position, writer );
 		}
 		}
+
+		consumable.consume( data.viewItem );
+
+		const text = writer.createText( data.viewItem.data );
+
+		writer.insert( text, position );
+
+		data.modelRange = writer.createRange(
+			position,
+			position.getShiftedBy( text.offsetSize )
+		);
+		data.modelCursor = data.modelRange.end;
 	};
 	};
 }
 }
 
 
@@ -697,7 +716,7 @@ function upcastAttributeToMarker( config ) {
 
 
 		function addMarkerElements( position, markerViewNames ) {
 		function addMarkerElements( position, markerViewNames ) {
 			for ( const markerViewName of markerViewNames ) {
 			for ( const markerViewName of markerViewNames ) {
-				const markerName = config.model( markerViewName );
+				const markerName = config.model( markerViewName, conversionApi );
 				const element = conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );
 				const element = conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );
 
 
 				conversionApi.writer.insert( element, position );
 				conversionApi.writer.insert( element, position );
@@ -754,7 +773,7 @@ function prepareToElementConverter( config ) {
 			return;
 			return;
 		}
 		}
 
 
-		const modelElement = getModelElement( config.model, data.viewItem, conversionApi.writer );
+		const modelElement = getModelElement( config.model, data.viewItem, conversionApi );
 
 
 		if ( !modelElement ) {
 		if ( !modelElement ) {
 			return;
 			return;
@@ -775,12 +794,12 @@ function prepareToElementConverter( config ) {
 //
 //
 // @param {String|Function|module:engine/model/element~Element} model Model conversion configuration.
 // @param {String|Function|module:engine/model/element~Element} model Model conversion configuration.
 // @param {module:engine/view/node~Node} input The converted view node.
 // @param {module:engine/view/node~Node} input The converted view node.
-// @param {module:engine/model/writer~Writer} writer A writer instance to use to create the model element.
-function getModelElement( model, input, writer ) {
+// @param {module:engine/conversion/upcastdispatcher~UpcastConversionApi} conversionApi The upcast conversion API.
+function getModelElement( model, input, conversionApi ) {
 	if ( model instanceof Function ) {
 	if ( model instanceof Function ) {
-		return model( input, writer );
+		return model( input, conversionApi );
 	} else {
 	} else {
-		return writer.createElement( model );
+		return conversionApi.writer.createElement( model );
 	}
 	}
 }
 }
 
 
@@ -858,7 +877,8 @@ function prepareToAttributeConverter( config, shallow ) {
 		}
 		}
 
 
 		const modelKey = config.model.key;
 		const modelKey = config.model.key;
-		const modelValue = typeof config.model.value == 'function' ? config.model.value( data.viewItem ) : config.model.value;
+		const modelValue = typeof config.model.value == 'function' ?
+			config.model.value( data.viewItem, conversionApi ) : config.model.value;
 
 
 		// Do not convert if attribute building function returned falsy value.
 		// Do not convert if attribute building function returned falsy value.
 		if ( modelValue === null ) {
 		if ( modelValue === null ) {
@@ -939,10 +959,10 @@ function setAttributeOn( modelRange, modelAttribute, shallow, conversionApi ) {
 function normalizeElementToMarkerConfig( config ) {
 function normalizeElementToMarkerConfig( config ) {
 	const oldModel = config.model;
 	const oldModel = config.model;
 
 
-	config.model = ( viewElement, modelWriter ) => {
-		const markerName = typeof oldModel == 'string' ? oldModel : oldModel( viewElement );
+	config.model = ( viewElement, conversionApi ) => {
+		const markerName = typeof oldModel == 'string' ? oldModel : oldModel( viewElement, conversionApi );
 
 
-		return modelWriter.createElement( '$marker', { 'data-name': markerName } );
+		return conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );
 	};
 	};
 }
 }
 
 
@@ -956,11 +976,11 @@ function normalizeDataToMarkerConfig( config, type ) {
 	// Upcast <markerGroup-start> and <markerGroup-end> elements.
 	// Upcast <markerGroup-start> and <markerGroup-end> elements.
 	configForElements.view = config.view + '-' + type;
 	configForElements.view = config.view + '-' + type;
 
 
-	configForElements.model = ( viewElement, modelWriter ) => {
+	configForElements.model = ( viewElement, conversionApi ) => {
 		const viewName = viewElement.getAttribute( 'name' );
 		const viewName = viewElement.getAttribute( 'name' );
-		const markerName = config.model( viewName );
+		const markerName = config.model( viewName, conversionApi );
 
 
-		return modelWriter.createElement( '$marker', { 'data-name': markerName } );
+		return conversionApi.writer.createElement( '$marker', { 'data-name': markerName } );
 	};
 	};
 
 
 	return configForElements;
 	return configForElements;

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

@@ -229,8 +229,8 @@ export function stringify( node, selectionOrPositionOrRange = null, markers = nu
 	downcastDispatcher.on( 'insert:$text', insertText() );
 	downcastDispatcher.on( 'insert:$text', insertText() );
 	downcastDispatcher.on( 'attribute', ( evt, data, conversionApi ) => {
 	downcastDispatcher.on( 'attribute', ( evt, data, conversionApi ) => {
 		if ( data.item instanceof ModelSelection || data.item instanceof DocumentSelection || data.item.is( '$textProxy' ) ) {
 		if ( data.item instanceof ModelSelection || data.item instanceof DocumentSelection || data.item.is( '$textProxy' ) ) {
-			const converter = wrap( ( modelAttributeValue, viewWriter ) => {
-				return viewWriter.createAttributeElement(
+			const converter = wrap( ( modelAttributeValue, { writer } ) => {
+				return writer.createAttributeElement(
 					'model-text-with-attributes',
 					'model-text-with-attributes',
 					{ [ data.attributeKey ]: stringifyAttributeValue( modelAttributeValue ) }
 					{ [ data.attributeKey ]: stringifyAttributeValue( modelAttributeValue ) }
 				);
 				);
@@ -248,7 +248,7 @@ export function stringify( node, selectionOrPositionOrRange = null, markers = nu
 
 
 	downcastDispatcher.on( 'selection', convertRangeSelection() );
 	downcastDispatcher.on( 'selection', convertRangeSelection() );
 	downcastDispatcher.on( 'selection', convertCollapsedSelection() );
 	downcastDispatcher.on( 'selection', convertCollapsedSelection() );
-	downcastDispatcher.on( 'addMarker', insertUIElement( ( data, writer ) => {
+	downcastDispatcher.on( 'addMarker', insertUIElement( ( data, { writer } ) => {
 		const name = data.markerName + ':' + ( data.isOpening ? 'start' : 'end' );
 		const name = data.markerName + ':' + ( data.isOpening ? 'start' : 'end' );
 
 
 		return writer.createUIElement( name );
 		return writer.createUIElement( name );

+ 15 - 8
packages/ckeditor5-engine/src/model/model.js

@@ -25,6 +25,7 @@ import deleteContent from './utils/deletecontent';
 import modifySelection from './utils/modifyselection';
 import modifySelection from './utils/modifyselection';
 import getSelectedContent from './utils/getselectedcontent';
 import getSelectedContent from './utils/getselectedcontent';
 import { injectSelectionPostFixer } from './utils/selection-post-fixer';
 import { injectSelectionPostFixer } from './utils/selection-post-fixer';
+import { autoParagraphEmptyRoots } from './utils/autoparagraphing';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 import CKEditorError from '@ckeditor/ckeditor5-utils/src/ckeditorerror';
 
 
 // @if CK_DEBUG_ENGINE // const { dumpTrees } = require( '../dev-utils/utils' );
 // @if CK_DEBUG_ENGINE // const { dumpTrees } = require( '../dev-utils/utils' );
@@ -100,7 +101,8 @@ export default class Model {
 		} );
 		} );
 		this.schema.register( '$text', {
 		this.schema.register( '$text', {
 			allowIn: '$block',
 			allowIn: '$block',
-			isInline: true
+			isInline: true,
+			isContent: true
 		} );
 		} );
 		this.schema.register( '$clipboardHolder', {
 		this.schema.register( '$clipboardHolder', {
 			allowContentOf: '$root',
 			allowContentOf: '$root',
@@ -121,6 +123,9 @@ export default class Model {
 
 
 		injectSelectionPostFixer( this );
 		injectSelectionPostFixer( this );
 
 
+		// Post-fixer which takes care of adding empty paragraph elements to the empty roots.
+		this.document.registerPostFixer( autoParagraphEmptyRoots );
+
 		// @if CK_DEBUG_ENGINE // this.on( 'applyOperation', () => {
 		// @if CK_DEBUG_ENGINE // this.on( 'applyOperation', () => {
 		// @if CK_DEBUG_ENGINE // 	dumpTrees( this.document, this.document.version );
 		// @if CK_DEBUG_ENGINE // 	dumpTrees( this.document, this.document.version );
 		// @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
 		// @if CK_DEBUG_ENGINE // }, { priority: 'lowest' } );
@@ -540,7 +545,7 @@ export default class Model {
 	 *
 	 *
 	 * * any text node (`options.ignoreWhitespaces` allows controlling whether this text node must also contain
 	 * * any text node (`options.ignoreWhitespaces` allows controlling whether this text node must also contain
 	 * any non-whitespace characters),
 	 * any non-whitespace characters),
-	 * * or any {@link module:engine/model/schema~Schema#isObject object element},
+	 * * or any {@link module:engine/model/schema~Schema#isContent content element},
 	 * * or any {@link module:engine/model/markercollection~Marker marker} which
 	 * * or any {@link module:engine/model/markercollection~Marker marker} which
 	 * {@link module:engine/model/markercollection~Marker#_affectsData affects data}.
 	 * {@link module:engine/model/markercollection~Marker#_affectsData affects data}.
 	 *
 	 *
@@ -573,14 +578,16 @@ export default class Model {
 		}
 		}
 
 
 		for ( const item of range.getItems() ) {
 		for ( const item of range.getItems() ) {
-			if ( item.is( '$textProxy' ) ) {
-				if ( !ignoreWhitespaces ) {
-					return true;
-				} else if ( item.data.search( /\S/ ) !== -1 ) {
+			if ( this.schema.isContent( item ) ) {
+				if ( item.is( '$textProxy' ) ) {
+					if ( !ignoreWhitespaces ) {
+						return true;
+					} else if ( item.data.search( /\S/ ) !== -1 ) {
+						return true;
+					}
+				} else {
 					return true;
 					return true;
 				}
 				}
-			} else if ( this.schema.isObject( item ) ) {
-				return true;
 			}
 			}
 		}
 		}
 
 

+ 81 - 2
packages/ckeditor5-engine/src/model/schema.js

@@ -282,7 +282,13 @@ export default class Schema {
 	isObject( item ) {
 	isObject( item ) {
 		const def = this.getDefinition( item );
 		const def = this.getDefinition( item );
 
 
-		return !!( def && def.isObject );
+		if ( !def ) {
+			return false;
+		}
+
+		// Note: Check out the implementation of #isLimit(), #isSelectable(), and #isContent()
+		// to understand why these three constitute an object.
+		return !!( def.isObject || ( def.isLimit && def.isSelectable && def.isContent ) );
 	}
 	}
 
 
 	/**
 	/**
@@ -292,7 +298,7 @@ export default class Schema {
 	 *		schema.isInline( 'paragraph' ); // -> false
 	 *		schema.isInline( 'paragraph' ); // -> false
 	 *		schema.isInline( 'softBreak' ); // -> true
 	 *		schema.isInline( 'softBreak' ); // -> true
 	 *
 	 *
-	 *		const text = writer.createText('foo' );
+	 *		const text = writer.createText( 'foo' );
 	 *		schema.isInline( text ); // -> true
 	 *		schema.isInline( text ); // -> true
 	 *
 	 *
 	 * See the {@glink framework/guides/deep-dive/schema#inline-elements Inline elements} section of the Schema deep dive
 	 * See the {@glink framework/guides/deep-dive/schema#inline-elements Inline elements} section of the Schema deep dive
@@ -306,6 +312,60 @@ export default class Schema {
 		return !!( def && def.isInline );
 		return !!( def && def.isInline );
 	}
 	}
 
 
+	/**
+	 * Returns `true` if the given item is defined to be
+	 * a selectable element by the {@link module:engine/model/schema~SchemaItemDefinition}'s `isSelectable` property.
+	 *
+	 *		schema.isSelectable( 'paragraph' ); // -> false
+	 *		schema.isSelectable( 'heading1' ); // -> false
+	 *		schema.isSelectable( 'image' ); // -> true
+	 *		schema.isSelectable( 'tableCell' ); // -> true
+	 *
+	 *		const text = writer.createText( 'foo' );
+	 *		schema.isSelectable( text ); // -> false
+	 *
+	 * See the {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements} section of the Schema deep dive}
+	 * guide for more details.
+	 *
+	 * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
+	 */
+	isSelectable( item ) {
+		const def = this.getDefinition( item );
+
+		if ( !def ) {
+			return false;
+		}
+
+		return !!( def.isSelectable || def.isObject );
+	}
+
+	/**
+	 * Returns `true` if the given item is defined to be
+	 * a content by the {@link module:engine/model/schema~SchemaItemDefinition}'s `isContent` property.
+	 *
+	 *		schema.isContent( 'paragraph' ); // -> false
+	 *		schema.isContent( 'heading1' ); // -> false
+	 *		schema.isContent( 'image' ); // -> true
+	 *		schema.isContent( 'horizontalLine' ); // -> true
+	 *
+	 *		const text = writer.createText( 'foo' );
+	 *		schema.isContent( text ); // -> true
+	 *
+	 * See the {@glink framework/guides/deep-dive/schema#content-elements Content elements} section of the Schema deep dive}
+	 * guide for more details.
+	 *
+	 * @param {module:engine/model/item~Item|module:engine/model/schema~SchemaContextItem|String} item
+	 */
+	isContent( item ) {
+		const def = this.getDefinition( item );
+
+		if ( !def ) {
+			return false;
+		}
+
+		return !!( def.isContent || def.isObject );
+	}
+
 	/**
 	/**
 	 * Checks whether the given node (`child`) can be a child of the given context.
 	 * Checks whether the given node (`child`) can be a child of the given context.
 	 *
 	 *
@@ -1185,6 +1245,25 @@ mix( Schema, ObservableMixin );
  *
  *
  * Read more about the object elements in the
  * Read more about the object elements in the
  * {@glink framework/guides/deep-dive/schema#object-elements Object elements} section of the Schema deep dive} guide.
  * {@glink framework/guides/deep-dive/schema#object-elements Object elements} section of the Schema deep dive} guide.
+ *
+ * @property {Boolean} isSelectable
+ * `true` when an element should be selectable as a whole by the user. Examples of selectable elements: `image`, `table`, `tableCell`, etc.
+ *
+ * **Note:** An object is also a selectable element, so
+ * {@link module:engine/model/schema~Schema#isSelectable `isSelectable()`} returns `true` for object elements automatically.
+ *
+ * Read more about selectable elements in the
+ * {@glink framework/guides/deep-dive/schema#selectable-elements Selectable elements} section of the Schema deep dive} guide.
+ *
+ * @property {Boolean} isContent
+ * An item is a content when it always finds its way to editor data output regardless of the number and type of its descendants.
+ * Examples of content elements: `$text`, `image`, `table`, etc. (but not `paragraph`, `heading1` or `tableCell`).
+ *
+ * **Note:** An object is also a content element, so
+ * {@link module:engine/model/schema~Schema#isContent `isContent()`} returns `true` for object elements automatically.
+ *
+ * Read more about content elements in the
+ * {@glink framework/guides/deep-dive/schema#content-elements Content elements} section of the Schema deep dive} guide.
  */
  */
 
 
 /**
 /**

+ 77 - 0
packages/ckeditor5-engine/src/model/utils/autoparagraphing.js

@@ -0,0 +1,77 @@
+/**
+ * @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
+ */
+
+/**
+ * @module engine/model/utils/autoparagraphing
+ */
+
+/**
+ * Fixes all empty roots.
+ *
+ * @protected
+ * @param {module:engine/model/writer~Writer} writer The model writer.
+ * @returns {Boolean} `true` if any change has been applied, `false` otherwise.
+ */
+export function autoParagraphEmptyRoots( writer ) {
+	const { schema, document } = writer.model;
+
+	for ( const rootName of document.getRootNames() ) {
+		const root = document.getRoot( rootName );
+
+		if ( root.isEmpty && !schema.checkChild( root, '$text' ) ) {
+			// If paragraph element is allowed in the root, create paragraph element.
+			if ( schema.checkChild( root, 'paragraph' ) ) {
+				writer.insertElement( 'paragraph', root );
+
+				// Other roots will get fixed in the next post-fixer round. Those will be triggered
+				// in the same batch no matter if this method was triggered by the post-fixing or not
+				// (the above insertElement call will trigger the post-fixers).
+				return true;
+			}
+		}
+	}
+
+	return false;
+}
+
+/**
+ * Checks if the given node wrapped with a paragraph would be accepted by the schema in the given position.
+ *
+ * @protected
+ * @param {module:engine/model/position~Position} position The position at which to check.
+ * @param {module:engine/model/node~Node|String} nodeOrType The child node or child type to check.
+ * @param {module:engine/model/schema~Schema} schema A schema instance used for element validation.
+ */
+export function isParagraphable( position, nodeOrType, schema ) {
+	const context = schema.createContext( position );
+
+	// When paragraph is allowed in this context...
+	if ( !schema.checkChild( context, 'paragraph' ) ) {
+		return false;
+	}
+
+	// And a node would be allowed in this paragraph...
+	if ( !schema.checkChild( context.push( 'paragraph' ), nodeOrType ) ) {
+		return false;
+	}
+
+	return true;
+}
+
+/**
+ * Inserts a new paragraph at the given position and returns a position inside that paragraph.
+ *
+ * @protected
+ * @param {module:engine/model/position~Position} position The position where a paragraph should be inserted.
+ * @param {module:engine/model/writer~Writer} writer The model writer.
+ * @returns {module:engine/model/position~Position} Position inside the created paragraph.
+ */
+export function wrapInParagraph( position, writer ) {
+	const paragraph = writer.createElement( 'paragraph' );
+
+	writer.insert( paragraph, position );
+
+	return writer.createPositionAt( paragraph, 0 );
+}

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

@@ -92,41 +92,44 @@ export default function modifySelection( model, selection, options = {} ) {
 // @param {{ walker, unit, isForward, schema }} data
 // @param {{ walker, unit, isForward, schema }} data
 // @param {module:engine/view/treewalker~TreeWalkerValue} value
 // @param {module:engine/view/treewalker~TreeWalkerValue} value
 function tryExtendingTo( data, value ) {
 function tryExtendingTo( data, value ) {
+	const { isForward, walker, unit, schema } = data;
+	const { type, item, nextPosition } = value;
+
 	// If found text, we can certainly put the focus in it. Let's just find a correct position
 	// If found text, we can certainly put the focus in it. Let's just find a correct position
 	// based on the unit.
 	// based on the unit.
-	if ( value.type == 'text' ) {
+	if ( type == 'text' ) {
 		if ( data.unit === 'word' ) {
 		if ( data.unit === 'word' ) {
-			return getCorrectWordBreakPosition( data.walker, data.isForward );
+			return getCorrectWordBreakPosition( walker, isForward );
 		}
 		}
 
 
-		return getCorrectPosition( data.walker, data.unit, data.isForward );
+		return getCorrectPosition( walker, unit, isForward );
 	}
 	}
 
 
 	// Entering an element.
 	// Entering an element.
-	if ( value.type == ( data.isForward ? 'elementStart' : 'elementEnd' ) ) {
-		// If it's an object, we can select it now.
-		if ( data.schema.isObject( value.item ) ) {
-			return Position._createAt( value.item, data.isForward ? 'after' : 'before' );
+	if ( type == ( isForward ? 'elementStart' : 'elementEnd' ) ) {
+		// If it's a selectable, we can select it now.
+		if ( schema.isSelectable( item ) ) {
+			return Position._createAt( item, isForward ? 'after' : 'before' );
 		}
 		}
 
 
 		// If text allowed on this position, extend to this place.
 		// If text allowed on this position, extend to this place.
-		if ( data.schema.checkChild( value.nextPosition, '$text' ) ) {
-			return value.nextPosition;
+		if ( schema.checkChild( nextPosition, '$text' ) ) {
+			return nextPosition;
 		}
 		}
 	}
 	}
 	// Leaving an element.
 	// Leaving an element.
 	else {
 	else {
 		// If leaving a limit element, stop.
 		// If leaving a limit element, stop.
-		if ( data.schema.isLimit( value.item ) ) {
+		if ( schema.isLimit( item ) ) {
 			// NOTE: Fast-forward the walker until the end.
 			// NOTE: Fast-forward the walker until the end.
-			data.walker.skip( () => true );
+			walker.skip( () => true );
 
 
 			return;
 			return;
 		}
 		}
 
 
 		// If text allowed on this position, extend to this place.
 		// If text allowed on this position, extend to this place.
-		if ( data.schema.checkChild( value.nextPosition, '$text' ) ) {
-			return value.nextPosition;
+		if ( schema.checkChild( nextPosition, '$text' ) ) {
+			return nextPosition;
 		}
 		}
 	}
 	}
 }
 }

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

@@ -22,7 +22,7 @@ import Position from '../position';
  * allows a `$text`.
  * allows a `$text`.
  * * None of the selection's non-collapsed ranges crosses a {@link module:engine/model/schema~Schema#isLimit limit element}
  * * None of the selection's non-collapsed ranges crosses a {@link module:engine/model/schema~Schema#isLimit limit element}
  * boundary (a range must be rooted within one limit element).
  * boundary (a range must be rooted within one limit element).
- * * Only {@link module:engine/model/schema~Schema#isObject object elements} can be selected from the outside
+ * * Only {@link module:engine/model/schema~Schema#isSelectable selectable elements} can be selected from the outside
  * (e.g. `[<paragraph>foo</paragraph>]` is invalid). This rule applies independently to both selection ends, so this
  * (e.g. `[<paragraph>foo</paragraph>]` is invalid). This rule applies independently to both selection ends, so this
  * selection is correct: `<paragraph>f[oo</paragraph><image></image>]`.
  * selection is correct: `<paragraph>f[oo</paragraph><image></image>]`.
  *
  *
@@ -154,8 +154,7 @@ function tryFixingCollapsedRange( range, schema ) {
 // @param {module:engine/model/schema~Schema} schema
 // @param {module:engine/model/schema~Schema} schema
 // @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.
 // @returns {module:engine/model/range~Range|null} Returns fixed range or null if range is valid.
 function tryFixingNonCollapsedRage( range, schema ) {
 function tryFixingNonCollapsedRage( range, schema ) {
-	const start = range.start;
-	const end = range.end;
+	const { start, end } = range;
 
 
 	const isTextAllowedOnStart = schema.checkChild( start, '$text' );
 	const isTextAllowedOnStart = schema.checkChild( start, '$text' );
 	const isTextAllowedOnEnd = schema.checkChild( end, '$text' );
 	const isTextAllowedOnEnd = schema.checkChild( end, '$text' );
@@ -176,13 +175,13 @@ function tryFixingNonCollapsedRage( range, schema ) {
 		// - [<block>foo</block>]    ->    <block>[foo]</block>
 		// - [<block>foo</block>]    ->    <block>[foo]</block>
 		// - [<block>foo]</block>    ->    <block>[foo]</block>
 		// - [<block>foo]</block>    ->    <block>[foo]</block>
 		// - <block>f[oo</block>]    ->    <block>f[oo]</block>
 		// - <block>f[oo</block>]    ->    <block>f[oo]</block>
-		// - [<block>foo</block><object></object>]    ->    <block>[foo</block><object></object>]
+		// - [<block>foo</block><selectable></selectable>]    ->    <block>[foo</block><selectable></selectable>]
 		if ( checkSelectionOnNonLimitElements( start, end, schema ) ) {
 		if ( checkSelectionOnNonLimitElements( start, end, schema ) ) {
-			const isStartObject = start.nodeAfter && schema.isObject( start.nodeAfter );
-			const fixedStart = isStartObject ? null : schema.getNearestSelectionRange( start, 'forward' );
+			const isStartBeforeSelectable = start.nodeAfter && schema.isSelectable( start.nodeAfter );
+			const fixedStart = isStartBeforeSelectable ? null : schema.getNearestSelectionRange( start, 'forward' );
 
 
-			const isEndObject = end.nodeBefore && schema.isObject( end.nodeBefore );
-			const fixedEnd = isEndObject ? null : schema.getNearestSelectionRange( end, 'backward' );
+			const isEndAfterSelectable = end.nodeBefore && schema.isSelectable( end.nodeBefore );
+			const fixedEnd = isEndAfterSelectable ? null : schema.getNearestSelectionRange( end, 'backward' );
 
 
 			// The schema.getNearestSelectionRange might return null - if that happens use original position.
 			// The schema.getNearestSelectionRange might return null - if that happens use original position.
 			const rangeStart = fixedStart ? fixedStart.start : start;
 			const rangeStart = fixedStart ? fixedStart.start : start;
@@ -200,8 +199,8 @@ function tryFixingNonCollapsedRage( range, schema ) {
 	if ( isStartInLimit || isEndInLimit ) {
 	if ( isStartInLimit || isEndInLimit ) {
 		const bothInSameParent = ( start.nodeAfter && end.nodeBefore ) && start.nodeAfter.parent === end.nodeBefore.parent;
 		const bothInSameParent = ( start.nodeAfter && end.nodeBefore ) && start.nodeAfter.parent === end.nodeBefore.parent;
 
 
-		const expandStart = isStartInLimit && ( !bothInSameParent || !isInObject( start.nodeAfter, schema ) );
-		const expandEnd = isEndInLimit && ( !bothInSameParent || !isInObject( end.nodeBefore, schema ) );
+		const expandStart = isStartInLimit && ( !bothInSameParent || !isSelectable( start.nodeAfter, schema ) );
+		const expandEnd = isEndInLimit && ( !bothInSameParent || !isSelectable( end.nodeBefore, schema ) );
 
 
 		// Although we've already found limit element on start/end positions we must find the outer-most limit element.
 		// Although we've already found limit element on start/end positions we must find the outer-most limit element.
 		// as limit elements might be nested directly inside (ie table > tableRow > tableCell).
 		// as limit elements might be nested directly inside (ie table > tableRow > tableCell).
@@ -285,11 +284,11 @@ function mergeIntersectingRanges( ranges ) {
 	return nonIntersectingRanges;
 	return nonIntersectingRanges;
 }
 }
 
 
-// Checks if node exists and if it's an object.
+// Checks if node exists and if it's a selectable.
 //
 //
 // @param {module:engine/model/node~Node} node
 // @param {module:engine/model/node~Node} node
 // @param {module:engine/model/schema~Schema} schema
 // @param {module:engine/model/schema~Schema} schema
 // @returns {Boolean}
 // @returns {Boolean}
-function isInObject( node, schema ) {
-	return node && schema.isObject( node );
+function isSelectable( node, schema ) {
+	return node && schema.isSelectable( node );
 }
 }

+ 126 - 2
packages/ckeditor5-engine/tests/controller/datacontroller.js

@@ -155,8 +155,8 @@ describe( 'DataController', () => {
 
 
 			const viewFragment = new ViewDocumentFragment( viewDocument, [ parseView( 'foo' ) ] );
 			const viewFragment = new ViewDocumentFragment( viewDocument, [ parseView( 'foo' ) ] );
 
 
-			// Model fragment in root.
-			expect( stringify( data.toModel( viewFragment ) ) ).to.equal( '' );
+			// Model fragment in root (note that it is auto-paragraphed because $text is not allowed directly in $root).
+			expect( stringify( data.toModel( viewFragment ) ) ).to.equal( '<paragraph>foo</paragraph>' );
 
 
 			// Model fragment in inline root.
 			// Model fragment in inline root.
 			expect( stringify( data.toModel( viewFragment, [ 'inlineRoot' ] ) ) ).to.equal( 'foo' );
 			expect( stringify( data.toModel( viewFragment, [ 'inlineRoot' ] ) ) ).to.equal( 'foo' );
@@ -454,6 +454,82 @@ describe( 'DataController', () => {
 				data.get( { rootName: 'nonexistent' } );
 				data.get( { rootName: 'nonexistent' } );
 			}, /datacontroller-get-non-existent-root:/ );
 			}, /datacontroller-get-non-existent-root:/ );
 		} );
 		} );
+
+		it( 'should allow to provide additional options for retrieving data - insert conversion', () => {
+			schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+
+			data.downcastDispatcher.on( 'insert:paragraph', ( evt, data, conversionApi ) => {
+				conversionApi.consumable.consume( data.item, 'insert' );
+
+				const viewPosition = conversionApi.mapper.toViewPosition( data.range.start );
+				const viewElement = conversionApi.writer.createContainerElement( 'p', {
+					attribute: conversionApi.options.attributeValue
+				} );
+
+				conversionApi.mapper.bindElements( data.item, viewElement );
+				conversionApi.writer.insert( viewPosition, viewElement );
+			}, { priority: 'high' } );
+
+			setData( model, '<paragraph>foo</paragraph>' );
+
+			expect( data.get( { attributeValue: 'foo' } ) ).to.equal( '<p attribute="foo">foo</p>' );
+			expect( data.get( { attributeValue: 'bar' } ) ).to.equal( '<p attribute="bar">foo</p>' );
+		} );
+
+		it( 'should allow to provide additional options for retrieving data - attribute conversion', () => {
+			schema.register( 'paragraph', { inheritAllFrom: '$block', allowAttributes: [ 'foo' ] } );
+			downcastHelpers.elementToElement( { model: 'paragraph', view: 'p' } );
+
+			data.downcastDispatcher.on( 'attribute:foo', ( evt, data, conversionApi ) => {
+				if ( data.attributeNewValue === conversionApi.options.skipAttribute ) {
+					return;
+				}
+
+				const viewRange = conversionApi.mapper.toViewRange( data.range );
+				const viewElement = conversionApi.writer.createAttributeElement( data.attributeNewValue );
+
+				conversionApi.writer.wrap( viewRange, viewElement );
+			} );
+
+			setData( model, '<paragraph>f<$text foo="a">o</$text>ob<$text foo="b">a</$text>r</paragraph>' );
+
+			expect( data.get() ).to.equal( '<p>f<a>o</a>ob<b>a</b>r</p>' );
+			expect( data.get( { skipAttribute: 'a' } ) ).to.equal( '<p>foob<b>a</b>r</p>' );
+			expect( data.get( { skipAttribute: 'b' } ) ).to.equal( '<p>f<a>o</a>obar</p>' );
+		} );
+
+		it( 'should allow to provide additional options for retrieving data - addMarker conversion', () => {
+			schema.register( 'paragraph', { inheritAllFrom: '$block' } );
+			downcastHelpers.elementToElement( { model: 'paragraph', view: 'p' } );
+
+			data.downcastDispatcher.on( 'addMarker', ( evt, data, conversionApi ) => {
+				if ( conversionApi.options.skipMarker ) {
+					return;
+				}
+
+				const viewElement = conversionApi.writer.createAttributeElement( 'marker' );
+				const viewRange = conversionApi.mapper.toViewRange( data.markerRange );
+
+				conversionApi.writer.wrap( viewRange, viewElement );
+			} );
+
+			setData( model, '<paragraph>foo</paragraph>' );
+
+			const root = model.document.getRoot();
+
+			model.change( writer => {
+				const start = writer.createPositionFromPath( root, [ 0, 1 ] );
+				const end = writer.createPositionFromPath( root, [ 0, 2 ] );
+
+				writer.addMarker( 'marker', {
+					range: writer.createRange( start, end ),
+					usingOperation: false
+				} );
+			} );
+
+			expect( data.get( { skipMarker: false } ) ).to.equal( '<p>f<marker>o</marker>o</p>' );
+			expect( data.get( { skipMarker: true } ) ).to.equal( '<p>foo</p>' );
+		} );
 	} );
 	} );
 
 
 	describe( 'stringify()', () => {
 	describe( 'stringify()', () => {
@@ -478,6 +554,24 @@ describe( 'DataController', () => {
 
 
 			expect( data.stringify( modelDocumentFragment ) ).to.equal( '<p>foo</p><p>bar</p>' );
 			expect( data.stringify( modelDocumentFragment ) ).to.equal( '<p>foo</p><p>bar</p>' );
 		} );
 		} );
+
+		it( 'should allow to provide additional options to the conversion process', () => {
+			const spy = sinon.spy();
+
+			data.downcastDispatcher.on( 'insert:paragraph', ( evt, data, conversionApi ) => {
+				spy( conversionApi.options );
+			}, { priority: 'high' } );
+
+			const modelDocumentFragment = parseModel( '<paragraph>foo</paragraph><paragraph>bar</paragraph>', schema );
+
+			const options = { foo: 'bar' };
+
+			data.stringify( modelDocumentFragment );
+			expect( spy.lastCall.args[ 0 ] ).to.not.equal( options );
+
+			data.stringify( modelDocumentFragment, options );
+			expect( spy.lastCall.args[ 0 ] ).to.equal( options );
+		} );
 	} );
 	} );
 
 
 	describe( 'toView()', () => {
 	describe( 'toView()', () => {
@@ -590,6 +684,36 @@ describe( 'DataController', () => {
 			expect( mappedViewRange.end.nodeBefore ).to.equal( firstViewElement );
 			expect( mappedViewRange.end.nodeBefore ).to.equal( firstViewElement );
 			expect( mappedViewRange.end.nodeAfter ).to.equal( viewDocumentFragment.getChild( 1 ) );
 			expect( mappedViewRange.end.nodeAfter ).to.equal( viewDocumentFragment.getChild( 1 ) );
 		} );
 		} );
+
+		it( 'should allow to provide additional options to the conversion process', () => {
+			const root = model.document.getRoot();
+			const spy = sinon.spy();
+
+			data.downcastDispatcher.on( 'insert:paragraph', ( evt, data, conversionApi ) => {
+				spy( conversionApi.options );
+			}, { priority: 'high' } );
+
+			data.downcastDispatcher.on( 'addMarker:marker', ( evt, data, conversionApi ) => {
+				spy( conversionApi.options );
+			}, { priority: 'high' } );
+
+			setData( model, '<paragraph>foo</paragraph>' );
+
+			model.change( writer => {
+				writer.addMarker( 'marker', {
+					range: model.createRange( model.createPositionFromPath( root, [ 0, 1 ] ) ),
+					usingOperation: false
+				} );
+			} );
+
+			const options = { foo: 'bar' };
+
+			data.toView( root, options );
+
+			sinon.assert.calledTwice( spy );
+			expect( spy.firstCall.args[ 0 ] ).to.equal( options );
+			expect( spy.lastCall.args[ 0 ] ).to.equal( options );
+		} );
 	} );
 	} );
 
 
 	describe( 'destroy()', () => {
 	describe( 'destroy()', () => {

+ 40 - 64
packages/ckeditor5-engine/tests/conversion/downcasthelpers.js

@@ -104,7 +104,7 @@ describe( 'DowncastHelpers', () => {
 		it( 'config.view is a function', () => {
 		it( 'config.view is a function', () => {
 			downcastHelpers.elementToElement( {
 			downcastHelpers.elementToElement( {
 				model: 'heading',
 				model: 'heading',
-				view: ( modelElement, viewWriter ) => viewWriter.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) )
+				view: ( modelElement, { writer } ) => writer.createContainerElement( 'h' + modelElement.getAttribute( 'level' ) )
 			} );
 			} );
 
 
 			model.change( writer => {
 			model.change( writer => {
@@ -225,8 +225,8 @@ describe( 'DowncastHelpers', () => {
 		it( 'config.view is a function', () => {
 		it( 'config.view is a function', () => {
 			downcastHelpers.attributeToElement( {
 			downcastHelpers.attributeToElement( {
 				model: 'bold',
 				model: 'bold',
-				view: ( modelAttributeValue, viewWriter ) => {
-					return viewWriter.createAttributeElement( 'span', { style: 'font-weight:' + modelAttributeValue } );
+				view: ( modelAttributeValue, { writer } ) => {
+					return writer.createAttributeElement( 'span', { style: 'font-weight:' + modelAttributeValue } );
 				}
 				}
 			} );
 			} );
 
 
@@ -243,15 +243,15 @@ describe( 'DowncastHelpers', () => {
 					key: 'color',
 					key: 'color',
 					name: '$text'
 					name: '$text'
 				},
 				},
-				view: ( modelAttributeValue, viewWriter ) => {
-					return viewWriter.createAttributeElement( 'span', { style: 'color:' + modelAttributeValue } );
+				view: ( modelAttributeValue, { writer } ) => {
+					return writer.createAttributeElement( 'span', { style: 'color:' + modelAttributeValue } );
 				}
 				}
 			} );
 			} );
 
 
 			downcastHelpers.elementToElement( {
 			downcastHelpers.elementToElement( {
 				model: 'smiley',
 				model: 'smiley',
-				view: ( modelElement, viewWriter ) => {
-					return viewWriter.createEmptyElement( 'img', {
+				view: ( modelElement, { writer } ) => {
+					return writer.createEmptyElement( 'img', {
 						src: 'smile.jpg',
 						src: 'smile.jpg',
 						class: 'smiley'
 						class: 'smiley'
 					} );
 					} );
@@ -283,7 +283,7 @@ describe( 'DowncastHelpers', () => {
 
 
 			downcastHelpers.attributeToElement( {
 			downcastHelpers.attributeToElement( {
 				model: 'bold',
 				model: 'bold',
-				view: ( modelAttributeValue, viewWriter ) => viewWriter.createAttributeElement( 'b' )
+				view: ( modelAttributeValue, { writer } ) => writer.createAttributeElement( 'b' )
 			} );
 			} );
 
 
 			model.change( writer => {
 			model.change( writer => {
@@ -304,9 +304,9 @@ describe( 'DowncastHelpers', () => {
 
 
 			downcastHelpers.attributeToElement( {
 			downcastHelpers.attributeToElement( {
 				model: 'style',
 				model: 'style',
-				view: ( modelAttributeValue, viewWriter ) => {
+				view: ( modelAttributeValue, { writer } ) => {
 					if ( modelAttributeValue == 'bold' ) {
 					if ( modelAttributeValue == 'bold' ) {
-						return viewWriter.createAttributeElement( 'b' );
+						return writer.createAttributeElement( 'b' );
 					}
 					}
 				}
 				}
 			} );
 			} );
@@ -333,8 +333,8 @@ describe( 'DowncastHelpers', () => {
 
 
 			downcastHelpers.attributeToElement( {
 			downcastHelpers.attributeToElement( {
 				model: 'link',
 				model: 'link',
-				view: ( modelAttributeValue, viewWriter ) => {
-					return viewWriter.createAttributeElement( 'a', { href: modelAttributeValue } );
+				view: ( modelAttributeValue, { writer } ) => {
+					return writer.createAttributeElement( 'a', { href: modelAttributeValue } );
 				}
 				}
 			} );
 			} );
 
 
@@ -357,7 +357,7 @@ describe( 'DowncastHelpers', () => {
 
 
 			downcastHelpers.attributeToElement( {
 			downcastHelpers.attributeToElement( {
 				model: 'bold',
 				model: 'bold',
-				view: ( modelAttributeValue, viewWriter ) => viewWriter.createAttributeElement( 'b' )
+				view: ( modelAttributeValue, { writer } ) => writer.createAttributeElement( 'b' )
 			} );
 			} );
 
 
 			model.change( writer => {
 			model.change( writer => {
@@ -378,11 +378,11 @@ describe( 'DowncastHelpers', () => {
 
 
 			downcastHelpers.attributeToElement( {
 			downcastHelpers.attributeToElement( {
 				model: 'bold',
 				model: 'bold',
-				view: ( modelAttributeValue, viewWriter ) => viewWriter.createAttributeElement( 'b' )
+				view: ( modelAttributeValue, { writer } ) => writer.createAttributeElement( 'b' )
 			} );
 			} );
 			downcastHelpers.attributeToElement( {
 			downcastHelpers.attributeToElement( {
 				model: 'bold',
 				model: 'bold',
-				view: ( modelAttributeValue, viewWriter ) => viewWriter.createAttributeElement( 'strong' ),
+				view: ( modelAttributeValue, { writer } ) => writer.createAttributeElement( 'strong' ),
 				converterPriority: 'high'
 				converterPriority: 'high'
 			} );
 			} );
 
 
@@ -423,7 +423,7 @@ describe( 'DowncastHelpers', () => {
 			downcastHelpers.elementToElement( { model: 'image', view: 'img' } );
 			downcastHelpers.elementToElement( { model: 'image', view: 'img' } );
 			downcastHelpers.elementToElement( {
 			downcastHelpers.elementToElement( {
 				model: 'paragraph',
 				model: 'paragraph',
-				view: ( modelItem, viewWriter ) => viewWriter.createContainerElement( 'p' )
+				view: ( modelItem, { writer } ) => writer.createContainerElement( 'p' )
 			} );
 			} );
 
 
 			downcastHelpers.attributeToAttribute( {
 			downcastHelpers.attributeToAttribute( {
@@ -605,7 +605,12 @@ describe( 'DowncastHelpers', () => {
 		it( 'config.view is a function', () => {
 		it( 'config.view is a function', () => {
 			downcastHelpers.attributeToAttribute( {
 			downcastHelpers.attributeToAttribute( {
 				model: 'styled',
 				model: 'styled',
-				view: attributeValue => ( { key: 'class', value: 'styled-' + attributeValue } )
+				view: ( attributeValue, conversionApi ) => {
+					// To ensure conversion API is provided.
+					expect( conversionApi.writer ).to.instanceof( DowncastWriter );
+
+					return { key: 'class', value: 'styled-' + attributeValue };
+				}
 			} );
 			} );
 
 
 			model.change( writer => {
 			model.change( writer => {
@@ -677,41 +682,6 @@ describe( 'DowncastHelpers', () => {
 			expect( viewToString( viewRoot ) ).to.equal( '<div><p>foobar</p></div>' );
 			expect( viewToString( viewRoot ) ).to.equal( '<div><p>foobar</p></div>' );
 		} );
 		} );
 
 
-		it( 'should convert insert/change/remove with attribute generating function as a parameter', () => {
-			downcastHelpers.elementToElement( { model: 'div', view: 'div' } );
-			downcastHelpers.attributeToAttribute( {
-				model: 'theme',
-				view: ( value, data ) => {
-					if ( data.item instanceof ModelElement && data.item.childCount > 0 ) {
-						value += ' fix-content';
-					}
-
-					return { key: 'class', value };
-				}
-			} );
-
-			const modelParagraph = new ModelElement( 'paragraph', { theme: 'nice' }, new ModelText( 'foobar' ) );
-			const modelDiv = new ModelElement( 'div', { theme: 'nice' } );
-
-			model.change( writer => {
-				writer.insert( [ modelParagraph, modelDiv ], modelRootStart );
-			} );
-
-			expect( viewToString( viewRoot ) ).to.equal( '<div><p class="nice fix-content">foobar</p><div class="nice"></div></div>' );
-
-			model.change( writer => {
-				writer.setAttribute( 'theme', 'awesome', modelParagraph );
-			} );
-
-			expect( viewToString( viewRoot ) ).to.equal( '<div><p class="awesome fix-content">foobar</p><div class="nice"></div></div>' );
-
-			model.change( writer => {
-				writer.removeAttribute( 'theme', modelParagraph );
-			} );
-
-			expect( viewToString( viewRoot ) ).to.equal( '<div><p>foobar</p><div class="nice"></div></div>' );
-		} );
-
 		it( 'should be possible to override setAttribute', () => {
 		it( 'should be possible to override setAttribute', () => {
 			downcastHelpers.attributeToAttribute( {
 			downcastHelpers.attributeToAttribute( {
 				model: 'class',
 				model: 'class',
@@ -807,8 +777,8 @@ describe( 'DowncastHelpers', () => {
 		it( 'config.view is a function', () => {
 		it( 'config.view is a function', () => {
 			downcastHelpers.markerToElement( {
 			downcastHelpers.markerToElement( {
 				model: 'search',
 				model: 'search',
-				view: ( data, viewWriter ) => {
-					return viewWriter.createUIElement( 'span', { 'data-marker': 'search', 'data-start': data.isOpening } );
+				view: ( data, { writer } ) => {
+					return writer.createUIElement( 'span', { 'data-marker': 'search', 'data-start': data.isOpening } );
 				}
 				}
 			} );
 			} );
 
 
@@ -838,7 +808,7 @@ describe( 'DowncastHelpers', () => {
 			it( 'should insert and remove ui element', () => {
 			it( 'should insert and remove ui element', () => {
 				downcastHelpers.markerToElement( {
 				downcastHelpers.markerToElement( {
 					model: 'marker',
 					model: 'marker',
-					view: ( data, viewWriter ) => viewWriter.createUIElement( 'span', { 'class': 'marker' } )
+					view: ( data, { writer } ) => writer.createUIElement( 'span', { 'class': 'marker' } )
 				} );
 				} );
 
 
 				model.change( writer => {
 				model.change( writer => {
@@ -859,7 +829,7 @@ describe( 'DowncastHelpers', () => {
 
 
 				downcastHelpers.markerToElement( {
 				downcastHelpers.markerToElement( {
 					model: 'marker',
 					model: 'marker',
-					view: ( data, viewWriter ) => viewWriter.createUIElement( 'span', { 'class': 'marker' } )
+					view: ( data, { writer } ) => writer.createUIElement( 'span', { 'class': 'marker' } )
 				} );
 				} );
 
 
 				controller.downcastDispatcher.on( 'addMarker:marker', ( evt, data, conversionApi ) => {
 				controller.downcastDispatcher.on( 'addMarker:marker', ( evt, data, conversionApi ) => {
@@ -911,7 +881,7 @@ describe( 'DowncastHelpers', () => {
 			it( 'should insert and remove ui element - element as a creator', () => {
 			it( 'should insert and remove ui element - element as a creator', () => {
 				downcastHelpers.markerToElement( {
 				downcastHelpers.markerToElement( {
 					model: 'marker',
 					model: 'marker',
-					view: ( data, viewWriter ) => viewWriter.createUIElement( 'span', { 'class': 'marker' } )
+					view: ( data, { writer } ) => writer.createUIElement( 'span', { 'class': 'marker' } )
 				} );
 				} );
 
 
 				model.change( writer => {
 				model.change( writer => {
@@ -931,7 +901,7 @@ describe( 'DowncastHelpers', () => {
 			it( 'should insert and remove ui element - function as a creator', () => {
 			it( 'should insert and remove ui element - function as a creator', () => {
 				downcastHelpers.markerToElement( {
 				downcastHelpers.markerToElement( {
 					model: 'marker',
 					model: 'marker',
-					view: ( data, viewWriter ) => viewWriter.createUIElement( 'span', { 'class': data.markerName } )
+					view: ( data, { writer } ) => writer.createUIElement( 'span', { 'class': data.markerName } )
 				} );
 				} );
 
 
 				model.change( writer => {
 				model.change( writer => {
@@ -951,12 +921,12 @@ describe( 'DowncastHelpers', () => {
 			it( 'should insert and remove different opening and ending element', () => {
 			it( 'should insert and remove different opening and ending element', () => {
 				downcastHelpers.markerToElement( {
 				downcastHelpers.markerToElement( {
 					model: 'marker',
 					model: 'marker',
-					view: ( data, viewWriter ) => {
+					view: ( data, { writer } ) => {
 						if ( data.isOpening ) {
 						if ( data.isOpening ) {
-							return viewWriter.createUIElement( 'span', { 'class': data.markerName, 'data-start': true } );
+							return writer.createUIElement( 'span', { 'class': data.markerName, 'data-start': true } );
 						}
 						}
 
 
-						return viewWriter.createUIElement( 'span', { 'class': data.markerName, 'data-end': true } );
+						return writer.createUIElement( 'span', { 'class': data.markerName, 'data-end': true } );
 					}
 					}
 				} );
 				} );
 
 
@@ -980,7 +950,7 @@ describe( 'DowncastHelpers', () => {
 
 
 				downcastHelpers.markerToElement( {
 				downcastHelpers.markerToElement( {
 					model: 'marker',
 					model: 'marker',
-					view: ( data, viewWriter ) => viewWriter.createUIElement( 'span', { 'class': 'marker' } )
+					view: ( data, { writer } ) => writer.createUIElement( 'span', { 'class': 'marker' } )
 				} );
 				} );
 				controller.downcastDispatcher.on( 'addMarker:marker', ( evt, data, conversionApi ) => {
 				controller.downcastDispatcher.on( 'addMarker:marker', ( evt, data, conversionApi ) => {
 					conversionApi.consumable.consume( data.item, 'addMarker:marker' );
 					conversionApi.consumable.consume( data.item, 'addMarker:marker' );
@@ -1316,9 +1286,12 @@ describe( 'DowncastHelpers', () => {
 
 
 			downcastHelpers.markerToData( {
 			downcastHelpers.markerToData( {
 				model: 'group',
 				model: 'group',
-				view: markerName => {
+				view: ( markerName, conversionApi ) => {
 					const namePart = markerName.split( ':' )[ 1 ];
 					const namePart = markerName.split( ':' )[ 1 ];
 
 
+					// To ensure conversion API is provided.
+					expect( conversionApi.writer ).to.instanceof( DowncastWriter );
+
 					return {
 					return {
 						group: 'g',
 						group: 'g',
 						name: namePart + '_' + customData[ namePart ]
 						name: namePart + '_' + customData[ namePart ]
@@ -1510,9 +1483,12 @@ describe( 'DowncastHelpers', () => {
 		it( 'config.view is a function', () => {
 		it( 'config.view is a function', () => {
 			downcastHelpers.markerToHighlight( {
 			downcastHelpers.markerToHighlight( {
 				model: 'comment',
 				model: 'comment',
-				view: data => {
+				view: ( data, conversionApi ) => {
 					const commentType = data.markerName.split( ':' )[ 1 ];
 					const commentType = data.markerName.split( ':' )[ 1 ];
 
 
+					// To ensure conversion API is provided.
+					expect( conversionApi.writer ).to.instanceof( DowncastWriter );
+
 					return {
 					return {
 						classes: [ 'comment', 'comment-' + commentType ]
 						classes: [ 'comment', 'comment-' + commentType ]
 					};
 					};

+ 37 - 0
packages/ckeditor5-engine/tests/conversion/upcastdispatcher.js

@@ -605,6 +605,43 @@ describe( 'UpcastDispatcher', () => {
 				sinon.assert.calledOnce( spy );
 				sinon.assert.calledOnce( spy );
 			} );
 			} );
 
 
+			it(
+				'should auto-paragraph an element if it is not allowed at the insertion position' +
+				'but would be inserted if auto-paragraphed',
+				() => {
+					const spy = sinon.spy();
+
+					model.schema.register( 'section', {
+						allowIn: '$root'
+					} );
+					model.schema.register( 'span', {
+						allowIn: 'paragraph'
+					} );
+					model.schema.extend( 'paragraph', {
+						allowIn: 'section'
+					} );
+
+					dispatcher.on( 'documentFragment', ( evt, data, conversionApi ) => {
+						const section = conversionApi.writer.createElement( 'section' );
+						const position = ModelPosition._createAt( section, 0 );
+
+						const span = conversionApi.writer.createElement( 'span' );
+
+						const result = conversionApi.splitToAllowedParent( span, position );
+
+						expect( section.getChild( 0 ).name ).to.equal( 'paragraph' );
+						expect( result ).to.deep.equal( {
+							position: ModelPosition._createAt( section.getChild( 0 ), 0 )
+						} );
+
+						spy();
+					} );
+
+					model.change( writer => dispatcher.convert( new ViewDocumentFragment( viewDocument ), writer ) );
+					sinon.assert.calledOnce( spy );
+				}
+			);
+
 			it( 'should return null if element is not allowed in position and any of ancestors', () => {
 			it( 'should return null if element is not allowed in position and any of ancestors', () => {
 				const spy = sinon.spy();
 				const spy = sinon.spy();
 
 

+ 51 - 7
packages/ckeditor5-engine/tests/conversion/upcasthelpers.js

@@ -29,6 +29,7 @@ import Mapper from '../../src/conversion/mapper';
 import ViewSelection from '../../src/view/selection';
 import ViewSelection from '../../src/view/selection';
 import ViewRange from '../../src/view/range';
 import ViewRange from '../../src/view/range';
 import { StylesProcessor } from '../../src/view/stylesmap';
 import { StylesProcessor } from '../../src/view/stylesmap';
+import Writer from '../../src/model/writer';
 
 
 /* globals console */
 /* globals console */
 
 
@@ -108,8 +109,8 @@ describe( 'UpcastHelpers', () => {
 					name: 'p',
 					name: 'p',
 					classes: 'heading'
 					classes: 'heading'
 				},
 				},
-				model: ( viewElement, modelWriter ) => {
-					return modelWriter.createElement( 'heading', { level: viewElement.getAttribute( 'data-level' ) } );
+				model: ( viewElement, { writer } ) => {
+					return writer.createElement( 'heading', { level: viewElement.getAttribute( 'data-level' ) } );
 				}
 				}
 			} );
 			} );
 
 
@@ -250,10 +251,13 @@ describe( 'UpcastHelpers', () => {
 				},
 				},
 				model: {
 				model: {
 					key: 'fontSize',
 					key: 'fontSize',
-					value: viewElement => {
+					value: ( viewElement, conversionApi ) => {
 						const fontSize = viewElement.getStyle( 'font-size' );
 						const fontSize = viewElement.getStyle( 'font-size' );
 						const value = fontSize.substr( 0, fontSize.length - 2 );
 						const value = fontSize.substr( 0, fontSize.length - 2 );
 
 
+						// To ensure conversion API is provided.
+						expect( conversionApi.writer ).to.instanceof( Writer );
+
 						if ( value <= 10 ) {
 						if ( value <= 10 ) {
 							return 'small';
 							return 'small';
 						} else if ( value > 12 ) {
 						} else if ( value > 12 ) {
@@ -504,10 +508,13 @@ describe( 'UpcastHelpers', () => {
 				},
 				},
 				model: {
 				model: {
 					key: 'styled',
 					key: 'styled',
-					value: viewElement => {
+					value: ( viewElement, conversionApi ) => {
 						const regexp = /styled-([\S]+)/;
 						const regexp = /styled-([\S]+)/;
 						const match = viewElement.getAttribute( 'class' ).match( regexp );
 						const match = viewElement.getAttribute( 'class' ).match( regexp );
 
 
+						// To ensure conversion API is provided.
+						expect( conversionApi.writer ).to.instanceof( Writer );
+
 						return match[ 1 ];
 						return match[ 1 ];
 					}
 					}
 				}
 				}
@@ -660,7 +667,12 @@ describe( 'UpcastHelpers', () => {
 		it( 'config.model is a function', () => {
 		it( 'config.model is a function', () => {
 			upcastHelpers.elementToMarker( {
 			upcastHelpers.elementToMarker( {
 				view: 'comment',
 				view: 'comment',
-				model: viewElement => 'comment:' + viewElement.getAttribute( 'data-comment-id' )
+				model: ( viewElement, conversionApi ) => {
+					// To ensure conversion API is provided.
+					expect( conversionApi.writer ).to.instanceof( Writer );
+
+					return 'comment:' + viewElement.getAttribute( 'data-comment-id' );
+				}
 			} );
 			} );
 
 
 			const frag = new ViewDocumentFragment( viewDocument, [
 			const frag = new ViewDocumentFragment( viewDocument, [
@@ -837,7 +849,14 @@ describe( 'UpcastHelpers', () => {
 		} );
 		} );
 
 
 		it( 'conversion callback, mixed, multiple markers, name', () => {
 		it( 'conversion callback, mixed, multiple markers, name', () => {
-			upcastHelpers.dataToMarker( { view: 'g', model: name => 'group:' + name.split( '_' )[ 0 ] } );
+			upcastHelpers.dataToMarker( {
+				view: 'g',
+				model: ( name, conversionApi ) => {
+					// To ensure conversion API is provided.
+					expect( conversionApi.writer ).to.instanceof( Writer );
+
+					return 'group:' + name.split( '_' )[ 0 ];
+				} } );
 
 
 			expectResult(
 			expectResult(
 				viewParse(
 				viewParse(
@@ -949,7 +968,7 @@ describe( 'upcast-converters', () => {
 
 
 		it( 'should not convert text if it is wrong with schema', () => {
 		it( 'should not convert text if it is wrong with schema', () => {
 			schema.addChildCheck( ( ctx, childDef ) => {
 			schema.addChildCheck( ( ctx, childDef ) => {
-				if ( childDef.name == '$text' && ctx.endsWith( '$root' ) ) {
+				if ( ( childDef.name == '$text' || childDef.name == 'paragraph' ) && ctx.endsWith( '$root' ) ) {
 					return false;
 					return false;
 				}
 				}
 			} );
 			} );
@@ -969,6 +988,31 @@ describe( 'upcast-converters', () => {
 			expect( conversionResult.getChild( 0 ).data ).to.equal( 'foobar' );
 			expect( conversionResult.getChild( 0 ).data ).to.equal( 'foobar' );
 		} );
 		} );
 
 
+		it( 'should auto-paragraph a text if it is not allowed at the insertion position but would be inserted if auto-paragraphed', () => {
+			schema.addChildCheck( ( ctx, childDef ) => {
+				if ( childDef.name == '$text' && ctx.endsWith( '$root' ) ) {
+					return false;
+				}
+			} );
+
+			const viewText = new ViewText( viewDocument, 'foobar' );
+			dispatcher.on( 'text', convertText() );
+			let conversionResult = model.change( writer => dispatcher.convert( viewText, writer, context ) );
+
+			expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
+			expect( conversionResult.childCount ).to.equal( 1 );
+			expect( conversionResult.getChild( 0 ).name ).to.equal( 'paragraph' );
+			expect( conversionResult.getNodeByPath( [ 0, 0 ] ) ).to.be.instanceof( ModelText );
+			expect( conversionResult.getNodeByPath( [ 0, 0 ] ).data ).to.equal( 'foobar' );
+
+			conversionResult = model.change( writer => dispatcher.convert( viewText, writer, [ '$block' ] ) );
+
+			expect( conversionResult ).to.be.instanceof( ModelDocumentFragment );
+			expect( conversionResult.childCount ).to.equal( 1 );
+			expect( conversionResult.getChild( 0 ) ).to.be.instanceof( ModelText );
+			expect( conversionResult.getChild( 0 ).data ).to.equal( 'foobar' );
+		} );
+
 		it( 'should support unicode', () => {
 		it( 'should support unicode', () => {
 			const viewText = new ViewText( viewDocument, 'நிலைக்கு' );
 			const viewText = new ViewText( viewDocument, 'நிலைக்கு' );
 
 

+ 25 - 3
packages/ckeditor5-engine/tests/model/model.js

@@ -43,6 +43,7 @@ describe( 'Model', () => {
 
 
 		it( 'registers $text to the schema', () => {
 		it( 'registers $text to the schema', () => {
 			expect( schema.isRegistered( '$text' ) ).to.be.true;
 			expect( schema.isRegistered( '$text' ) ).to.be.true;
+			expect( schema.isContent( '$text' ) ).to.be.true;
 			expect( schema.checkChild( [ '$block' ], '$text' ) ).to.be.true;
 			expect( schema.checkChild( [ '$block' ], '$text' ) ).to.be.true;
 		} );
 		} );
 
 
@@ -535,6 +536,10 @@ describe( 'Model', () => {
 			schema.register( 'image', {
 			schema.register( 'image', {
 				isObject: true
 				isObject: true
 			} );
 			} );
+			schema.register( 'content', {
+				inheritAllFrom: '$block',
+				isContent: true
+			} );
 			schema.extend( 'image', { allowIn: 'div' } );
 			schema.extend( 'image', { allowIn: 'div' } );
 			schema.register( 'listItem', {
 			schema.register( 'listItem', {
 				inheritAllFrom: '$block'
 				inheritAllFrom: '$block'
@@ -544,15 +549,19 @@ describe( 'Model', () => {
 				model,
 				model,
 
 
 				'<div>' +
 				'<div>' +
-				'<paragraph></paragraph>' +
+					'<paragraph></paragraph>' +
 				'</div>' +
 				'</div>' +
 				'<paragraph>foo</paragraph>' +
 				'<paragraph>foo</paragraph>' +
 				'<div>' +
 				'<div>' +
-				'<image></image>' +
+					'<image></image>' +
 				'</div>' +
 				'</div>' +
 				'<listItem></listItem>' +
 				'<listItem></listItem>' +
 				'<listItem></listItem>' +
 				'<listItem></listItem>' +
-				'<listItem></listItem>'
+				'<listItem></listItem>' +
+				'<content>foo</content>' +
+				'<div>' +
+					'<content></content>' +
+				'</div>'
 			);
 			);
 
 
 			root = model.document.getRoot();
 			root = model.document.getRoot();
@@ -761,6 +770,19 @@ describe( 'Model', () => {
 			expect( model.hasContent( pEmpty, { ignoreMarkers: true } ) ).to.be.true;
 			expect( model.hasContent( pEmpty, { ignoreMarkers: true } ) ).to.be.true;
 			expect( model.hasContent( pEmpty, { ignoreMarkers: true, ignoreWhitespaces: true } ) ).to.be.false;
 			expect( model.hasContent( pEmpty, { ignoreMarkers: true, ignoreWhitespaces: true } ) ).to.be.false;
 		} );
 		} );
+
+		it( 'should return true for an item registered as a content (isContent=true, isObject=false) in the schema', () => {
+			const contentElement = root.getChild( 6 );
+
+			expect( model.hasContent( contentElement ) ).to.be.true;
+		} );
+
+		it( 'should return true if a range contains an item registered as a content (isContent=true, isObject=false) in the schema', () => {
+			// [<div><content></content></div>]
+			const range = new ModelRange( ModelPosition._createAt( root, 6 ), ModelPosition._createAt( root, 7 ) );
+
+			expect( model.hasContent( range ) ).to.be.true;
+		} );
 	} );
 	} );
 
 
 	describe( 'createPositionFromPath()', () => {
 	describe( 'createPositionFromPath()', () => {

+ 113 - 0
packages/ckeditor5-engine/tests/model/schema.js

@@ -401,6 +401,16 @@ describe( 'Schema', () => {
 			expect( schema.isObject( 'foo' ) ).to.be.true;
 			expect( schema.isObject( 'foo' ) ).to.be.true;
 		} );
 		} );
 
 
+		it( 'returns true if an item is a limit, selectable, and a content at once (but not explicitely an object)', () => {
+			schema.register( 'foo', {
+				isLimit: true,
+				isSelectable: true,
+				isContent: true
+			} );
+
+			expect( schema.isObject( 'foo' ) ).to.be.true;
+		} );
+
 		it( 'returns false if an item was registered as a limit (because not all limits are objects)', () => {
 		it( 'returns false if an item was registered as a limit (because not all limits are objects)', () => {
 			schema.register( 'foo', {
 			schema.register( 'foo', {
 				isLimit: true
 				isLimit: true
@@ -409,6 +419,39 @@ describe( 'Schema', () => {
 			expect( schema.isObject( 'foo' ) ).to.be.false;
 			expect( schema.isObject( 'foo' ) ).to.be.false;
 		} );
 		} );
 
 
+		it( 'returns false if an item is a limit and a selectable but not a content ' +
+			'(because an object must always find its way into data regardless of its children)',
+		() => {
+			schema.register( 'foo', {
+				isLimit: true,
+				isSelectable: true
+			} );
+
+			expect( schema.isObject( 'foo' ) ).to.be.false;
+		} );
+
+		it( 'returns false if an item is a limit and content but not a selectable ' +
+			'(because the user must always be able to select an object)',
+		() => {
+			schema.register( 'foo', {
+				isLimit: true,
+				isContent: true
+			} );
+
+			expect( schema.isObject( 'foo' ) ).to.be.false;
+		} );
+
+		it( 'returns false if an item is a selectable and a content but not a limit ' +
+			'(because an object should never be split or crossed by the selection)',
+		() => {
+			schema.register( 'foo', {
+				isSelectable: true,
+				isContent: true
+			} );
+
+			expect( schema.isObject( 'foo' ) ).to.be.false;
+		} );
+
 		it( 'returns false if an item was not registered as an object', () => {
 		it( 'returns false if an item was not registered as an object', () => {
 			schema.register( 'foo' );
 			schema.register( 'foo' );
 
 
@@ -462,6 +505,76 @@ describe( 'Schema', () => {
 		} );
 		} );
 	} );
 	} );
 
 
+	describe( 'isSelectable()', () => {
+		it( 'should return true if an item was registered as a selectable', () => {
+			schema.register( 'foo', {
+				isSelectable: true
+			} );
+
+			expect( schema.isSelectable( 'foo' ) ).to.be.true;
+		} );
+
+		it( 'should return true if an item was registered as an object (because all objects are selectables)', () => {
+			schema.register( 'foo', {
+				isObject: true
+			} );
+
+			expect( schema.isSelectable( 'foo' ) ).to.be.true;
+		} );
+
+		it( 'should return false if an item was not registered as an object or selectable', () => {
+			schema.register( 'foo' );
+
+			expect( schema.isSelectable( 'foo' ) ).to.be.false;
+		} );
+
+		it( 'should return false if an item was not registered at all', () => {
+			expect( schema.isSelectable( 'foo' ) ).to.be.false;
+		} );
+
+		it( 'uses getDefinition()\'s item to definition normalization', () => {
+			const stub = sinon.stub( schema, 'getDefinition' ).returns( { isSelectable: true } );
+
+			expect( schema.isSelectable( 'foo' ) ).to.be.true;
+			expect( stub.calledOnce ).to.be.true;
+		} );
+	} );
+
+	describe( 'isContent()', () => {
+		it( 'should return true if an item was registered as a content', () => {
+			schema.register( 'foo', {
+				isContent: true
+			} );
+
+			expect( schema.isContent( 'foo' ) ).to.be.true;
+		} );
+
+		it( 'should return true if an item was registered as an object (because all objects are content)', () => {
+			schema.register( 'foo', {
+				isObject: true
+			} );
+
+			expect( schema.isContent( 'foo' ) ).to.be.true;
+		} );
+
+		it( 'should return false if an item was not registered as an object or a content', () => {
+			schema.register( 'foo' );
+
+			expect( schema.isContent( 'foo' ) ).to.be.false;
+		} );
+
+		it( 'should return false if an item was not registered at all', () => {
+			expect( schema.isContent( 'foo' ) ).to.be.false;
+		} );
+
+		it( 'uses getDefinition()\'s item to definition normalization', () => {
+			const stub = sinon.stub( schema, 'getDefinition' ).returns( { isContent: true } );
+
+			expect( schema.isContent( 'foo' ) ).to.be.true;
+			expect( stub.calledOnce ).to.be.true;
+		} );
+	} );
+
 	describe( 'checkChild()', () => {
 	describe( 'checkChild()', () => {
 		beforeEach( () => {
 		beforeEach( () => {
 			schema.register( '$root' );
 			schema.register( '$root' );

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

@@ -973,7 +973,7 @@ describe( 'Selection', () => {
 
 
 			model.schema.register( 'table', { isBlock: true, isLimit: true, isObject: true, allowIn: '$root' } );
 			model.schema.register( 'table', { isBlock: true, isLimit: true, isObject: true, allowIn: '$root' } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
-			model.schema.register( 'tableCell', { allowIn: 'tableRow', isObject: true } );
+			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true, isSelectable: true } );
 
 
 			model.schema.extend( 'p', { allowIn: 'tableCell' } );
 			model.schema.extend( 'p', { allowIn: 'tableCell' } );
 		} );
 		} );

+ 8 - 3
packages/ckeditor5-engine/tests/model/utils/deletecontent.js

@@ -941,10 +941,15 @@ describe( 'DataController utils', () => {
 					{ rootName: 'bodyRoot' }
 					{ rootName: 'bodyRoot' }
 				);
 				);
 
 
-				deleteContent( model, doc.selection, { doNotAutoparagraph: true } );
+				// This must be tested inside a change block to check results before the post-fixers get triggered.
+				model.change( () => {
+					deleteContent( model, doc.selection, { doNotAutoparagraph: true } );
 
 
-				expect( getData( model, { rootName: 'bodyRoot' } ) )
-					.to.equal( '[]' );
+					expect( getData( model, { rootName: 'bodyRoot' } ) ).to.equal( '[]' );
+				} );
+
+				// Note that auto-paragraphing post-fixer injected a paragraph into the empty root.
+				expect( getData( model, { rootName: 'bodyRoot' } ) ).to.equal( '<paragraph>[]</paragraph>' );
 			} );
 			} );
 		} );
 		} );
 
 

+ 70 - 0
packages/ckeditor5-engine/tests/model/utils/modifyselection.js

@@ -917,6 +917,76 @@ describe( 'DataController utils', () => {
 			);
 			);
 		} );
 		} );
 
 
+		describe( 'selectable handling', () => {
+			beforeEach( () => {
+				model.schema.register( 'sel', {
+					isSelectable: true
+				} );
+				model.schema.extend( 'sel', { allowIn: '$root' } );
+				model.schema.extend( '$text', { allowIn: 'sel' } );
+
+				model.schema.register( 'inlineSel', {
+					allowIn: 'p',
+					isObject: true
+				} );
+				model.schema.extend( '$text', { allowIn: 'inlineSel' } );
+			} );
+
+			test(
+				'extends over next selectable element when at the end of an element',
+				'<p>foo[]</p><sel>bar</sel>',
+				'<p>foo[</p><sel>bar</sel>]'
+			);
+
+			test(
+				'extends over previous selectable element when at the beginning of an element ',
+				'<sel>bar</sel><p>[]foo</p>',
+				'[<sel>bar</sel><p>]foo</p>',
+				{ direction: 'backward' }
+			);
+
+			test(
+				'extends over selectable elements - forward',
+				'[<sel></sel>]<sel></sel>',
+				'[<sel></sel><sel></sel>]'
+			);
+
+			it( 'extends over selectable elements - backward', () => {
+				setData( model, '<sel></sel>[<sel></sel>]', { lastRangeBackward: true } );
+
+				modifySelection( model, doc.selection, { direction: 'backward' } );
+
+				expect( stringify( doc.getRoot(), doc.selection ) ).to.equal( '[<sel></sel><sel></sel>]' );
+				expect( doc.selection.isBackward ).to.true;
+			} );
+
+			test(
+				'extends over inline selectables - forward',
+				'<p>foo[]<inlineSel>bar</inlineSel></p>',
+				'<p>foo[<inlineSel>bar</inlineSel>]</p>'
+			);
+
+			test(
+				'extends over inline selectables - backward',
+				'<p><inlineSel>bar</inlineSel>[]foo</p>',
+				'<p>[<inlineSel>bar</inlineSel>]foo</p>',
+				{ direction: 'backward' }
+			);
+
+			test(
+				'extends over empty inline selectables - forward',
+				'<p>foo[]<inlineSel></inlineSel></p>',
+				'<p>foo[<inlineSel></inlineSel>]</p>'
+			);
+
+			test(
+				'extends over empty inline selectables - backward',
+				'<p><inlineSel></inlineSel>[]foo</p>',
+				'<p>[<inlineSel></inlineSel>]foo</p>',
+				{ direction: 'backward' }
+			);
+		} );
+
 		describe( 'limits handling', () => {
 		describe( 'limits handling', () => {
 			beforeEach( () => {
 			beforeEach( () => {
 				model.schema.register( 'inlineLimit', {
 				model.schema.register( 'inlineLimit', {

+ 30 - 4
packages/ckeditor5-engine/tests/model/utils/selection-post-fixer.js

@@ -41,7 +41,8 @@ describe( 'Selection post-fixer', () => {
 			model.schema.register( 'tableCell', {
 			model.schema.register( 'tableCell', {
 				allowIn: 'tableRow',
 				allowIn: 'tableRow',
 				allowAttributes: [ 'colspan', 'rowspan' ],
 				allowAttributes: [ 'colspan', 'rowspan' ],
-				isObject: true
+				isLimit: true,
+				isSelectable: true
 			} );
 			} );
 
 
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
@@ -74,7 +75,8 @@ describe( 'Selection post-fixer', () => {
 		it( 'should not crash if there is no correct position for model selection', () => {
 		it( 'should not crash if there is no correct position for model selection', () => {
 			setModelData( model, '' );
 			setModelData( model, '' );
 
 
-			expect( getModelData( model ) ).to.equal( '[]' );
+			// Note that auto-paragraphing post-fixer injected a paragraph into the empty root.
+			expect( getModelData( model ) ).to.equal( '<paragraph>[]</paragraph>' );
 		} );
 		} );
 
 
 		it( 'should react to structure changes', () => {
 		it( 'should react to structure changes', () => {
@@ -1205,6 +1207,30 @@ describe( 'Selection post-fixer', () => {
 
 
 				expect( getModelData( model ) ).to.equal( '<div>[<div></div>]</div>' );
 				expect( getModelData( model ) ).to.equal( '<div>[<div></div>]</div>' );
 			} );
 			} );
+
+			it( 'should not fix #5 (selection starts before a selectable, which is not an object)', () => {
+				model.schema.register( 'div', { allowIn: '$root' } );
+				model.schema.register( 'selectable', { isSelectable: true, allowIn: 'div' } );
+				model.schema.extend( '$text', { allowIn: 'selectable' } );
+
+				setModelData( model,
+					'<div>[<selectable>foo]</selectable></div>'
+				);
+
+				expect( getModelData( model ) ).to.equal( '<div>[<selectable>foo]</selectable></div>' );
+			} );
+
+			it( 'should not fix #6 (selection ends after before a selectable, which is not an object)', () => {
+				model.schema.register( 'div', { allowIn: '$root' } );
+				model.schema.register( 'selectable', { isSelectable: true, allowIn: 'div' } );
+				model.schema.extend( '$text', { allowIn: 'selectable' } );
+
+				setModelData( model,
+					'<div><selectable>[foo</selectable>]</div>'
+				);
+
+				expect( getModelData( model ) ).to.equal( '<div><selectable>[foo</selectable>]</div>' );
+			} );
 		} );
 		} );
 
 
 		describe( 'non-collapsed selection - inline widget scenarios', () => {
 		describe( 'non-collapsed selection - inline widget scenarios', () => {
@@ -1274,7 +1300,7 @@ describe( 'Selection post-fixer', () => {
 					'<paragraph>foo</paragraph>' +
 					'<paragraph>foo</paragraph>' +
 					'<table>' +
 					'<table>' +
 						'<tableRow>' +
 						'<tableRow>' +
-							'[<tableCell><paragraph>aaa</paragraph></tableCell>]' +
+							'<tableCell><paragraph>[]aaa</paragraph></tableCell>' +
 							'<tableCell><paragraph>bbb</paragraph></tableCell>' +
 							'<tableCell><paragraph>bbb</paragraph></tableCell>' +
 						'</tableRow>' +
 						'</tableRow>' +
 					'</table>' +
 					'</table>' +
@@ -1296,7 +1322,7 @@ describe( 'Selection post-fixer', () => {
 					'<paragraph>foo</paragraph>' +
 					'<paragraph>foo</paragraph>' +
 					'<table>' +
 					'<table>' +
 						'<tableRow>' +
 						'<tableRow>' +
-							'[<tableCell><paragraph>aaa</paragraph></tableCell>]' +
+							'<tableCell><paragraph>[]aaa</paragraph></tableCell>' +
 							'<tableCell><paragraph>bbb</paragraph></tableCell>' +
 							'<tableCell><paragraph>bbb</paragraph></tableCell>' +
 						'</tableRow>' +
 						'</tableRow>' +
 					'</table>' +
 					'</table>' +

+ 1 - 1
packages/ckeditor5-enter/src/shiftenter.js

@@ -51,7 +51,7 @@ export default class ShiftEnter extends Plugin {
 		conversion.for( 'downcast' )
 		conversion.for( 'downcast' )
 			.elementToElement( {
 			.elementToElement( {
 				model: 'softBreak',
 				model: 'softBreak',
-				view: ( modelElement, viewWriter ) => viewWriter.createEmptyElement( 'br' )
+				view: ( modelElement, { writer } ) => writer.createEmptyElement( 'br' )
 			} );
 			} );
 
 
 		view.addObserver( EnterObserver );
 		view.addObserver( EnterObserver );

+ 4 - 4
packages/ckeditor5-font/lang/translations/ko.po

@@ -38,7 +38,7 @@ msgstr "매우 큰"
 
 
 msgctxt "Tooltip for the font family dropdown."
 msgctxt "Tooltip for the font family dropdown."
 msgid "Font Family"
 msgid "Font Family"
-msgstr "글꼴"
+msgstr "글꼴 집합"
 
 
 msgctxt "Dropdown option label for the default font family."
 msgctxt "Dropdown option label for the default font family."
 msgid "Default"
 msgid "Default"
@@ -46,12 +46,12 @@ msgstr "기본"
 
 
 msgctxt "Label of a button that allows selecting a font color."
 msgctxt "Label of a button that allows selecting a font color."
 msgid "Font Color"
 msgid "Font Color"
-msgstr "글자 색"
+msgstr "글자 색"
 
 
 msgctxt "Label of a button that allows selecting a font background color."
 msgctxt "Label of a button that allows selecting a font background color."
 msgid "Font Background Color"
 msgid "Font Background Color"
-msgstr "글자 배경색"
+msgstr "글자 배경 "
 
 
 msgctxt "Title of a color picker section containing the colors currently used in the document."
 msgctxt "Title of a color picker section containing the colors currently used in the document."
 msgid "Document colors"
 msgid "Document colors"
-msgstr "문서 색들"
+msgstr "문서 색들"

+ 1 - 1
packages/ckeditor5-font/lang/translations/zh.po

@@ -54,4 +54,4 @@ msgstr "前景顏色"
 
 
 msgctxt "Title of a color picker section containing the colors currently used in the document."
 msgctxt "Title of a color picker section containing the colors currently used in the document."
 msgid "Document colors"
 msgid "Document colors"
-msgstr ""
+msgstr "文件顏色"

+ 1 - 1
packages/ckeditor5-font/src/fontfamily/fontfamilyediting.js

@@ -92,7 +92,7 @@ export default class FontFamilyEditing extends Plugin {
 
 
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 			model: FONT_FAMILY,
 			model: FONT_FAMILY,
-			view: ( attributeValue, writer ) => {
+			view: ( attributeValue, { writer } ) => {
 				return writer.createAttributeElement( 'span', { style: 'font-family:' + attributeValue }, { priority: 7 } );
 				return writer.createAttributeElement( 'span', { style: 'font-family:' + attributeValue }, { priority: 7 } );
 			}
 			}
 		} );
 		} );

+ 1 - 1
packages/ckeditor5-font/src/fontsize/fontsizeediting.js

@@ -117,7 +117,7 @@ export default class FontSizeEditing extends Plugin {
 
 
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 			model: FONT_SIZE,
 			model: FONT_SIZE,
-			view: ( attributeValue, writer ) => {
+			view: ( attributeValue, { writer } ) => {
 				if ( !attributeValue ) {
 				if ( !attributeValue ) {
 					return;
 					return;
 				}
 				}

+ 1 - 1
packages/ckeditor5-font/src/utils.js

@@ -82,7 +82,7 @@ export function renderUpcastAttribute( styleAttr ) {
  * @param {String} styleAttr
  * @param {String} styleAttr
  */
  */
 export function renderDowncastElement( styleAttr ) {
 export function renderDowncastElement( styleAttr ) {
-	return ( modelAttributeValue, viewWriter ) => viewWriter.createAttributeElement( 'span', {
+	return ( modelAttributeValue, { writer } ) => writer.createAttributeElement( 'span', {
 		style: `${ styleAttr }:${ modelAttributeValue }`
 		style: `${ styleAttr }:${ modelAttributeValue }`
 	}, { priority: 7 } );
 	}, { priority: 7 } );
 }
 }

+ 1 - 1
packages/ckeditor5-font/tests/utils.js

@@ -60,7 +60,7 @@ describe( 'utils', () => {
 			const fake = testUtils.sinon.fake();
 			const fake = testUtils.sinon.fake();
 			const fakeViewWriter = { createAttributeElement: fake };
 			const fakeViewWriter = { createAttributeElement: fake };
 
 
-			downcastViewConverterFn( 'blue', fakeViewWriter );
+			downcastViewConverterFn( 'blue', { writer: fakeViewWriter } );
 
 
 			sinon.assert.calledWithExactly( fake, 'span', { style: 'color:blue' }, { priority: 7 } );
 			sinon.assert.calledWithExactly( fake, 'span', { style: 'color:blue' }, { priority: 7 } );
 		} );
 		} );

+ 7 - 7
packages/ckeditor5-heading/lang/translations/ko.po

@@ -30,31 +30,31 @@ msgstr "제목 선택"
 
 
 msgctxt "Dropdown option label for the heading level 1 format."
 msgctxt "Dropdown option label for the heading level 1 format."
 msgid "Heading 1"
 msgid "Heading 1"
-msgstr "제목1"
+msgstr "제목 1"
 
 
 msgctxt "Dropdown option label for the heading level 2 format."
 msgctxt "Dropdown option label for the heading level 2 format."
 msgid "Heading 2"
 msgid "Heading 2"
-msgstr "제목2"
+msgstr "제목 2"
 
 
 msgctxt "Dropdown option label for the heading level 3 format."
 msgctxt "Dropdown option label for the heading level 3 format."
 msgid "Heading 3"
 msgid "Heading 3"
-msgstr "제목3"
+msgstr "제목 3"
 
 
 msgctxt "Dropdown option label for the heading level 4 format."
 msgctxt "Dropdown option label for the heading level 4 format."
 msgid "Heading 4"
 msgid "Heading 4"
-msgstr "제목4"
+msgstr "제목 4"
 
 
 msgctxt "Dropdown option label for the heading level 5 format."
 msgctxt "Dropdown option label for the heading level 5 format."
 msgid "Heading 5"
 msgid "Heading 5"
-msgstr "제목5"
+msgstr "제목 5"
 
 
 msgctxt "Dropdown option label for the heading level 6 format."
 msgctxt "Dropdown option label for the heading level 6 format."
 msgid "Heading 6"
 msgid "Heading 6"
-msgstr "제목6"
+msgstr "제목 6"
 
 
 msgctxt "A default value of the placeholder for the content title."
 msgctxt "A default value of the placeholder for the content title."
 msgid "Type your title"
 msgid "Type your title"
-msgstr "제목 입력"
+msgstr "제목 입력해주세요"
 
 
 msgctxt "A default value of the placeholder for the content body."
 msgctxt "A default value of the placeholder for the content body."
 msgid "Type or paste your content here."
 msgid "Type or paste your content here."

+ 2 - 2
packages/ckeditor5-heading/lang/translations/zh.po

@@ -54,8 +54,8 @@ msgstr "標題 6"
 
 
 msgctxt "A default value of the placeholder for the content title."
 msgctxt "A default value of the placeholder for the content title."
 msgid "Type your title"
 msgid "Type your title"
-msgstr ""
+msgstr "輸入你的標題"
 
 
 msgctxt "A default value of the placeholder for the content body."
 msgctxt "A default value of the placeholder for the content body."
 msgid "Type or paste your content here."
 msgid "Type or paste your content here."
-msgstr ""
+msgstr "在此輸入或貼上你的內容。"

+ 53 - 0
packages/ckeditor5-highlight/lang/translations/ko.po

@@ -0,0 +1,53 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Korean (https://www.transifex.com/ckeditor/teams/11143/ko/)\n"
+"Language: ko\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Toolbar button tooltip for applying yellow marker (text highlight)."
+msgid "Yellow marker"
+msgstr "노란색 마커"
+
+msgctxt "Toolbar button tooltip for applying green marker (text highlight)."
+msgid "Green marker"
+msgstr "초록색 마커"
+
+msgctxt "Toolbar button tooltip for applying pink marker (text highlight)."
+msgid "Pink marker"
+msgstr "분홍색 마커"
+
+msgctxt "Toolbar button tooltip for applying blue marker (text highlight)."
+msgid "Blue marker"
+msgstr "파란색 마커"
+
+msgctxt "Toolbar button tooltip for applying red pen (text color)."
+msgid "Red pen"
+msgstr "빨간색 펜"
+
+msgctxt "Toolbar button tooltip for applying green pen (text color)."
+msgid "Green pen"
+msgstr "초록색 펜"
+
+msgctxt "Toolbar button tooltip for removing text highlight."
+msgid "Remove highlight"
+msgstr "강조 제거"
+
+msgctxt "Toolbar button tooltip for the text highlight feature."
+msgid "Highlight"
+msgstr "강조"
+
+msgctxt "Label used by assistive technologies describing the highlight feature toolbar."
+msgid "Text highlight toolbar"
+msgstr "글자 강조 툴바"

+ 1 - 1
packages/ckeditor5-highlight/lang/translations/zh.po

@@ -50,4 +50,4 @@ msgstr "高亮"
 
 
 msgctxt "Label used by assistive technologies describing the highlight feature toolbar."
 msgctxt "Label used by assistive technologies describing the highlight feature toolbar."
 msgid "Text highlight toolbar"
 msgid "Text highlight toolbar"
-msgstr ""
+msgstr "高亮"

+ 21 - 0
packages/ckeditor5-horizontal-line/lang/translations/zh.po

@@ -0,0 +1,21 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Chinese (Taiwan) (https://www.transifex.com/ckeditor/teams/11143/zh_TW/)\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Horizontal line"
+msgid "Horizontal line"
+msgstr "水平線"

+ 9 - 9
packages/ckeditor5-horizontal-line/src/horizontallineediting.js

@@ -42,24 +42,24 @@ export default class HorizontalLineEditing extends Plugin {
 
 
 		conversion.for( 'dataDowncast' ).elementToElement( {
 		conversion.for( 'dataDowncast' ).elementToElement( {
 			model: 'horizontalLine',
 			model: 'horizontalLine',
-			view: ( modelElement, viewWriter ) => {
-				return viewWriter.createEmptyElement( 'hr' );
+			view: ( modelElement, { writer } ) => {
+				return writer.createEmptyElement( 'hr' );
 			}
 			}
 		} );
 		} );
 
 
 		conversion.for( 'editingDowncast' ).elementToElement( {
 		conversion.for( 'editingDowncast' ).elementToElement( {
 			model: 'horizontalLine',
 			model: 'horizontalLine',
-			view: ( modelElement, viewWriter ) => {
+			view: ( modelElement, { writer } ) => {
 				const label = t( 'Horizontal line' );
 				const label = t( 'Horizontal line' );
-				const viewWrapper = viewWriter.createContainerElement( 'div' );
-				const viewHrElement = viewWriter.createEmptyElement( 'hr' );
+				const viewWrapper = writer.createContainerElement( 'div' );
+				const viewHrElement = writer.createEmptyElement( 'hr' );
 
 
-				viewWriter.addClass( 'ck-horizontal-line', viewWrapper );
-				viewWriter.setCustomProperty( 'hr', true, viewWrapper );
+				writer.addClass( 'ck-horizontal-line', viewWrapper );
+				writer.setCustomProperty( 'hr', true, viewWrapper );
 
 
-				viewWriter.insert( viewWriter.createPositionAt( viewWrapper, 0 ), viewHrElement );
+				writer.insert( writer.createPositionAt( viewWrapper, 0 ), viewHrElement );
 
 
-				return toHorizontalLineWidget( viewWrapper, viewWriter, label );
+				return toHorizontalLineWidget( viewWrapper, writer, label );
 			}
 			}
 		} );
 		} );
 
 

+ 1 - 1
packages/ckeditor5-horizontal-line/tests/horizontallinecommand.js

@@ -84,7 +84,7 @@ describe( 'HorizontalLineCommand', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows horizontal line', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows horizontal line', () => {
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
-			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true } );
+			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true, isSelectable: true } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );

+ 7 - 7
packages/ckeditor5-image/lang/translations/de.po

@@ -42,11 +42,11 @@ msgstr "rechtsbündiges Bild"
 
 
 msgctxt "Label for the Change image text alternative button."
 msgctxt "Label for the Change image text alternative button."
 msgid "Change image text alternative"
 msgid "Change image text alternative"
-msgstr "Alternativ Text ändern"
+msgstr "Alternativtext ändern"
 
 
 msgctxt "Label for the image text alternative input."
 msgctxt "Label for the image text alternative input."
 msgid "Text alternative"
 msgid "Text alternative"
-msgstr "Textalternative"
+msgstr "Alternativtext"
 
 
 msgctxt "Placeholder text for image caption displayed when caption is empty."
 msgctxt "Placeholder text for image caption displayed when caption is empty."
 msgid "Enter image caption"
 msgid "Enter image caption"
@@ -66,20 +66,20 @@ msgstr "Bild Werkzeugleiste"
 
 
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgid "Resize image"
 msgid "Resize image"
-msgstr ""
+msgstr "Bildgröße ändern"
 
 
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgid "Resize image to %0"
 msgid "Resize image to %0"
-msgstr ""
+msgstr "Bildgröße ändern in %0"
 
 
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgid "Resize image to the original size"
 msgid "Resize image to the original size"
-msgstr ""
+msgstr "Bild in Originalgröße ändern"
 
 
 msgctxt "Default label for the resize option that resets the size of the image."
 msgctxt "Default label for the resize option that resets the size of the image."
 msgid "Original"
 msgid "Original"
-msgstr ""
+msgstr "Original"
 
 
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgid "Image resize list"
 msgid "Image resize list"
-msgstr ""
+msgstr "Bildgrößen-Liste"

+ 13 - 13
packages/ckeditor5-image/lang/translations/ko.po

@@ -18,15 +18,15 @@ msgstr ""
 
 
 msgctxt "Label for the image widget."
 msgctxt "Label for the image widget."
 msgid "image widget"
 msgid "image widget"
-msgstr "이미지 위젯"
+msgstr "사진 위젯"
 
 
 msgctxt "Label for the Side image option."
 msgctxt "Label for the Side image option."
 msgid "Side image"
 msgid "Side image"
-msgstr "내부 우측 정렬"
+msgstr "본문 옆에 배치"
 
 
 msgctxt "Label for the Full size image option."
 msgctxt "Label for the Full size image option."
 msgid "Full size image"
 msgid "Full size image"
-msgstr "문서 너비"
+msgstr "꽉 찬 크기"
 
 
 msgctxt "Label for the Left aligned image option"
 msgctxt "Label for the Left aligned image option"
 msgid "Left aligned image"
 msgid "Left aligned image"
@@ -42,19 +42,19 @@ msgstr "오른쪽 정렬"
 
 
 msgctxt "Label for the Change image text alternative button."
 msgctxt "Label for the Change image text alternative button."
 msgid "Change image text alternative"
 msgid "Change image text alternative"
-msgstr "대체 텍스트 변경"
+msgstr "대체 문구 변경"
 
 
 msgctxt "Label for the image text alternative input."
 msgctxt "Label for the image text alternative input."
 msgid "Text alternative"
 msgid "Text alternative"
-msgstr "대체 텍스트"
+msgstr "대체 문구"
 
 
 msgctxt "Placeholder text for image caption displayed when caption is empty."
 msgctxt "Placeholder text for image caption displayed when caption is empty."
 msgid "Enter image caption"
 msgid "Enter image caption"
-msgstr "이미지 설명을 입력하세요"
+msgstr "사진 설명을 입력하세요"
 
 
 msgctxt "Label for the insert image toolbar button."
 msgctxt "Label for the insert image toolbar button."
 msgid "Insert image"
 msgid "Insert image"
-msgstr "이미지 삽입"
+msgstr "사진 삽입"
 
 
 msgctxt "Title of the notification displayed when upload fails."
 msgctxt "Title of the notification displayed when upload fails."
 msgid "Upload failed"
 msgid "Upload failed"
@@ -62,24 +62,24 @@ msgstr "업로드 실패"
 
 
 msgctxt "The label used by assistive technologies describing an image toolbar attached to an image widget."
 msgctxt "The label used by assistive technologies describing an image toolbar attached to an image widget."
 msgid "Image toolbar"
 msgid "Image toolbar"
-msgstr "이미지 툴바"
+msgstr "사진 툴바"
 
 
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgid "Resize image"
 msgid "Resize image"
-msgstr ""
+msgstr "사진 크기 조절"
 
 
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgid "Resize image to %0"
 msgid "Resize image to %0"
-msgstr ""
+msgstr "사진의 크기를 %0으로 조절"
 
 
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgid "Resize image to the original size"
 msgid "Resize image to the original size"
-msgstr ""
+msgstr "사진을 원래 크기로 돌려놓기"
 
 
 msgctxt "Default label for the resize option that resets the size of the image."
 msgctxt "Default label for the resize option that resets the size of the image."
 msgid "Original"
 msgid "Original"
-msgstr ""
+msgstr "원본"
 
 
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgid "Image resize list"
 msgid "Image resize list"
-msgstr ""
+msgstr "사진 크기 목록"

+ 5 - 5
packages/ckeditor5-image/lang/translations/sr-latn.po

@@ -66,20 +66,20 @@ msgstr "Slika traka sa alatkama"
 
 
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgid "Resize image"
 msgid "Resize image"
-msgstr ""
+msgstr "Promenite veličinu slike"
 
 
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgid "Resize image to %0"
 msgid "Resize image to %0"
-msgstr ""
+msgstr "Promenite veličinu slike na% 0"
 
 
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgid "Resize image to the original size"
 msgid "Resize image to the original size"
-msgstr ""
+msgstr "Promenite veličinu slike do originalne veličine"
 
 
 msgctxt "Default label for the resize option that resets the size of the image."
 msgctxt "Default label for the resize option that resets the size of the image."
 msgid "Original"
 msgid "Original"
-msgstr ""
+msgstr "Original"
 
 
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgid "Image resize list"
 msgid "Image resize list"
-msgstr ""
+msgstr "Lista veličine slike"

+ 5 - 5
packages/ckeditor5-image/lang/translations/sr.po

@@ -66,20 +66,20 @@ msgstr "Слика трака са алтакама"
 
 
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgid "Resize image"
 msgid "Resize image"
-msgstr ""
+msgstr "Промените величину слике"
 
 
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgid "Resize image to %0"
 msgid "Resize image to %0"
-msgstr ""
+msgstr "Промените величину слике на% 0"
 
 
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgid "Resize image to the original size"
 msgid "Resize image to the original size"
-msgstr ""
+msgstr "Промените величину слике до оригиналне величине"
 
 
 msgctxt "Default label for the resize option that resets the size of the image."
 msgctxt "Default label for the resize option that resets the size of the image."
 msgid "Original"
 msgid "Original"
-msgstr ""
+msgstr "Оригинал"
 
 
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgid "Image resize list"
 msgid "Image resize list"
-msgstr ""
+msgstr "Листа величине слике"

+ 6 - 6
packages/ckeditor5-image/lang/translations/zh.po

@@ -62,24 +62,24 @@ msgstr "上傳失敗"
 
 
 msgctxt "The label used by assistive technologies describing an image toolbar attached to an image widget."
 msgctxt "The label used by assistive technologies describing an image toolbar attached to an image widget."
 msgid "Image toolbar"
 msgid "Image toolbar"
-msgstr ""
+msgstr "圖片工具"
 
 
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgctxt "The label used for the dropdown in the image toolbar containing defined resize options"
 msgid "Resize image"
 msgid "Resize image"
-msgstr ""
+msgstr "縮放圖片"
 
 
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgctxt "The label used for the standalone resize options buttons in the image toolbar"
 msgid "Resize image to %0"
 msgid "Resize image to %0"
-msgstr ""
+msgstr "縮放圖片到 %0"
 
 
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgctxt "The accessibility label of the standalone image resize reset option button in the image toolbar for the screen readers"
 msgid "Resize image to the original size"
 msgid "Resize image to the original size"
-msgstr ""
+msgstr "縮放圖片到原始尺寸"
 
 
 msgctxt "Default label for the resize option that resets the size of the image."
 msgctxt "Default label for the resize option that resets the size of the image."
 msgid "Original"
 msgid "Original"
-msgstr ""
+msgstr "原始圖片"
 
 
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgctxt "The accessibility label of the image resize dropdown list for the screen readers."
 msgid "Image resize list"
 msgid "Image resize list"
-msgstr ""
+msgstr "圖片縮放清單"

+ 3 - 3
packages/ckeditor5-image/src/image/imageediting.js

@@ -61,12 +61,12 @@ export default class ImageEditing extends Plugin {
 
 
 		conversion.for( 'dataDowncast' ).elementToElement( {
 		conversion.for( 'dataDowncast' ).elementToElement( {
 			model: 'image',
 			model: 'image',
-			view: ( modelElement, viewWriter ) => createImageViewElement( viewWriter )
+			view: ( modelElement, { writer } ) => createImageViewElement( writer )
 		} );
 		} );
 
 
 		conversion.for( 'editingDowncast' ).elementToElement( {
 		conversion.for( 'editingDowncast' ).elementToElement( {
 			model: 'image',
 			model: 'image',
-			view: ( modelElement, viewWriter ) => toImageWidget( createImageViewElement( viewWriter ), viewWriter, t( 'image widget' ) )
+			view: ( modelElement, { writer } ) => toImageWidget( createImageViewElement( writer ), writer, t( 'image widget' ) )
 		} );
 		} );
 
 
 		conversion.for( 'downcast' )
 		conversion.for( 'downcast' )
@@ -82,7 +82,7 @@ export default class ImageEditing extends Plugin {
 						src: true
 						src: true
 					}
 					}
 				},
 				},
-				model: ( viewImage, modelWriter ) => modelWriter.createElement( 'image', { src: viewImage.getAttribute( 'src' ) } )
+				model: ( viewImage, { writer } ) => writer.createElement( 'image', { src: viewImage.getAttribute( 'src' ) } )
 			} )
 			} )
 			.attributeToAttribute( {
 			.attributeToAttribute( {
 				view: {
 				view: {

+ 3 - 3
packages/ckeditor5-image/tests/image/converters.js

@@ -37,8 +37,8 @@ describe( 'Image converters', () => {
 					isBlock: true
 					isBlock: true
 				} );
 				} );
 
 
-				const editingElementCreator = ( modelElement, viewWriter ) =>
-					toImageWidget( createImageViewElement( viewWriter ), viewWriter, '' );
+				const editingElementCreator = ( modelElement, { writer } ) =>
+					toImageWidget( createImageViewElement( writer ), writer, '' );
 
 
 				editor.conversion.for( 'editingDowncast' ).elementToElement( {
 				editor.conversion.for( 'editingDowncast' ).elementToElement( {
 					model: 'image',
 					model: 'image',
@@ -75,7 +75,7 @@ describe( 'Image converters', () => {
 							src: true
 							src: true
 						}
 						}
 					},
 					},
-					model: ( viewImage, writer ) => {
+					model: ( viewImage, { writer } ) => {
 						imgConverterCalled = true;
 						imgConverterCalled = true;
 
 
 						return writer.createElement( 'image', { src: viewImage.getAttribute( 'src' ) } );
 						return writer.createElement( 'image', { src: viewImage.getAttribute( 'src' ) } );

+ 1 - 1
packages/ckeditor5-image/tests/image/imageinsertcommand.js

@@ -91,7 +91,7 @@ describe( 'ImageInsertCommand', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows image', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows image', () => {
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
-			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true } );
+			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true, isSelectable: true } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );

+ 1 - 1
packages/ckeditor5-image/tests/image/utils.js

@@ -192,7 +192,7 @@ describe( 'image widget utils', () => {
 		it( 'should be true when the selection is inside isLimit element which allows image', () => {
 		it( 'should be true when the selection is inside isLimit element which allows image', () => {
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
-			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true } );
+			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true, isSelectable: true } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );

+ 1 - 1
packages/ckeditor5-image/tests/imagecaption/imagecaptionediting.js

@@ -256,7 +256,7 @@ describe( 'ImageCaptionEditing', () => {
 			model.change( writer => {
 			model.change( writer => {
 				model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 				model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 				model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
 				model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
-				model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true } );
+				model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true, isSelectable: true } );
 				model.schema.extend( '$block', { allowIn: 'tableCell' } );
 				model.schema.extend( '$block', { allowIn: 'tableCell' } );
 				editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 				editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 				editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tr' } );
 				editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tr' } );

+ 1 - 1
packages/ckeditor5-image/tests/imageupload/imageuploadcommand.js

@@ -108,7 +108,7 @@ describe( 'ImageUploadCommand', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows image', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows image', () => {
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
-			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true } );
+			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true, isSelectable: true } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );

+ 2 - 2
packages/ckeditor5-indent/lang/translations/ko.po

@@ -18,8 +18,8 @@ msgstr ""
 
 
 msgctxt "Toolbar button tooltip for the increase indentation feature."
 msgctxt "Toolbar button tooltip for the increase indentation feature."
 msgid "Increase indent"
 msgid "Increase indent"
-msgstr "들여쓰기"
+msgstr "들여쓰기 늘리기"
 
 
 msgctxt "Toolbar button tooltip for the decrease indentation feature."
 msgctxt "Toolbar button tooltip for the decrease indentation feature."
 msgid "Decrease indent"
 msgid "Decrease indent"
-msgstr "내어쓰기"
+msgstr "들여쓰기 줄이기"

+ 1 - 1
packages/ckeditor5-link/lang/translations/ko.po

@@ -30,7 +30,7 @@ msgstr "링크 주소"
 
 
 msgctxt "Label for the image link button."
 msgctxt "Label for the image link button."
 msgid "Link image"
 msgid "Link image"
-msgstr ""
+msgstr "사진 링크"
 
 
 msgctxt "Button opening the Link URL editing balloon."
 msgctxt "Button opening the Link URL editing balloon."
 msgid "Edit link"
 msgid "Edit link"

+ 1 - 1
packages/ckeditor5-link/lang/translations/zh.po

@@ -30,7 +30,7 @@ msgstr "連結˙ URL"
 
 
 msgctxt "Label for the image link button."
 msgctxt "Label for the image link button."
 msgid "Link image"
 msgid "Link image"
-msgstr ""
+msgstr "圖片連結"
 
 
 msgctxt "Button opening the Link URL editing balloon."
 msgctxt "Button opening the Link URL editing balloon."
 msgid "Edit link"
 msgid "Edit link"

+ 3 - 3
packages/ckeditor5-link/src/linkediting.js

@@ -75,8 +75,8 @@ export default class LinkEditing extends Plugin {
 			.attributeToElement( { model: 'linkHref', view: createLinkElement } );
 			.attributeToElement( { model: 'linkHref', view: createLinkElement } );
 
 
 		editor.conversion.for( 'editingDowncast' )
 		editor.conversion.for( 'editingDowncast' )
-			.attributeToElement( { model: 'linkHref', view: ( href, writer ) => {
-				return createLinkElement( ensureSafeUrl( href ), writer );
+			.attributeToElement( { model: 'linkHref', view: ( href, conversionApi ) => {
+				return createLinkElement( ensureSafeUrl( href ), conversionApi );
 			} } );
 			} } );
 
 
 		editor.conversion.for( 'upcast' )
 		editor.conversion.for( 'upcast' )
@@ -190,7 +190,7 @@ export default class LinkEditing extends Plugin {
 
 
 			editor.conversion.for( 'downcast' ).attributeToElement( {
 			editor.conversion.for( 'downcast' ).attributeToElement( {
 				model: decorator.id,
 				model: decorator.id,
-				view: ( manualDecoratorName, writer ) => {
+				view: ( manualDecoratorName, { writer } ) => {
 					if ( manualDecoratorName ) {
 					if ( manualDecoratorName ) {
 						const attributes = manualDecorators.get( decorator.id ).attributes;
 						const attributes = manualDecorators.get( decorator.id ).attributes;
 						const element = writer.createAttributeElement( 'a', attributes, { priority: 5 } );
 						const element = writer.createAttributeElement( 'a', attributes, { priority: 5 } );

+ 2 - 1
packages/ckeditor5-link/src/utils.js

@@ -31,9 +31,10 @@ export function isLinkElement( node ) {
  * Creates link {@link module:engine/view/attributeelement~AttributeElement} with the provided `href` attribute.
  * Creates link {@link module:engine/view/attributeelement~AttributeElement} with the provided `href` attribute.
  *
  *
  * @param {String} href
  * @param {String} href
+ * @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
  * @returns {module:engine/view/attributeelement~AttributeElement}
  * @returns {module:engine/view/attributeelement~AttributeElement}
  */
  */
-export function createLinkElement( href, writer ) {
+export function createLinkElement( href, { writer } ) {
 	// Priority 5 - https://github.com/ckeditor/ckeditor5-link/issues/121.
 	// Priority 5 - https://github.com/ckeditor/ckeditor5-link/issues/121.
 	const linkElement = writer.createAttributeElement( 'a', { href }, { priority: 5 } );
 	const linkElement = writer.createAttributeElement( 'a', { href }, { priority: 5 } );
 	writer.setCustomProperty( 'link', true, linkElement );
 	writer.setCustomProperty( 'link', true, linkElement );

+ 4 - 2
packages/ckeditor5-link/tests/utils.js

@@ -15,7 +15,8 @@ import { createLinkElement, isLinkElement, ensureSafeUrl, normalizeDecorators, i
 describe( 'utils', () => {
 describe( 'utils', () => {
 	describe( 'isLinkElement()', () => {
 	describe( 'isLinkElement()', () => {
 		it( 'should return true for elements created by createLinkElement', () => {
 		it( 'should return true for elements created by createLinkElement', () => {
-			const element = createLinkElement( 'http://ckeditor.com', new ViewDowncastWriter( new ViewDocument() ) );
+			const writer = new ViewDowncastWriter( new ViewDocument() );
+			const element = createLinkElement( 'http://ckeditor.com', { writer } );
 
 
 			expect( isLinkElement( element ) ).to.be.true;
 			expect( isLinkElement( element ) ).to.be.true;
 		} );
 		} );
@@ -35,7 +36,8 @@ describe( 'utils', () => {
 
 
 	describe( 'createLinkElement()', () => {
 	describe( 'createLinkElement()', () => {
 		it( 'should create link AttributeElement', () => {
 		it( 'should create link AttributeElement', () => {
-			const element = createLinkElement( 'http://cksource.com', new ViewDowncastWriter( new ViewDocument() ) );
+			const writer = new ViewDowncastWriter( new ViewDocument() );
+			const element = createLinkElement( 'http://cksource.com', { writer } );
 
 
 			expect( isLinkElement( element ) ).to.be.true;
 			expect( isLinkElement( element ) ).to.be.true;
 			expect( element.priority ).to.equal( 5 );
 			expect( element.priority ).to.equal( 5 );

+ 3 - 3
packages/ckeditor5-list/lang/translations/ko.po

@@ -18,12 +18,12 @@ msgstr ""
 
 
 msgctxt "Toolbar button tooltip for the Numbered List feature."
 msgctxt "Toolbar button tooltip for the Numbered List feature."
 msgid "Numbered List"
 msgid "Numbered List"
-msgstr "번호매기기"
+msgstr "번호 목록"
 
 
 msgctxt "Toolbar button tooltip for the Bulleted List feature."
 msgctxt "Toolbar button tooltip for the Bulleted List feature."
 msgid "Bulleted List"
 msgid "Bulleted List"
-msgstr "글머리기호"
+msgstr "불릿 목록"
 
 
 msgctxt "Toolbar button tooltip for the To-do List feature."
 msgctxt "Toolbar button tooltip for the To-do List feature."
 msgid "To-do List"
 msgid "To-do List"
-msgstr "할일 목록"
+msgstr "확인 목록"

+ 1 - 1
packages/ckeditor5-list/lang/translations/zh.po

@@ -26,4 +26,4 @@ msgstr "符號清單"
 
 
 msgctxt "Toolbar button tooltip for the To-do List feature."
 msgctxt "Toolbar button tooltip for the To-do List feature."
 msgid "To-do List"
 msgid "To-do List"
-msgstr ""
+msgstr "代辦清單"

+ 2 - 2
packages/ckeditor5-list/tests/todolistediting.js

@@ -559,12 +559,12 @@ describe( 'TodoListEditing', () => {
 
 
 			editor.conversion.for( 'downcast' ).markerToElement( {
 			editor.conversion.for( 'downcast' ).markerToElement( {
 				model: 'element1',
 				model: 'element1',
-				view: ( data, writer ) => writer.createUIElement( 'element1' )
+				view: ( data, { writer } ) => writer.createUIElement( 'element1' )
 			} );
 			} );
 
 
 			editor.conversion.for( 'downcast' ).markerToElement( {
 			editor.conversion.for( 'downcast' ).markerToElement( {
 				model: 'element2',
 				model: 'element2',
-				view: ( data, writer ) => writer.createUIElement( 'element2' )
+				view: ( data, { writer } ) => writer.createUIElement( 'element2' )
 			} );
 			} );
 
 
 			editor.conversion.for( 'downcast' ).markerToHighlight( {
 			editor.conversion.for( 'downcast' ).markerToHighlight( {

+ 4 - 4
packages/ckeditor5-media-embed/lang/translations/ko.po

@@ -26,19 +26,19 @@ msgstr "미디어 URL"
 
 
 msgctxt "The help text displayed under the media URL input helping users to discover the interface."
 msgctxt "The help text displayed under the media URL input helping users to discover the interface."
 msgid "Paste the media URL in the input."
 msgid "Paste the media URL in the input."
-msgstr "미디어 URL을 입력해주세요."
+msgstr "미디어 URL을 입력해주세요."
 
 
 msgctxt "The tip displayed next to the media URL input informing about an easier way of embedding."
 msgctxt "The tip displayed next to the media URL input informing about an easier way of embedding."
 msgid "Tip: Paste the URL into the content to embed faster."
 msgid "Tip: Paste the URL into the content to embed faster."
-msgstr "Tip: URL을 복사 후 붙여넣기하면 더 빠릅니다."
+msgstr "팁: URL을 붙여넣기하면 더 빨리 삽입할 수 있습니다."
 
 
 msgctxt "An error message that informs about an empty value in the URL input."
 msgctxt "An error message that informs about an empty value in the URL input."
 msgid "The URL must not be empty."
 msgid "The URL must not be empty."
-msgstr "URL이 비어있습니다."
+msgstr "URL이 비어있을 수 없습니다."
 
 
 msgctxt "An error message that informs about unsupported media URL."
 msgctxt "An error message that informs about unsupported media URL."
 msgid "This media URL is not supported."
 msgid "This media URL is not supported."
-msgstr "이 URL은 지원되지 않습니다."
+msgstr "이 미디어 URL은 지원되지 않습니다."
 
 
 msgctxt "Toolbar button tooltip for the Media Embed feature."
 msgctxt "Toolbar button tooltip for the Media Embed feature."
 msgid "Insert media"
 msgid "Insert media"

+ 6 - 6
packages/ckeditor5-media-embed/lang/translations/zh.po

@@ -22,23 +22,23 @@ msgstr "影音小工具"
 
 
 msgctxt "Label for the URL input in the Media Embed URL editing balloon."
 msgctxt "Label for the URL input in the Media Embed URL editing balloon."
 msgid "Media URL"
 msgid "Media URL"
-msgstr "影音URL"
+msgstr "影音網址"
 
 
 msgctxt "The help text displayed under the media URL input helping users to discover the interface."
 msgctxt "The help text displayed under the media URL input helping users to discover the interface."
 msgid "Paste the media URL in the input."
 msgid "Paste the media URL in the input."
-msgstr "在輸入框貼上影音URL。"
+msgstr "在輸入框貼上影音網址。"
 
 
 msgctxt "The tip displayed next to the media URL input informing about an easier way of embedding."
 msgctxt "The tip displayed next to the media URL input informing about an easier way of embedding."
 msgid "Tip: Paste the URL into the content to embed faster."
 msgid "Tip: Paste the URL into the content to embed faster."
-msgstr "提示:在內容貼上URL更快崁入。"
+msgstr "提示:在內容貼上網址更快崁入。"
 
 
 msgctxt "An error message that informs about an empty value in the URL input."
 msgctxt "An error message that informs about an empty value in the URL input."
 msgid "The URL must not be empty."
 msgid "The URL must not be empty."
-msgstr "URL不能空白。"
+msgstr "網址不能空白。"
 
 
 msgctxt "An error message that informs about unsupported media URL."
 msgctxt "An error message that informs about unsupported media URL."
 msgid "This media URL is not supported."
 msgid "This media URL is not supported."
-msgstr "影音URL不支援。"
+msgstr "不支援此影音網址。"
 
 
 msgctxt "Toolbar button tooltip for the Media Embed feature."
 msgctxt "Toolbar button tooltip for the Media Embed feature."
 msgid "Insert media"
 msgid "Insert media"
@@ -46,4 +46,4 @@ msgstr "插入影音"
 
 
 msgctxt "The label used by assistive technologies describing an image toolbar attached to an image widget."
 msgctxt "The label used by assistive technologies describing an image toolbar attached to an image widget."
 msgid "Media toolbar"
 msgid "Media toolbar"
-msgstr ""
+msgstr "影音工具"

+ 9 - 9
packages/ckeditor5-media-embed/src/mediaembedediting.js

@@ -177,10 +177,10 @@ export default class MediaEmbedEditing extends Plugin {
 		// Model -> Data
 		// Model -> Data
 		conversion.for( 'dataDowncast' ).elementToElement( {
 		conversion.for( 'dataDowncast' ).elementToElement( {
 			model: 'media',
 			model: 'media',
-			view: ( modelElement, viewWriter ) => {
+			view: ( modelElement, { writer } ) => {
 				const url = modelElement.getAttribute( 'url' );
 				const url = modelElement.getAttribute( 'url' );
 
 
-				return createMediaFigureElement( viewWriter, registry, url, {
+				return createMediaFigureElement( writer, registry, url, {
 					renderMediaPreview: url && renderMediaPreview
 					renderMediaPreview: url && renderMediaPreview
 				} );
 				} );
 			}
 			}
@@ -195,13 +195,13 @@ export default class MediaEmbedEditing extends Plugin {
 		// Model -> View (element)
 		// Model -> View (element)
 		conversion.for( 'editingDowncast' ).elementToElement( {
 		conversion.for( 'editingDowncast' ).elementToElement( {
 			model: 'media',
 			model: 'media',
-			view: ( modelElement, viewWriter ) => {
+			view: ( modelElement, { writer } ) => {
 				const url = modelElement.getAttribute( 'url' );
 				const url = modelElement.getAttribute( 'url' );
-				const figure = createMediaFigureElement( viewWriter, registry, url, {
+				const figure = createMediaFigureElement( writer, registry, url, {
 					renderForEditingView: true
 					renderForEditingView: true
 				} );
 				} );
 
 
-				return toMediaWidget( figure, viewWriter, t( 'media widget' ) );
+				return toMediaWidget( figure, writer, t( 'media widget' ) );
 			}
 			}
 		} );
 		} );
 
 
@@ -221,11 +221,11 @@ export default class MediaEmbedEditing extends Plugin {
 						url: true
 						url: true
 					}
 					}
 				},
 				},
-				model: ( viewMedia, modelWriter ) => {
+				model: ( viewMedia, { writer } ) => {
 					const url = viewMedia.getAttribute( 'url' );
 					const url = viewMedia.getAttribute( 'url' );
 
 
 					if ( registry.hasMedia( url ) ) {
 					if ( registry.hasMedia( url ) ) {
-						return modelWriter.createElement( 'media', { url } );
+						return writer.createElement( 'media', { url } );
 					}
 					}
 				}
 				}
 			} )
 			} )
@@ -237,11 +237,11 @@ export default class MediaEmbedEditing extends Plugin {
 						'data-oembed-url': true
 						'data-oembed-url': true
 					}
 					}
 				},
 				},
-				model: ( viewMedia, modelWriter ) => {
+				model: ( viewMedia, { writer } ) => {
 					const url = viewMedia.getAttribute( 'data-oembed-url' );
 					const url = viewMedia.getAttribute( 'data-oembed-url' );
 
 
 					if ( registry.hasMedia( url ) ) {
 					if ( registry.hasMedia( url ) ) {
-						return modelWriter.createElement( 'media', { url } );
+						return writer.createElement( 'media', { url } );
 					}
 					}
 				}
 				}
 			} );
 			} );

+ 2 - 2
packages/ckeditor5-mention/docs/_snippets/examples/chat-with-mentions.js

@@ -127,7 +127,7 @@ function MentionLinks( editor ) {
 	// element.
 	// element.
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 		model: 'mention',
 		model: 'mention',
-		view: ( modelAttributeValue, viewWriter ) => {
+		view: ( modelAttributeValue, { writer } ) => {
 			// Do not convert empty attributes (lack of value means no mention).
 			// Do not convert empty attributes (lack of value means no mention).
 			if ( !modelAttributeValue ) {
 			if ( !modelAttributeValue ) {
 				return;
 				return;
@@ -142,7 +142,7 @@ function MentionLinks( editor ) {
 				href = `https://example.com/social/${ modelAttributeValue.id.slice( 1 ) }`;
 				href = `https://example.com/social/${ modelAttributeValue.id.slice( 1 ) }`;
 			}
 			}
 
 
-			return viewWriter.createAttributeElement( 'a', {
+			return writer.createAttributeElement( 'a', {
 				class: 'mention',
 				class: 'mention',
 				'data-mention': modelAttributeValue.id,
 				'data-mention': modelAttributeValue.id,
 				href
 				href

+ 2 - 2
packages/ckeditor5-mention/docs/_snippets/features/mention-customization.js

@@ -68,13 +68,13 @@ function MentionCustomization( editor ) {
 	// Downcast the model 'mention' text attribute to a view <a> element.
 	// Downcast the model 'mention' text attribute to a view <a> element.
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 		model: 'mention',
 		model: 'mention',
-		view: ( modelAttributeValue, viewWriter ) => {
+		view: ( modelAttributeValue, { writer } ) => {
 			// Do not convert empty attributes (lack of value means no mention).
 			// Do not convert empty attributes (lack of value means no mention).
 			if ( !modelAttributeValue ) {
 			if ( !modelAttributeValue ) {
 				return;
 				return;
 			}
 			}
 
 
-			return viewWriter.createAttributeElement( 'a', {
+			return writer.createAttributeElement( 'a', {
 				class: 'mention',
 				class: 'mention',
 				'data-mention': modelAttributeValue.id,
 				'data-mention': modelAttributeValue.id,
 				'data-user-id': modelAttributeValue.userId,
 				'data-user-id': modelAttributeValue.userId,

+ 2 - 2
packages/ckeditor5-mention/docs/examples/chat-with-mentions.md

@@ -308,7 +308,7 @@ function MentionLinks( editor ) {
 	// element.
 	// element.
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 		model: 'mention',
 		model: 'mention',
-		view: ( modelAttributeValue, viewWriter ) => {
+		view: ( modelAttributeValue, { writer } ) => {
 			// Do not convert empty attributes (lack of value means no mention).
 			// Do not convert empty attributes (lack of value means no mention).
 			if ( !modelAttributeValue ) {
 			if ( !modelAttributeValue ) {
 				return;
 				return;
@@ -323,7 +323,7 @@ function MentionLinks( editor ) {
 				href = `https://example.com/social/${ modelAttributeValue.id.slice( 1 ) }`;
 				href = `https://example.com/social/${ modelAttributeValue.id.slice( 1 ) }`;
 			}
 			}
 
 
-			return viewWriter.createAttributeElement( 'a', {
+			return writer.createAttributeElement( 'a', {
 				class: 'mention',
 				class: 'mention',
 				'data-mention': modelAttributeValue.id,
 				'data-mention': modelAttributeValue.id,
 				href
 				href

+ 4 - 4
packages/ckeditor5-mention/docs/features/mentions.md

@@ -247,13 +247,13 @@ function MentionCustomization( editor ) {
 	// Downcast the model 'mention' text attribute to a view <a> element.
 	// Downcast the model 'mention' text attribute to a view <a> element.
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 		model: 'mention',
 		model: 'mention',
-		view: ( modelAttributeValue, viewWriter ) => {
+		view: ( modelAttributeValue, { writer } ) => {
 			// Do not convert empty attributes (lack of value means no mention).
 			// Do not convert empty attributes (lack of value means no mention).
 			if ( !modelAttributeValue ) {
 			if ( !modelAttributeValue ) {
 				return;
 				return;
 			}
 			}
 
 
-			return viewWriter.createAttributeElement( 'a', {
+			return writer.createAttributeElement( 'a', {
 				class: 'mention',
 				class: 'mention',
 				'data-mention': modelAttributeValue.id,
 				'data-mention': modelAttributeValue.id,
 				'data-user-id': modelAttributeValue.userId,
 				'data-user-id': modelAttributeValue.userId,
@@ -339,13 +339,13 @@ function MentionCustomization( editor ) {
 	// Downcast the model 'mention' text attribute to a view <a> element.
 	// Downcast the model 'mention' text attribute to a view <a> element.
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 		model: 'mention',
 		model: 'mention',
-		view: ( modelAttributeValue, viewWriter ) => {
+		view: ( modelAttributeValue, { writer } ) => {
 			// Do not convert empty attributes (lack of value means no mention).
 			// Do not convert empty attributes (lack of value means no mention).
 			if ( !modelAttributeValue ) {
 			if ( !modelAttributeValue ) {
 				return;
 				return;
 			}
 			}
 
 
-			return viewWriter.createAttributeElement( 'a', {
+			return writer.createAttributeElement( 'a', {
 				class: 'mention',
 				class: 'mention',
 				'data-mention': modelAttributeValue.id,
 				'data-mention': modelAttributeValue.id,
 				'data-user-id': modelAttributeValue.userId,
 				'data-user-id': modelAttributeValue.userId,

+ 3 - 3
packages/ckeditor5-mention/src/mentionediting.js

@@ -129,9 +129,9 @@ function preventPartialMentionDowncast( dispatcher ) {
 // Creates a mention element from the mention data.
 // Creates a mention element from the mention data.
 //
 //
 // @param {Object} mention
 // @param {Object} mention
-// @param {module:engine/view/downcastwriter~DowncastWriter} viewWriter
+// @param {module:engine/conversion/downcastdispatcher~DowncastConversionApi} conversionApi
 // @returns {module:engine/view/attributeelement~AttributeElement}
 // @returns {module:engine/view/attributeelement~AttributeElement}
-function createViewMentionElement( mention, viewWriter ) {
+function createViewMentionElement( mention, { writer } ) {
 	if ( !mention ) {
 	if ( !mention ) {
 		return;
 		return;
 	}
 	}
@@ -146,7 +146,7 @@ function createViewMentionElement( mention, viewWriter ) {
 		priority: 20
 		priority: 20
 	};
 	};
 
 
-	return viewWriter.createAttributeElement( 'span', attributes, options );
+	return writer.createAttributeElement( 'span', attributes, options );
 }
 }
 
 
 // Model post-fixer that disallows typing with selection when the selection is placed after the text node with the mention attribute or
 // Model post-fixer that disallows typing with selection when the selection is placed after the text node with the mention attribute or

+ 2 - 2
packages/ckeditor5-mention/tests/manual/mention-custom-view.js

@@ -41,12 +41,12 @@ class CustomMentionAttributeView extends Plugin {
 
 
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 			model: 'mention',
 			model: 'mention',
-			view: ( modelAttributeValue, viewWriter ) => {
+			view: ( modelAttributeValue, { writer } ) => {
 				if ( !modelAttributeValue ) {
 				if ( !modelAttributeValue ) {
 					return;
 					return;
 				}
 				}
 
 
-				return viewWriter.createAttributeElement( 'a', {
+				return writer.createAttributeElement( 'a', {
 					class: 'mention',
 					class: 'mention',
 					'data-mention': modelAttributeValue.id,
 					'data-mention': modelAttributeValue.id,
 					'href': modelAttributeValue.link
 					'href': modelAttributeValue.link

+ 7 - 7
packages/ckeditor5-mention/tests/manual/mention.js

@@ -30,10 +30,10 @@ class InlineWidget extends Plugin {
 
 
 		editor.conversion.for( 'editingDowncast' ).elementToElement( {
 		editor.conversion.for( 'editingDowncast' ).elementToElement( {
 			model: 'placeholder',
 			model: 'placeholder',
-			view: ( modelItem, viewWriter ) => {
-				const widgetElement = createPlaceholderView( modelItem, viewWriter );
+			view: ( modelItem, conversionApi ) => {
+				const widgetElement = createPlaceholderView( modelItem, conversionApi );
 
 
-				return toWidget( widgetElement, viewWriter );
+				return toWidget( widgetElement, conversionApi.writer );
 			}
 			}
 		} );
 		} );
 
 
@@ -66,11 +66,11 @@ class InlineWidget extends Plugin {
 
 
 		this._createToolbarButton();
 		this._createToolbarButton();
 
 
-		function createPlaceholderView( modelItem, viewWriter ) {
-			const widgetElement = viewWriter.createContainerElement( 'placeholder' );
-			const viewText = viewWriter.createText( '{' + modelItem.getAttribute( 'type' ) + '}' );
+		function createPlaceholderView( modelItem, { writer } ) {
+			const widgetElement = writer.createContainerElement( 'placeholder' );
+			const viewText = writer.createText( '{' + modelItem.getAttribute( 'type' ) + '}' );
 
 
-			viewWriter.insert( viewWriter.createPositionAt( widgetElement, 0 ), viewText );
+			writer.insert( writer.createPositionAt( widgetElement, 0 ), viewText );
 
 
 			return widgetElement;
 			return widgetElement;
 		}
 		}

+ 4 - 4
packages/ckeditor5-mention/tests/mentionediting.js

@@ -188,12 +188,12 @@ describe( 'MentionEditing', () => {
 
 
 			editor.conversion.for( 'downcast' ).attributeToElement( {
 			editor.conversion.for( 'downcast' ).attributeToElement( {
 				model: 'mention',
 				model: 'mention',
-				view: ( modelAttributeValue, viewWriter ) => {
+				view: ( modelAttributeValue, { writer } ) => {
 					if ( !modelAttributeValue ) {
 					if ( !modelAttributeValue ) {
 						return;
 						return;
 					}
 					}
 
 
-					return viewWriter.createAttributeElement( 'a', {
+					return writer.createAttributeElement( 'a', {
 						class: 'mention',
 						class: 'mention',
 						'data-mention': modelAttributeValue.id,
 						'data-mention': modelAttributeValue.id,
 						'href': modelAttributeValue.link
 						'href': modelAttributeValue.link
@@ -672,12 +672,12 @@ function addCustomMentionConverters( editor ) {
 
 
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 	editor.conversion.for( 'downcast' ).attributeToElement( {
 		model: 'mention',
 		model: 'mention',
-		view: ( modelAttributeValue, viewWriter ) => {
+		view: ( modelAttributeValue, { writer } ) => {
 			if ( !modelAttributeValue ) {
 			if ( !modelAttributeValue ) {
 				return;
 				return;
 			}
 			}
 
 
-			return viewWriter.createAttributeElement( 'b', {
+			return writer.createAttributeElement( 'b', {
 				class: 'mention',
 				class: 'mention',
 				'data-mention': modelAttributeValue.id
 				'data-mention': modelAttributeValue.id
 			}, {
 			}, {

+ 1 - 1
packages/ckeditor5-mention/tests/mentionui.js

@@ -566,7 +566,7 @@ describe( 'MentionUI', () => {
 				editor.conversion.for( 'downcast' )
 				editor.conversion.for( 'downcast' )
 					.elementToElement( {
 					.elementToElement( {
 						model: 'softBreak',
 						model: 'softBreak',
-						view: ( modelElement, viewWriter ) => viewWriter.createEmptyElement( 'br' )
+						view: ( modelElement, { writer } ) => writer.createEmptyElement( 'br' )
 					} );
 					} );
 
 
 				setData( model, '<paragraph>abc<softBreak></softBreak>[] foo</paragraph>' );
 				setData( model, '<paragraph>abc<softBreak></softBreak>[] foo</paragraph>' );

+ 21 - 0
packages/ckeditor5-page-break/lang/translations/zh.po

@@ -0,0 +1,21 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Chinese (Taiwan) (https://www.transifex.com/ckeditor/teams/11143/zh_TW/)\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Page break"
+msgid "Page break"
+msgstr "換頁"

+ 14 - 14
packages/ckeditor5-page-break/src/pagebreakediting.js

@@ -42,8 +42,8 @@ export default class PageBreakEditing extends Plugin {
 
 
 		conversion.for( 'dataDowncast' ).elementToElement( {
 		conversion.for( 'dataDowncast' ).elementToElement( {
 			model: 'pageBreak',
 			model: 'pageBreak',
-			view: ( modelElement, viewWriter ) => {
-				const divElement = viewWriter.createContainerElement( 'div', {
+			view: ( modelElement, { writer } ) => {
+				const divElement = writer.createContainerElement( 'div', {
 					class: 'page-break',
 					class: 'page-break',
 					// If user has no `.ck-content` styles, it should always break a page during print.
 					// If user has no `.ck-content` styles, it should always break a page during print.
 					style: 'page-break-after: always'
 					style: 'page-break-after: always'
@@ -51,11 +51,11 @@ export default class PageBreakEditing extends Plugin {
 
 
 				// For a rationale of using span inside a div see:
 				// For a rationale of using span inside a div see:
 				// https://github.com/ckeditor/ckeditor5-page-break/pull/1#discussion_r328934062.
 				// https://github.com/ckeditor/ckeditor5-page-break/pull/1#discussion_r328934062.
-				const spanElement = viewWriter.createContainerElement( 'span', {
+				const spanElement = writer.createContainerElement( 'span', {
 					style: 'display: none'
 					style: 'display: none'
 				} );
 				} );
 
 
-				viewWriter.insert( viewWriter.createPositionAt( divElement, 0 ), spanElement );
+				writer.insert( writer.createPositionAt( divElement, 0 ), spanElement );
 
 
 				return divElement;
 				return divElement;
 			}
 			}
@@ -63,21 +63,21 @@ export default class PageBreakEditing extends Plugin {
 
 
 		conversion.for( 'editingDowncast' ).elementToElement( {
 		conversion.for( 'editingDowncast' ).elementToElement( {
 			model: 'pageBreak',
 			model: 'pageBreak',
-			view: ( modelElement, viewWriter ) => {
+			view: ( modelElement, { writer } ) => {
 				const label = t( 'Page break' );
 				const label = t( 'Page break' );
-				const viewWrapper = viewWriter.createContainerElement( 'div' );
-				const viewLabelElement = viewWriter.createContainerElement( 'span' );
-				const innerText = viewWriter.createText( t( 'Page break' ) );
+				const viewWrapper = writer.createContainerElement( 'div' );
+				const viewLabelElement = writer.createContainerElement( 'span' );
+				const innerText = writer.createText( t( 'Page break' ) );
 
 
-				viewWriter.addClass( 'page-break', viewWrapper );
-				viewWriter.setCustomProperty( 'pageBreak', true, viewWrapper );
+				writer.addClass( 'page-break', viewWrapper );
+				writer.setCustomProperty( 'pageBreak', true, viewWrapper );
 
 
-				viewWriter.addClass( 'page-break__label', viewLabelElement );
+				writer.addClass( 'page-break__label', viewLabelElement );
 
 
-				viewWriter.insert( viewWriter.createPositionAt( viewWrapper, 0 ), viewLabelElement );
-				viewWriter.insert( viewWriter.createPositionAt( viewLabelElement, 0 ), innerText );
+				writer.insert( writer.createPositionAt( viewWrapper, 0 ), viewLabelElement );
+				writer.insert( writer.createPositionAt( viewLabelElement, 0 ), innerText );
 
 
-				return toPageBreakWidget( viewWrapper, viewWriter, label );
+				return toPageBreakWidget( viewWrapper, writer, label );
 			}
 			}
 		} );
 		} );
 
 

+ 1 - 1
packages/ckeditor5-page-break/tests/pagebreakcommand.js

@@ -84,7 +84,7 @@ describe( 'PageBreakCommand', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows page break', () => {
 		it( 'should be true when the selection is inside block element inside isLimit element which allows page break', () => {
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'table', { allowWhere: '$block', isLimit: true, isObject: true, isBlock: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
 			model.schema.register( 'tableRow', { allowIn: 'table', isLimit: true } );
-			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true } );
+			model.schema.register( 'tableCell', { allowIn: 'tableRow', isLimit: true, isSelectable: true } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			model.schema.extend( '$block', { allowIn: 'tableCell' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'table', view: 'table' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );
 			editor.conversion.for( 'downcast' ).elementToElement( { model: 'tableRow', view: 'tableRow' } );

+ 5 - 89
packages/ckeditor5-paragraph/src/paragraph.js

@@ -40,7 +40,6 @@ export default class Paragraph extends Plugin {
 	init() {
 	init() {
 		const editor = this.editor;
 		const editor = this.editor;
 		const model = editor.model;
 		const model = editor.model;
-		const data = editor.data;
 
 
 		editor.commands.add( 'paragraph', new ParagraphCommand( editor ) );
 		editor.commands.add( 'paragraph', new ParagraphCommand( editor ) );
 		editor.commands.add( 'insertParagraph', new InsertParagraphCommand( editor ) );
 		editor.commands.add( 'insertParagraph', new InsertParagraphCommand( editor ) );
@@ -50,12 +49,9 @@ export default class Paragraph extends Plugin {
 
 
 		editor.conversion.elementToElement( { model: 'paragraph', view: 'p' } );
 		editor.conversion.elementToElement( { model: 'paragraph', view: 'p' } );
 
 
-		// Content autoparagraphing. --------------------------------------------------
-
-		// Handles element which has not been converted by any plugin and checks if it would be converted if
-		// we wrap it in a paragraph or change it to a paragraph.
+		// Conversion for paragraph-like elements which has not been converted by any plugin.
 		editor.conversion.for( 'upcast' ).elementToElement( {
 		editor.conversion.for( 'upcast' ).elementToElement( {
-			model: ( viewElement, modelWriter ) => {
+			model: ( viewElement, { writer } ) => {
 				if ( !Paragraph.paragraphLikeElements.has( viewElement.name ) ) {
 				if ( !Paragraph.paragraphLikeElements.has( viewElement.name ) ) {
 					return null;
 					return null;
 				}
 				}
@@ -65,69 +61,11 @@ export default class Paragraph extends Plugin {
 					return null;
 					return null;
 				}
 				}
 
 
-				return modelWriter.createElement( 'paragraph' );
+				return writer.createElement( 'paragraph' );
 			},
 			},
 			view: /.+/,
 			view: /.+/,
 			converterPriority: 'low'
 			converterPriority: 'low'
 		} );
 		} );
-
-		data.upcastDispatcher.on( 'element', ( evt, data, conversionApi ) => {
-			// Do not try auto-paragraphing if the element was already converted.
-			if ( !conversionApi.consumable.test( data.viewItem, { name: data.viewItem.name } ) ) {
-				return;
-			}
-
-			// If the element is not paragraph-like try wrapping it in a paragraph.
-			if ( isParagraphable( data.viewItem, data.modelCursor, conversionApi.schema ) ) {
-				Object.assign( data, wrapInParagraph( data.viewItem, data.modelCursor, conversionApi ) );
-			}
-		}, { priority: 'low' } );
-
-		// Handles not converted text nodes and checks if would be converted if we wraps then by a paragraph.
-		data.upcastDispatcher.on( 'text', ( evt, data, conversionApi ) => {
-			// When node is already converted then do nothing.
-			if ( data.modelRange ) {
-				return;
-			}
-
-			if ( isParagraphable( data.viewItem, data.modelCursor, conversionApi.schema ) ) {
-				Object.assign( data, wrapInParagraph( data.viewItem, data.modelCursor, conversionApi ) );
-			}
-		}, { priority: 'lowest' } );
-
-		// Empty roots autoparagraphing. -----------------------------------------------
-
-		// Post-fixer which takes care of adding empty paragraph elements to empty roots.
-		// Besides fixing content on #changesDone we also need to handle editor.data#ready event because
-		// if initial data is empty or setData() wasn't even called there will be no #change fired.
-		model.document.registerPostFixer( writer => this._autoparagraphEmptyRoots( writer ) );
-
-		editor.data.on( 'ready', () => {
-			model.enqueueChange( 'transparent', writer => this._autoparagraphEmptyRoots( writer ) );
-		}, { priority: 'lowest' } );
-	}
-
-	/**
-	 * Fixes all empty roots.
-	 *
-	 * @private
-	 * @returns {Boolean} `true` if any change has been applied, `false` otherwise.
-	 */
-	_autoparagraphEmptyRoots( writer ) {
-		const model = this.editor.model;
-
-		for ( const rootName of model.document.getRootNames() ) {
-			const root = model.document.getRoot( rootName );
-
-			if ( root.isEmpty && root.rootName != '$graveyard' ) {
-				// If paragraph element is allowed in the root, create paragraph element.
-				if ( model.schema.checkChild( root, 'paragraph' ) ) {
-					writer.insertElement( 'paragraph', root );
-
-					return true;
-				}
-			}
-		}
 	}
 	}
 }
 }
 
 
@@ -175,28 +113,6 @@ Paragraph.paragraphLikeElements = new Set( [
 	'h6',
 	'h6',
 	'li',
 	'li',
 	'p',
 	'p',
-	'td'
+	'td',
+	'th'
 ] );
 ] );
-
-function wrapInParagraph( input, position, conversionApi ) {
-	const paragraph = conversionApi.writer.createElement( 'paragraph' );
-
-	conversionApi.writer.insert( paragraph, position );
-	return conversionApi.convertItem( input, conversionApi.writer.createPositionAt( paragraph, 0 ) );
-}
-
-function isParagraphable( node, position, schema ) {
-	const context = schema.createContext( position );
-
-	// When paragraph is allowed in this context...
-	if ( !schema.checkChild( context, 'paragraph' ) ) {
-		return false;
-	}
-
-	// And a node would be allowed in this paragraph...
-	if ( !schema.checkChild( context.push( 'paragraph' ), node ) ) {
-		return false;
-	}
-
-	return true;
-}

+ 18 - 5
packages/ckeditor5-paragraph/tests/paragraph.js

@@ -76,18 +76,31 @@ describe( 'Paragraph feature', () => {
 			} );
 			} );
 
 
 			it( 'should autoparagraph any inline element', () => {
 			it( 'should autoparagraph any inline element', () => {
-				editor.model.schema.register( 'span', { allowWhere: '$text' } );
-				editor.model.schema.extend( '$text', { allowIn: 'span' } );
+				editor.model.schema.register( 'inline', { allowWhere: '$text' } );
+				editor.model.schema.extend( '$text', { allowIn: 'inline' } );
 
 
-				editor.conversion.for( 'downcast' ).elementToElement( { model: 'span', view: 'span' } );
-				editor.conversion.for( 'upcast' ).elementToElement( { model: 'span', view: 'span' } );
+				editor.conversion.for( 'downcast' ).elementToElement( { model: 'inline', view: 'span' } );
+				editor.conversion.for( 'upcast' ).elementToElement( { model: 'inline', view: 'span' } );
 
 
 				editor.setData( '<span>foo</span>' );
 				editor.setData( '<span>foo</span>' );
 
 
-				expect( getModelData( model, { withoutSelection: true } ) ).to.equal( '<paragraph><span>foo</span></paragraph>' );
+				expect( getModelData( model, { withoutSelection: true } ) ).to.equal( '<paragraph><inline>foo</inline></paragraph>' );
 				expect( editor.getData() ).to.equal( '<p><span>foo</span></p>' );
 				expect( editor.getData() ).to.equal( '<p><span>foo</span></p>' );
 			} );
 			} );
 
 
+			it( 'should autoparagraph any inline element with children', () => {
+				editor.model.schema.register( 'inline', { allowWhere: '$text' } );
+				editor.model.schema.extend( '$text', { allowIn: 'inline' } );
+
+				editor.conversion.for( 'downcast' ).elementToElement( { model: 'inline', view: 'span' } );
+				editor.conversion.for( 'upcast' ).elementToElement( { model: 'inline', view: 'span' } );
+
+				editor.setData( '<span>f<b>123</b>oo</span>' );
+
+				expect( getModelData( model, { withoutSelection: true } ) ).to.equal( '<paragraph><inline>f123oo</inline></paragraph>' );
+				expect( editor.getData() ).to.equal( '<p><span>f123oo</span></p>' );
+			} );
+
 			it( 'should not autoparagraph text (in clipboard holder)', () => {
 			it( 'should not autoparagraph text (in clipboard holder)', () => {
 				const modelFragment = editor.data.parse( 'foo', [ '$clipboardHolder' ] );
 				const modelFragment = editor.data.parse( 'foo', [ '$clipboardHolder' ] );
 
 

+ 37 - 0
packages/ckeditor5-restricted-editing/lang/translations/ko.po

@@ -0,0 +1,37 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Korean (https://www.transifex.com/ckeditor/teams/11143/ko/)\n"
+"Language: ko\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "A label of the button indicating that using this button will make a selected text non–editable."
+msgid "Disable editing"
+msgstr "편집 비활성화"
+
+msgctxt "A label of the button indicating that using this button will make a selected text editable."
+msgid "Enable editing"
+msgstr "편집 활성화"
+
+msgctxt "A label of the button that moves selection to the previous editable region in the content."
+msgid "Previous editable region"
+msgstr "이전 편집 가능한 구역"
+
+msgctxt "A label of the button that moves selection to the next editable region in the content."
+msgid "Next editable region"
+msgstr "다음 편집 가능한 구역"
+
+msgctxt "A label of the dropdown that provides controls to navigate editable regions in the content."
+msgid "Navigate editable regions"
+msgstr "편집 가능한 구역 탐색"

+ 37 - 0
packages/ckeditor5-restricted-editing/lang/translations/zh.po

@@ -0,0 +1,37 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Chinese (Taiwan) (https://www.transifex.com/ckeditor/teams/11143/zh_TW/)\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "A label of the button indicating that using this button will make a selected text non–editable."
+msgid "Disable editing"
+msgstr "停用編輯"
+
+msgctxt "A label of the button indicating that using this button will make a selected text editable."
+msgid "Enable editing"
+msgstr "啟用編輯"
+
+msgctxt "A label of the button that moves selection to the previous editable region in the content."
+msgid "Previous editable region"
+msgstr "上一個可編輯區域"
+
+msgctxt "A label of the button that moves selection to the next editable region in the content."
+msgid "Next editable region"
+msgstr "下一個可編輯區域"
+
+msgctxt "A label of the dropdown that provides controls to navigate editable regions in the content."
+msgid "Navigate editable regions"
+msgstr "編輯區域導航"

+ 4 - 4
packages/ckeditor5-restricted-editing/src/restrictededitingmodeediting.js

@@ -161,8 +161,8 @@ export default class RestrictedEditingModeEditing extends Plugin {
 		// Additionally the editing pipeline should always display a collapsed markers.
 		// Additionally the editing pipeline should always display a collapsed markers.
 		editor.conversion.for( 'editingDowncast' ).markerToElement( {
 		editor.conversion.for( 'editingDowncast' ).markerToElement( {
 			model: 'restrictedEditingException',
 			model: 'restrictedEditingException',
-			view: ( markerData, viewWriter ) => {
-				return viewWriter.createUIElement( 'span', {
+			view: ( markerData, { writer } ) => {
+				return writer.createUIElement( 'span', {
 					class: 'restricted-editing-exception restricted-editing-exception_collapsed'
 					class: 'restricted-editing-exception restricted-editing-exception_collapsed'
 				} );
 				} );
 			}
 			}
@@ -170,8 +170,8 @@ export default class RestrictedEditingModeEditing extends Plugin {
 
 
 		editor.conversion.for( 'dataDowncast' ).markerToElement( {
 		editor.conversion.for( 'dataDowncast' ).markerToElement( {
 			model: 'restrictedEditingException',
 			model: 'restrictedEditingException',
-			view: ( markerData, viewWriter ) => {
-				return viewWriter.createEmptyElement( 'span', {
+			view: ( markerData, { writer } ) => {
+				return writer.createEmptyElement( 'span', {
 					class: 'restricted-editing-exception'
 					class: 'restricted-editing-exception'
 				} );
 				} );
 			}
 			}

+ 2 - 2
packages/ckeditor5-restricted-editing/src/standardeditingmodeediting.js

@@ -45,10 +45,10 @@ export default class StandardEditingModeEditing extends Plugin {
 
 
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 		editor.conversion.for( 'downcast' ).attributeToElement( {
 			model: 'restrictedEditingException',
 			model: 'restrictedEditingException',
-			view: ( modelAttributeValue, viewWriter ) => {
+			view: ( modelAttributeValue, { writer } ) => {
 				if ( modelAttributeValue ) {
 				if ( modelAttributeValue ) {
 					// Make the restricted editing <span> outer-most in the view.
 					// Make the restricted editing <span> outer-most in the view.
-					return viewWriter.createAttributeElement( 'span', { class: 'restricted-editing-exception' }, { priority: -10 } );
+					return writer.createAttributeElement( 'span', { class: 'restricted-editing-exception' }, { priority: -10 } );
 				}
 				}
 			}
 			}
 		} );
 		} );

+ 21 - 0
packages/ckeditor5-select-all/lang/translations/zh.po

@@ -0,0 +1,21 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Chinese (Taiwan) (https://www.transifex.com/ckeditor/teams/11143/zh_TW/)\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "The label of the select all toolbar button."
+msgid "Select all"
+msgstr "選取全部"

+ 1037 - 0
packages/ckeditor5-special-characters/lang/translations/ko.po

@@ -0,0 +1,1037 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Korean (https://www.transifex.com/ckeditor/teams/11143/ko/)\n"
+"Language: ko\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Name of the special characters plugins, visible in a dropdown and as a button tooltip."
+msgid "Special characters"
+msgstr "특수 문자"
+
+msgctxt "A label for the \"leftwards double arrow\" symbol."
+msgid "leftwards double arrow"
+msgstr ""
+
+msgctxt "A label for the \"rightwards double arrow\" symbol."
+msgid "rightwards double arrow"
+msgstr ""
+
+msgctxt "A label for the \"upwards double arrow\" symbol."
+msgid "upwards double arrow"
+msgstr ""
+
+msgctxt "A label for the \"downwards double arrow\" symbol."
+msgid "downwards double arrow"
+msgstr ""
+
+msgctxt "A label for the \"leftwards dashed arrow\" symbol."
+msgid "leftwards dashed arrow"
+msgstr ""
+
+msgctxt "A label for the \"rightwards dashed arrow\" symbol."
+msgid "rightwards dashed arrow"
+msgstr ""
+
+msgctxt "A label for the \"upwards dashed arrow\" symbol."
+msgid "upwards dashed arrow"
+msgstr ""
+
+msgctxt "A label for the \"downwards dashed arrow\" symbol."
+msgid "downwards dashed arrow"
+msgstr ""
+
+msgctxt "A label for the \"leftwards arrow to bar\" symbol."
+msgid "leftwards arrow to bar"
+msgstr ""
+
+msgctxt "A label for the \"rightwards arrow to bar\" symbol."
+msgid "rightwards arrow to bar"
+msgstr ""
+
+msgctxt "A label for the \"upwards arrow to bar\" symbol."
+msgid "upwards arrow to bar"
+msgstr ""
+
+msgctxt "A label for the \"downwards arrow to bar\" symbol."
+msgid "downwards arrow to bar"
+msgstr ""
+
+msgctxt "A label for the \"up down arrow with base\" symbol."
+msgid "up down arrow with base"
+msgstr ""
+
+msgctxt "A label for the \"back with leftwards arrow above\" symbol."
+msgid "back with leftwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"end with leftwards arrow above\" symbol."
+msgid "end with leftwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"on with exclamation mark with left right arrow above\" symbol."
+msgid "on with exclamation mark with left right arrow above"
+msgstr ""
+
+msgctxt "A label for the \"soon with rightwards arrow above\" symbol."
+msgid "soon with rightwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"top with upwards arrow above\" symbol."
+msgid "top with upwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"dollar sign\" symbol."
+msgid "Dollar sign"
+msgstr ""
+
+msgctxt "A label for the \"euro sign\" symbol."
+msgid "Euro sign"
+msgstr ""
+
+msgctxt "A label for the \"yen sign\" symbol."
+msgid "Yen sign"
+msgstr ""
+
+msgctxt "A label for the \"pound sign\" symbol."
+msgid "Pound sign"
+msgstr ""
+
+msgctxt "A label for the \"cent sign\" symbol."
+msgid "Cent sign"
+msgstr ""
+
+msgctxt "A label for the \"euro-currency sign\" symbol."
+msgid "Euro-currency sign"
+msgstr ""
+
+msgctxt "A label for the \"colon sign\" symbol."
+msgid "Colon sign"
+msgstr ""
+
+msgctxt "A label for the \"cruzeiro sign\" symbol."
+msgid "Cruzeiro sign"
+msgstr ""
+
+msgctxt "A label for the \"french franc sign\" symbol."
+msgid "French franc sign"
+msgstr ""
+
+msgctxt "A label for the \"lira sign\" symbol."
+msgid "Lira sign"
+msgstr ""
+
+msgctxt "A label for the \"currency sign\" symbol."
+msgid "Currency sign"
+msgstr ""
+
+msgctxt "A label for the \"bitcoin sign\" symbol."
+msgid "Bitcoin sign"
+msgstr ""
+
+msgctxt "A label for the \"mill sign\" symbol."
+msgid "Mill sign"
+msgstr ""
+
+msgctxt "A label for the \"naira sign\" symbol."
+msgid "Naira sign"
+msgstr ""
+
+msgctxt "A label for the \"peseta sign\" symbol."
+msgid "Peseta sign"
+msgstr ""
+
+msgctxt "A label for the \"rupee sign\" symbol."
+msgid "Rupee sign"
+msgstr ""
+
+msgctxt "A label for the \"won sign\" symbol."
+msgid "Won sign"
+msgstr ""
+
+msgctxt "A label for the \"new sheqel sign\" symbol."
+msgid "New sheqel sign"
+msgstr ""
+
+msgctxt "A label for the \"dong sign\" symbol."
+msgid "Dong sign"
+msgstr ""
+
+msgctxt "A label for the \"kip sign\" symbol."
+msgid "Kip sign"
+msgstr ""
+
+msgctxt "A label for the \"tugrik sign\" symbol."
+msgid "Tugrik sign"
+msgstr ""
+
+msgctxt "A label for the \"drachma sign\" symbol."
+msgid "Drachma sign"
+msgstr ""
+
+msgctxt "A label for the \"german penny sign\" symbol."
+msgid "German penny sign"
+msgstr ""
+
+msgctxt "A label for the \"peso sign\" symbol."
+msgid "Peso sign"
+msgstr ""
+
+msgctxt "A label for the \"guarani sign\" symbol."
+msgid "Guarani sign"
+msgstr ""
+
+msgctxt "A label for the \"austral sign\" symbol."
+msgid "Austral sign"
+msgstr ""
+
+msgctxt "A label for the \"hryvnia sign\" symbol."
+msgid "Hryvnia sign"
+msgstr ""
+
+msgctxt "A label for the \"cedi sign\" symbol."
+msgid "Cedi sign"
+msgstr ""
+
+msgctxt "A label for the \"livre tournois sign\" symbol."
+msgid "Livre tournois sign"
+msgstr ""
+
+msgctxt "A label for the \"spesmilo sign\" symbol."
+msgid "Spesmilo sign"
+msgstr ""
+
+msgctxt "A label for the \"tenge sign\" symbol."
+msgid "Tenge sign"
+msgstr ""
+
+msgctxt "A label for the \"indian rupee sign\" symbol."
+msgid "Indian rupee sign"
+msgstr ""
+
+msgctxt "A label for the \"turkish lira sign\" symbol."
+msgid "Turkish lira sign"
+msgstr ""
+
+msgctxt "A label for the \"nordic mark sign\" symbol."
+msgid "Nordic mark sign"
+msgstr ""
+
+msgctxt "A label for the \"manat sign\" symbol."
+msgid "Manat sign"
+msgstr ""
+
+msgctxt "A label for the \"ruble sign\" symbol."
+msgid "Ruble sign"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter a with macron\" symbol."
+msgid "Latin capital letter a with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter a with macron\" symbol."
+msgid "Latin small letter a with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter a with breve\" symbol."
+msgid "Latin capital letter a with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter a with breve\" symbol."
+msgid "Latin small letter a with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter a with ogonek\" symbol."
+msgid "Latin capital letter a with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter a with ogonek\" symbol."
+msgid "Latin small letter a with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with acute\" symbol."
+msgid "Latin capital letter c with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with acute\" symbol."
+msgid "Latin small letter c with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with circumflex\" symbol."
+msgid "Latin capital letter c with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with circumflex\" symbol."
+msgid "Latin small letter c with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with dot above\" symbol."
+msgid "Latin capital letter c with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with dot above\" symbol."
+msgid "Latin small letter c with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with caron\" symbol."
+msgid "Latin capital letter c with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with caron\" symbol."
+msgid "Latin small letter c with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter d with caron\" symbol."
+msgid "Latin capital letter d with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter d with caron\" symbol."
+msgid "Latin small letter d with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter d with stroke\" symbol."
+msgid "Latin capital letter d with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter d with stroke\" symbol."
+msgid "Latin small letter d with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with macron\" symbol."
+msgid "Latin capital letter e with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with macron\" symbol."
+msgid "Latin small letter e with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with breve\" symbol."
+msgid "Latin capital letter e with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with breve\" symbol."
+msgid "Latin small letter e with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with dot above\" symbol."
+msgid "Latin capital letter e with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with dot above\" symbol."
+msgid "Latin small letter e with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with ogonek\" symbol."
+msgid "Latin capital letter e with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with ogonek\" symbol."
+msgid "Latin small letter e with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with caron\" symbol."
+msgid "Latin capital letter e with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with caron\" symbol."
+msgid "Latin small letter e with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with circumflex\" symbol."
+msgid "Latin capital letter g with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with circumflex\" symbol."
+msgid "Latin small letter g with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with breve\" symbol."
+msgid "Latin capital letter g with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with breve\" symbol."
+msgid "Latin small letter g with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with dot above\" symbol."
+msgid "Latin capital letter g with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with dot above\" symbol."
+msgid "Latin small letter g with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with cedilla\" symbol."
+msgid "Latin capital letter g with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with cedilla\" symbol."
+msgid "Latin small letter g with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter h with circumflex\" symbol."
+msgid "Latin capital letter h with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter h with circumflex\" symbol."
+msgid "Latin small letter h with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter h with stroke\" symbol."
+msgid "Latin capital letter h with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter h with stroke\" symbol."
+msgid "Latin small letter h with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with tilde\" symbol."
+msgid "Latin capital letter i with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with tilde\" symbol."
+msgid "Latin small letter i with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with macron\" symbol."
+msgid "Latin capital letter i with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with macron\" symbol."
+msgid "Latin small letter i with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with breve\" symbol."
+msgid "Latin capital letter i with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with breve\" symbol."
+msgid "Latin small letter i with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with ogonek\" symbol."
+msgid "Latin capital letter i with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with ogonek\" symbol."
+msgid "Latin small letter i with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with dot above\" symbol."
+msgid "Latin capital letter i with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter dotless i\" symbol."
+msgid "Latin small letter dotless i"
+msgstr ""
+
+msgctxt "A label for the \"latin capital ligature ij\" symbol."
+msgid "Latin capital ligature ij"
+msgstr ""
+
+msgctxt "A label for the \"latin small ligature ij\" symbol."
+msgid "Latin small ligature ij"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter j with circumflex\" symbol."
+msgid "Latin capital letter j with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter j with circumflex\" symbol."
+msgid "Latin small letter j with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter k with cedilla\" symbol."
+msgid "Latin capital letter k with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter k with cedilla\" symbol."
+msgid "Latin small letter k with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter kra\" symbol."
+msgid "Latin small letter kra"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with acute\" symbol."
+msgid "Latin capital letter l with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with acute\" symbol."
+msgid "Latin small letter l with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with cedilla\" symbol."
+msgid "Latin capital letter l with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with cedilla\" symbol."
+msgid "Latin small letter l with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with caron\" symbol."
+msgid "Latin capital letter l with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with caron\" symbol."
+msgid "Latin small letter l with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with middle dot\" symbol."
+msgid "Latin capital letter l with middle dot"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with middle dot\" symbol."
+msgid "Latin small letter l with middle dot"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with stroke\" symbol."
+msgid "Latin capital letter l with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with stroke\" symbol."
+msgid "Latin small letter l with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter n with acute\" symbol."
+msgid "Latin capital letter n with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n with acute\" symbol."
+msgid "Latin small letter n with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter n with cedilla\" symbol."
+msgid "Latin capital letter n with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n with cedilla\" symbol."
+msgid "Latin small letter n with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter n with caron\" symbol."
+msgid "Latin capital letter n with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n with caron\" symbol."
+msgid "Latin small letter n with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n preceded by apostrophe\" symbol."
+msgid "Latin small letter n preceded by apostrophe"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter eng\" symbol."
+msgid "Latin capital letter eng"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter eng\" symbol."
+msgid "Latin small letter eng"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter o with macron\" symbol."
+msgid "Latin capital letter o with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter o with macron\" symbol."
+msgid "Latin small letter o with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter o with breve\" symbol."
+msgid "Latin capital letter o with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter o with breve\" symbol."
+msgid "Latin small letter o with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter o with double acute\" symbol."
+msgid "Latin capital letter o with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter o with double acute\" symbol."
+msgid "Latin small letter o with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital ligature oe\" symbol."
+msgid "Latin capital ligature oe"
+msgstr ""
+
+msgctxt "A label for the \"latin small ligature oe\" symbol."
+msgid "Latin small ligature oe"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter r with acute\" symbol."
+msgid "Latin capital letter r with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter r with acute\" symbol."
+msgid "Latin small letter r with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter r with cedilla\" symbol."
+msgid "Latin capital letter r with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter r with cedilla\" symbol."
+msgid "Latin small letter r with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter r with caron\" symbol."
+msgid "Latin capital letter r with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter r with caron\" symbol."
+msgid "Latin small letter r with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with acute\" symbol."
+msgid "Latin capital letter s with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with acute\" symbol."
+msgid "Latin small letter s with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with circumflex\" symbol."
+msgid "Latin capital letter s with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with circumflex\" symbol."
+msgid "Latin small letter s with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with cedilla\" symbol."
+msgid "Latin capital letter s with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with cedilla\" symbol."
+msgid "Latin small letter s with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with caron\" symbol."
+msgid "Latin capital letter s with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with caron\" symbol."
+msgid "Latin small letter s with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter t with cedilla\" symbol."
+msgid "Latin capital letter t with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter t with cedilla\" symbol."
+msgid "Latin small letter t with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter t with caron\" symbol."
+msgid "Latin capital letter t with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter t with caron\" symbol."
+msgid "Latin small letter t with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter t with stroke\" symbol."
+msgid "Latin capital letter t with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter t with stroke\" symbol."
+msgid "Latin small letter t with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with tilde\" symbol."
+msgid "Latin capital letter u with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with tilde\" symbol."
+msgid "Latin small letter u with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with macron\" symbol."
+msgid "Latin capital letter u with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with macron\" symbol."
+msgid "Latin small letter u with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with breve\" symbol."
+msgid "Latin capital letter u with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with breve\" symbol."
+msgid "Latin small letter u with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with ring above\" symbol."
+msgid "Latin capital letter u with ring above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with ring above\" symbol."
+msgid "Latin small letter u with ring above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with double acute\" symbol."
+msgid "Latin capital letter u with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with double acute\" symbol."
+msgid "Latin small letter u with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with ogonek\" symbol."
+msgid "Latin capital letter u with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with ogonek\" symbol."
+msgid "Latin small letter u with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter w with circumflex\" symbol."
+msgid "Latin capital letter w with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter w with circumflex\" symbol."
+msgid "Latin small letter w with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter y with circumflex\" symbol."
+msgid "Latin capital letter y with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter y with circumflex\" symbol."
+msgid "Latin small letter y with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter y with diaeresis\" symbol."
+msgid "Latin capital letter y with diaeresis"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter z with acute\" symbol."
+msgid "Latin capital letter z with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter z with acute\" symbol."
+msgid "Latin small letter z with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter z with dot above\" symbol."
+msgid "Latin capital letter z with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter z with dot above\" symbol."
+msgid "Latin small letter z with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter z with caron\" symbol."
+msgid "Latin capital letter z with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter z with caron\" symbol."
+msgid "Latin small letter z with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter long s\" symbol."
+msgid "Latin small letter long s"
+msgstr ""
+
+msgctxt "A label for the \"less-than sign\" symbol."
+msgid "Less-than sign"
+msgstr ""
+
+msgctxt "A label for the \"greater-than sign\" symbol."
+msgid "Greater-than sign"
+msgstr ""
+
+msgctxt "A label for the \"less-than or equal to\" symbol."
+msgid "Less-than or equal to"
+msgstr ""
+
+msgctxt "A label for the \"greater-than or equal to\" symbol."
+msgid "Greater-than or equal to"
+msgstr ""
+
+msgctxt "A label for the \"en dash\" symbol."
+msgid "En dash"
+msgstr ""
+
+msgctxt "A label for the \"em dash\" symbol."
+msgid "Em dash"
+msgstr ""
+
+msgctxt "A label for the \"macron\" symbol."
+msgid "Macron"
+msgstr ""
+
+msgctxt "A label for the \"overline\" symbol."
+msgid "Overline"
+msgstr ""
+
+msgctxt "A label for the \"degree sign\" symbol."
+msgid "Degree sign"
+msgstr ""
+
+msgctxt "A label for the \"minus sign\" symbol."
+msgid "Minus sign"
+msgstr ""
+
+msgctxt "A label for the \"plus-minus sign\" symbol."
+msgid "Plus-minus sign"
+msgstr ""
+
+msgctxt "A label for the \"division sign\" symbol."
+msgid "Division sign"
+msgstr ""
+
+msgctxt "A label for the \"fraction slash\" symbol."
+msgid "Fraction slash"
+msgstr ""
+
+msgctxt "A label for the \"multiplication sign\" symbol."
+msgid "Multiplication sign"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter f with hook\" symbol."
+msgid "Latin small letter f with hook"
+msgstr ""
+
+msgctxt "A label for the \"integral\" symbol."
+msgid "Integral"
+msgstr ""
+
+msgctxt "A label for the \"n-ary summation\" symbol."
+msgid "N-ary summation"
+msgstr ""
+
+msgctxt "A label for the \"infinity\" symbol."
+msgid "Infinity"
+msgstr ""
+
+msgctxt "A label for the \"square root\" symbol."
+msgid "Square root"
+msgstr ""
+
+msgctxt "A label for the \"tilde operator\" symbol."
+msgid "Tilde operator"
+msgstr ""
+
+msgctxt "A label for the \"approximately equal to\" symbol."
+msgid "Approximately equal to"
+msgstr ""
+
+msgctxt "A label for the \"almost equal to\" symbol."
+msgid "Almost equal to"
+msgstr ""
+
+msgctxt "A label for the \"not equal to\" symbol."
+msgid "Not equal to"
+msgstr ""
+
+msgctxt "A label for the \"identical to\" symbol."
+msgid "Identical to"
+msgstr ""
+
+msgctxt "A label for the \"element of\" symbol."
+msgid "Element of"
+msgstr ""
+
+msgctxt "A label for the \"not an element of\" symbol."
+msgid "Not an element of"
+msgstr ""
+
+msgctxt "A label for the \"contains as member\" symbol."
+msgid "Contains as member"
+msgstr ""
+
+msgctxt "A label for the \"n-ary product\" symbol."
+msgid "N-ary product"
+msgstr ""
+
+msgctxt "A label for the \"logical and\" symbol."
+msgid "Logical and"
+msgstr ""
+
+msgctxt "A label for the \"logical or\" symbol."
+msgid "Logical or"
+msgstr ""
+
+msgctxt "A label for the \"not sign\" symbol."
+msgid "Not sign"
+msgstr ""
+
+msgctxt "A label for the \"intersection\" symbol."
+msgid "Intersection"
+msgstr ""
+
+msgctxt "A label for the \"union\" symbol."
+msgid "Union"
+msgstr ""
+
+msgctxt "A label for the \"partial differential\" symbol."
+msgid "Partial differential"
+msgstr ""
+
+msgctxt "A label for the \"for all\" symbol."
+msgid "For all"
+msgstr ""
+
+msgctxt "A label for the \"there exists\" symbol."
+msgid "There exists"
+msgstr ""
+
+msgctxt "A label for the \"empty set\" symbol."
+msgid "Empty set"
+msgstr ""
+
+msgctxt "A label for the \"nabla\" symbol."
+msgid "Nabla"
+msgstr ""
+
+msgctxt "A label for the \"asterisk operator\" symbol."
+msgid "Asterisk operator"
+msgstr ""
+
+msgctxt "A label for the \"proportional to\" symbol."
+msgid "Proportional to"
+msgstr ""
+
+msgctxt "A label for the \"angle\" symbol."
+msgid "Angle"
+msgstr ""
+
+msgctxt "A label for the \"vulgar fraction one quarter\" symbol."
+msgid "Vulgar fraction one quarter"
+msgstr ""
+
+msgctxt "A label for the \"vulgar fraction one half\" symbol."
+msgid "Vulgar fraction one half"
+msgstr ""
+
+msgctxt "A label for the \"vulgar fraction three quarters\" symbol."
+msgid "Vulgar fraction three quarters"
+msgstr ""
+
+msgctxt "A label for the \"single left-pointing angle quotation mark\" symbol."
+msgid "Single left-pointing angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"single right-pointing angle quotation mark\" symbol."
+msgid "Single right-pointing angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"left-pointing double angle quotation mark\" symbol."
+msgid "Left-pointing double angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"right-pointing double angle quotation mark\" symbol."
+msgid "Right-pointing double angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"left single quotation mark\" symbol."
+msgid "Left single quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"right single quotation mark\" symbol."
+msgid "Right single quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"left double quotation mark\" symbol."
+msgid "Left double quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"right double quotation mark\" symbol."
+msgid "Right double quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"single low-9 quotation mark\" symbol."
+msgid "Single low-9 quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"double low-9 quotation mark\" symbol."
+msgid "Double low-9 quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"inverted exclamation mark\" symbol."
+msgid "Inverted exclamation mark"
+msgstr ""
+
+msgctxt "A label for the \"inverted question mark\" symbol."
+msgid "Inverted question mark"
+msgstr ""
+
+msgctxt "A label for the \"two dot leader\" symbol."
+msgid "Two dot leader"
+msgstr ""
+
+msgctxt "A label for the \"horizontal ellipsis\" symbol."
+msgid "Horizontal ellipsis"
+msgstr ""
+
+msgctxt "A label for the \"double dagger\" symbol."
+msgid "Double dagger"
+msgstr ""
+
+msgctxt "A label for the \"per mille sign\" symbol."
+msgid "Per mille sign"
+msgstr ""
+
+msgctxt "A label for the \"per ten thousand sign\" symbol."
+msgid "Per ten thousand sign"
+msgstr ""
+
+msgctxt "A label for the \"double exclamation mark\" symbol."
+msgid "Double exclamation mark"
+msgstr ""
+
+msgctxt "A label for the \"question exclamation mark\" symbol."
+msgid "Question exclamation mark"
+msgstr ""
+
+msgctxt "A label for the \"exclamation question mark\" symbol."
+msgid "Exclamation question mark"
+msgstr ""
+
+msgctxt "A label for the \"double question mark\" symbol."
+msgid "Double question mark"
+msgstr ""
+
+msgctxt "A label for the \"copyright sign\" symbol."
+msgid "Copyright sign"
+msgstr ""
+
+msgctxt "A label for the \"registered sign\" symbol."
+msgid "Registered sign"
+msgstr ""
+
+msgctxt "A label for the \"trade mark sign\" symbol."
+msgid "Trade mark sign"
+msgstr ""
+
+msgctxt "A label for the \"section sign\" symbol."
+msgid "Section sign"
+msgstr ""
+
+msgctxt "A label for the \"paragraph sign\" symbol."
+msgid "Paragraph sign"
+msgstr ""
+
+msgctxt "A label for the \"reversed paragraph sign\" symbol."
+msgid "Reversed paragraph sign"
+msgstr ""
+
+msgctxt "A label for a tooltip showing when the user hovers over the plugin icon."
+msgid "Character categories"
+msgstr ""

+ 1037 - 0
packages/ckeditor5-special-characters/lang/translations/zh.po

@@ -0,0 +1,1037 @@
+# Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
+#
+#                                     !!! IMPORTANT !!!
+#
+#         Before you edit this file, please keep in mind that contributing to the project
+#                translations is possible ONLY via the Transifex online service.
+#
+#         To submit your translations, visit https://www.transifex.com/ckeditor/ckeditor5.
+#
+#                   To learn more, check out the official contributor's guide:
+#     https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html
+#
+msgid ""
+msgstr ""
+"Language-Team: Chinese (Taiwan) (https://www.transifex.com/ckeditor/teams/11143/zh_TW/)\n"
+"Language: zh_TW\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Name of the special characters plugins, visible in a dropdown and as a button tooltip."
+msgid "Special characters"
+msgstr "特殊字元"
+
+msgctxt "A label for the \"leftwards double arrow\" symbol."
+msgid "leftwards double arrow"
+msgstr "向左雙箭頭"
+
+msgctxt "A label for the \"rightwards double arrow\" symbol."
+msgid "rightwards double arrow"
+msgstr "向右雙箭頭"
+
+msgctxt "A label for the \"upwards double arrow\" symbol."
+msgid "upwards double arrow"
+msgstr "向上雙箭頭"
+
+msgctxt "A label for the \"downwards double arrow\" symbol."
+msgid "downwards double arrow"
+msgstr "向下雙箭頭"
+
+msgctxt "A label for the \"leftwards dashed arrow\" symbol."
+msgid "leftwards dashed arrow"
+msgstr "向左虛線箭頭"
+
+msgctxt "A label for the \"rightwards dashed arrow\" symbol."
+msgid "rightwards dashed arrow"
+msgstr "向右虛線箭頭"
+
+msgctxt "A label for the \"upwards dashed arrow\" symbol."
+msgid "upwards dashed arrow"
+msgstr "向上虛線箭頭"
+
+msgctxt "A label for the \"downwards dashed arrow\" symbol."
+msgid "downwards dashed arrow"
+msgstr "向下虛線箭頭"
+
+msgctxt "A label for the \"leftwards arrow to bar\" symbol."
+msgid "leftwards arrow to bar"
+msgstr "向左停止箭頭"
+
+msgctxt "A label for the \"rightwards arrow to bar\" symbol."
+msgid "rightwards arrow to bar"
+msgstr "向右停止箭頭"
+
+msgctxt "A label for the \"upwards arrow to bar\" symbol."
+msgid "upwards arrow to bar"
+msgstr "向上停止箭頭"
+
+msgctxt "A label for the \"downwards arrow to bar\" symbol."
+msgid "downwards arrow to bar"
+msgstr "向下停止箭頭"
+
+msgctxt "A label for the \"up down arrow with base\" symbol."
+msgid "up down arrow with base"
+msgstr ""
+
+msgctxt "A label for the \"back with leftwards arrow above\" symbol."
+msgid "back with leftwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"end with leftwards arrow above\" symbol."
+msgid "end with leftwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"on with exclamation mark with left right arrow above\" symbol."
+msgid "on with exclamation mark with left right arrow above"
+msgstr ""
+
+msgctxt "A label for the \"soon with rightwards arrow above\" symbol."
+msgid "soon with rightwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"top with upwards arrow above\" symbol."
+msgid "top with upwards arrow above"
+msgstr ""
+
+msgctxt "A label for the \"dollar sign\" symbol."
+msgid "Dollar sign"
+msgstr ""
+
+msgctxt "A label for the \"euro sign\" symbol."
+msgid "Euro sign"
+msgstr ""
+
+msgctxt "A label for the \"yen sign\" symbol."
+msgid "Yen sign"
+msgstr ""
+
+msgctxt "A label for the \"pound sign\" symbol."
+msgid "Pound sign"
+msgstr ""
+
+msgctxt "A label for the \"cent sign\" symbol."
+msgid "Cent sign"
+msgstr ""
+
+msgctxt "A label for the \"euro-currency sign\" symbol."
+msgid "Euro-currency sign"
+msgstr ""
+
+msgctxt "A label for the \"colon sign\" symbol."
+msgid "Colon sign"
+msgstr ""
+
+msgctxt "A label for the \"cruzeiro sign\" symbol."
+msgid "Cruzeiro sign"
+msgstr ""
+
+msgctxt "A label for the \"french franc sign\" symbol."
+msgid "French franc sign"
+msgstr ""
+
+msgctxt "A label for the \"lira sign\" symbol."
+msgid "Lira sign"
+msgstr ""
+
+msgctxt "A label for the \"currency sign\" symbol."
+msgid "Currency sign"
+msgstr ""
+
+msgctxt "A label for the \"bitcoin sign\" symbol."
+msgid "Bitcoin sign"
+msgstr ""
+
+msgctxt "A label for the \"mill sign\" symbol."
+msgid "Mill sign"
+msgstr ""
+
+msgctxt "A label for the \"naira sign\" symbol."
+msgid "Naira sign"
+msgstr ""
+
+msgctxt "A label for the \"peseta sign\" symbol."
+msgid "Peseta sign"
+msgstr ""
+
+msgctxt "A label for the \"rupee sign\" symbol."
+msgid "Rupee sign"
+msgstr ""
+
+msgctxt "A label for the \"won sign\" symbol."
+msgid "Won sign"
+msgstr ""
+
+msgctxt "A label for the \"new sheqel sign\" symbol."
+msgid "New sheqel sign"
+msgstr ""
+
+msgctxt "A label for the \"dong sign\" symbol."
+msgid "Dong sign"
+msgstr ""
+
+msgctxt "A label for the \"kip sign\" symbol."
+msgid "Kip sign"
+msgstr ""
+
+msgctxt "A label for the \"tugrik sign\" symbol."
+msgid "Tugrik sign"
+msgstr ""
+
+msgctxt "A label for the \"drachma sign\" symbol."
+msgid "Drachma sign"
+msgstr ""
+
+msgctxt "A label for the \"german penny sign\" symbol."
+msgid "German penny sign"
+msgstr ""
+
+msgctxt "A label for the \"peso sign\" symbol."
+msgid "Peso sign"
+msgstr ""
+
+msgctxt "A label for the \"guarani sign\" symbol."
+msgid "Guarani sign"
+msgstr ""
+
+msgctxt "A label for the \"austral sign\" symbol."
+msgid "Austral sign"
+msgstr ""
+
+msgctxt "A label for the \"hryvnia sign\" symbol."
+msgid "Hryvnia sign"
+msgstr ""
+
+msgctxt "A label for the \"cedi sign\" symbol."
+msgid "Cedi sign"
+msgstr ""
+
+msgctxt "A label for the \"livre tournois sign\" symbol."
+msgid "Livre tournois sign"
+msgstr ""
+
+msgctxt "A label for the \"spesmilo sign\" symbol."
+msgid "Spesmilo sign"
+msgstr ""
+
+msgctxt "A label for the \"tenge sign\" symbol."
+msgid "Tenge sign"
+msgstr ""
+
+msgctxt "A label for the \"indian rupee sign\" symbol."
+msgid "Indian rupee sign"
+msgstr ""
+
+msgctxt "A label for the \"turkish lira sign\" symbol."
+msgid "Turkish lira sign"
+msgstr ""
+
+msgctxt "A label for the \"nordic mark sign\" symbol."
+msgid "Nordic mark sign"
+msgstr ""
+
+msgctxt "A label for the \"manat sign\" symbol."
+msgid "Manat sign"
+msgstr ""
+
+msgctxt "A label for the \"ruble sign\" symbol."
+msgid "Ruble sign"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter a with macron\" symbol."
+msgid "Latin capital letter a with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter a with macron\" symbol."
+msgid "Latin small letter a with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter a with breve\" symbol."
+msgid "Latin capital letter a with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter a with breve\" symbol."
+msgid "Latin small letter a with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter a with ogonek\" symbol."
+msgid "Latin capital letter a with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter a with ogonek\" symbol."
+msgid "Latin small letter a with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with acute\" symbol."
+msgid "Latin capital letter c with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with acute\" symbol."
+msgid "Latin small letter c with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with circumflex\" symbol."
+msgid "Latin capital letter c with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with circumflex\" symbol."
+msgid "Latin small letter c with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with dot above\" symbol."
+msgid "Latin capital letter c with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with dot above\" symbol."
+msgid "Latin small letter c with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter c with caron\" symbol."
+msgid "Latin capital letter c with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter c with caron\" symbol."
+msgid "Latin small letter c with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter d with caron\" symbol."
+msgid "Latin capital letter d with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter d with caron\" symbol."
+msgid "Latin small letter d with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter d with stroke\" symbol."
+msgid "Latin capital letter d with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter d with stroke\" symbol."
+msgid "Latin small letter d with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with macron\" symbol."
+msgid "Latin capital letter e with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with macron\" symbol."
+msgid "Latin small letter e with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with breve\" symbol."
+msgid "Latin capital letter e with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with breve\" symbol."
+msgid "Latin small letter e with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with dot above\" symbol."
+msgid "Latin capital letter e with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with dot above\" symbol."
+msgid "Latin small letter e with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with ogonek\" symbol."
+msgid "Latin capital letter e with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with ogonek\" symbol."
+msgid "Latin small letter e with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter e with caron\" symbol."
+msgid "Latin capital letter e with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter e with caron\" symbol."
+msgid "Latin small letter e with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with circumflex\" symbol."
+msgid "Latin capital letter g with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with circumflex\" symbol."
+msgid "Latin small letter g with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with breve\" symbol."
+msgid "Latin capital letter g with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with breve\" symbol."
+msgid "Latin small letter g with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with dot above\" symbol."
+msgid "Latin capital letter g with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with dot above\" symbol."
+msgid "Latin small letter g with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter g with cedilla\" symbol."
+msgid "Latin capital letter g with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter g with cedilla\" symbol."
+msgid "Latin small letter g with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter h with circumflex\" symbol."
+msgid "Latin capital letter h with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter h with circumflex\" symbol."
+msgid "Latin small letter h with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter h with stroke\" symbol."
+msgid "Latin capital letter h with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter h with stroke\" symbol."
+msgid "Latin small letter h with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with tilde\" symbol."
+msgid "Latin capital letter i with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with tilde\" symbol."
+msgid "Latin small letter i with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with macron\" symbol."
+msgid "Latin capital letter i with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with macron\" symbol."
+msgid "Latin small letter i with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with breve\" symbol."
+msgid "Latin capital letter i with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with breve\" symbol."
+msgid "Latin small letter i with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with ogonek\" symbol."
+msgid "Latin capital letter i with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter i with ogonek\" symbol."
+msgid "Latin small letter i with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter i with dot above\" symbol."
+msgid "Latin capital letter i with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter dotless i\" symbol."
+msgid "Latin small letter dotless i"
+msgstr ""
+
+msgctxt "A label for the \"latin capital ligature ij\" symbol."
+msgid "Latin capital ligature ij"
+msgstr ""
+
+msgctxt "A label for the \"latin small ligature ij\" symbol."
+msgid "Latin small ligature ij"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter j with circumflex\" symbol."
+msgid "Latin capital letter j with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter j with circumflex\" symbol."
+msgid "Latin small letter j with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter k with cedilla\" symbol."
+msgid "Latin capital letter k with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter k with cedilla\" symbol."
+msgid "Latin small letter k with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter kra\" symbol."
+msgid "Latin small letter kra"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with acute\" symbol."
+msgid "Latin capital letter l with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with acute\" symbol."
+msgid "Latin small letter l with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with cedilla\" symbol."
+msgid "Latin capital letter l with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with cedilla\" symbol."
+msgid "Latin small letter l with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with caron\" symbol."
+msgid "Latin capital letter l with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with caron\" symbol."
+msgid "Latin small letter l with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with middle dot\" symbol."
+msgid "Latin capital letter l with middle dot"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with middle dot\" symbol."
+msgid "Latin small letter l with middle dot"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter l with stroke\" symbol."
+msgid "Latin capital letter l with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter l with stroke\" symbol."
+msgid "Latin small letter l with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter n with acute\" symbol."
+msgid "Latin capital letter n with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n with acute\" symbol."
+msgid "Latin small letter n with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter n with cedilla\" symbol."
+msgid "Latin capital letter n with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n with cedilla\" symbol."
+msgid "Latin small letter n with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter n with caron\" symbol."
+msgid "Latin capital letter n with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n with caron\" symbol."
+msgid "Latin small letter n with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter n preceded by apostrophe\" symbol."
+msgid "Latin small letter n preceded by apostrophe"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter eng\" symbol."
+msgid "Latin capital letter eng"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter eng\" symbol."
+msgid "Latin small letter eng"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter o with macron\" symbol."
+msgid "Latin capital letter o with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter o with macron\" symbol."
+msgid "Latin small letter o with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter o with breve\" symbol."
+msgid "Latin capital letter o with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter o with breve\" symbol."
+msgid "Latin small letter o with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter o with double acute\" symbol."
+msgid "Latin capital letter o with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter o with double acute\" symbol."
+msgid "Latin small letter o with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital ligature oe\" symbol."
+msgid "Latin capital ligature oe"
+msgstr ""
+
+msgctxt "A label for the \"latin small ligature oe\" symbol."
+msgid "Latin small ligature oe"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter r with acute\" symbol."
+msgid "Latin capital letter r with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter r with acute\" symbol."
+msgid "Latin small letter r with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter r with cedilla\" symbol."
+msgid "Latin capital letter r with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter r with cedilla\" symbol."
+msgid "Latin small letter r with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter r with caron\" symbol."
+msgid "Latin capital letter r with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter r with caron\" symbol."
+msgid "Latin small letter r with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with acute\" symbol."
+msgid "Latin capital letter s with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with acute\" symbol."
+msgid "Latin small letter s with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with circumflex\" symbol."
+msgid "Latin capital letter s with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with circumflex\" symbol."
+msgid "Latin small letter s with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with cedilla\" symbol."
+msgid "Latin capital letter s with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with cedilla\" symbol."
+msgid "Latin small letter s with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter s with caron\" symbol."
+msgid "Latin capital letter s with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter s with caron\" symbol."
+msgid "Latin small letter s with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter t with cedilla\" symbol."
+msgid "Latin capital letter t with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter t with cedilla\" symbol."
+msgid "Latin small letter t with cedilla"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter t with caron\" symbol."
+msgid "Latin capital letter t with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter t with caron\" symbol."
+msgid "Latin small letter t with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter t with stroke\" symbol."
+msgid "Latin capital letter t with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter t with stroke\" symbol."
+msgid "Latin small letter t with stroke"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with tilde\" symbol."
+msgid "Latin capital letter u with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with tilde\" symbol."
+msgid "Latin small letter u with tilde"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with macron\" symbol."
+msgid "Latin capital letter u with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with macron\" symbol."
+msgid "Latin small letter u with macron"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with breve\" symbol."
+msgid "Latin capital letter u with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with breve\" symbol."
+msgid "Latin small letter u with breve"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with ring above\" symbol."
+msgid "Latin capital letter u with ring above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with ring above\" symbol."
+msgid "Latin small letter u with ring above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with double acute\" symbol."
+msgid "Latin capital letter u with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with double acute\" symbol."
+msgid "Latin small letter u with double acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter u with ogonek\" symbol."
+msgid "Latin capital letter u with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter u with ogonek\" symbol."
+msgid "Latin small letter u with ogonek"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter w with circumflex\" symbol."
+msgid "Latin capital letter w with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter w with circumflex\" symbol."
+msgid "Latin small letter w with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter y with circumflex\" symbol."
+msgid "Latin capital letter y with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter y with circumflex\" symbol."
+msgid "Latin small letter y with circumflex"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter y with diaeresis\" symbol."
+msgid "Latin capital letter y with diaeresis"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter z with acute\" symbol."
+msgid "Latin capital letter z with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter z with acute\" symbol."
+msgid "Latin small letter z with acute"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter z with dot above\" symbol."
+msgid "Latin capital letter z with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter z with dot above\" symbol."
+msgid "Latin small letter z with dot above"
+msgstr ""
+
+msgctxt "A label for the \"latin capital letter z with caron\" symbol."
+msgid "Latin capital letter z with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter z with caron\" symbol."
+msgid "Latin small letter z with caron"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter long s\" symbol."
+msgid "Latin small letter long s"
+msgstr ""
+
+msgctxt "A label for the \"less-than sign\" symbol."
+msgid "Less-than sign"
+msgstr ""
+
+msgctxt "A label for the \"greater-than sign\" symbol."
+msgid "Greater-than sign"
+msgstr ""
+
+msgctxt "A label for the \"less-than or equal to\" symbol."
+msgid "Less-than or equal to"
+msgstr ""
+
+msgctxt "A label for the \"greater-than or equal to\" symbol."
+msgid "Greater-than or equal to"
+msgstr ""
+
+msgctxt "A label for the \"en dash\" symbol."
+msgid "En dash"
+msgstr ""
+
+msgctxt "A label for the \"em dash\" symbol."
+msgid "Em dash"
+msgstr ""
+
+msgctxt "A label for the \"macron\" symbol."
+msgid "Macron"
+msgstr ""
+
+msgctxt "A label for the \"overline\" symbol."
+msgid "Overline"
+msgstr ""
+
+msgctxt "A label for the \"degree sign\" symbol."
+msgid "Degree sign"
+msgstr ""
+
+msgctxt "A label for the \"minus sign\" symbol."
+msgid "Minus sign"
+msgstr ""
+
+msgctxt "A label for the \"plus-minus sign\" symbol."
+msgid "Plus-minus sign"
+msgstr ""
+
+msgctxt "A label for the \"division sign\" symbol."
+msgid "Division sign"
+msgstr ""
+
+msgctxt "A label for the \"fraction slash\" symbol."
+msgid "Fraction slash"
+msgstr ""
+
+msgctxt "A label for the \"multiplication sign\" symbol."
+msgid "Multiplication sign"
+msgstr ""
+
+msgctxt "A label for the \"latin small letter f with hook\" symbol."
+msgid "Latin small letter f with hook"
+msgstr ""
+
+msgctxt "A label for the \"integral\" symbol."
+msgid "Integral"
+msgstr ""
+
+msgctxt "A label for the \"n-ary summation\" symbol."
+msgid "N-ary summation"
+msgstr ""
+
+msgctxt "A label for the \"infinity\" symbol."
+msgid "Infinity"
+msgstr ""
+
+msgctxt "A label for the \"square root\" symbol."
+msgid "Square root"
+msgstr ""
+
+msgctxt "A label for the \"tilde operator\" symbol."
+msgid "Tilde operator"
+msgstr ""
+
+msgctxt "A label for the \"approximately equal to\" symbol."
+msgid "Approximately equal to"
+msgstr ""
+
+msgctxt "A label for the \"almost equal to\" symbol."
+msgid "Almost equal to"
+msgstr ""
+
+msgctxt "A label for the \"not equal to\" symbol."
+msgid "Not equal to"
+msgstr ""
+
+msgctxt "A label for the \"identical to\" symbol."
+msgid "Identical to"
+msgstr ""
+
+msgctxt "A label for the \"element of\" symbol."
+msgid "Element of"
+msgstr ""
+
+msgctxt "A label for the \"not an element of\" symbol."
+msgid "Not an element of"
+msgstr ""
+
+msgctxt "A label for the \"contains as member\" symbol."
+msgid "Contains as member"
+msgstr ""
+
+msgctxt "A label for the \"n-ary product\" symbol."
+msgid "N-ary product"
+msgstr ""
+
+msgctxt "A label for the \"logical and\" symbol."
+msgid "Logical and"
+msgstr ""
+
+msgctxt "A label for the \"logical or\" symbol."
+msgid "Logical or"
+msgstr ""
+
+msgctxt "A label for the \"not sign\" symbol."
+msgid "Not sign"
+msgstr ""
+
+msgctxt "A label for the \"intersection\" symbol."
+msgid "Intersection"
+msgstr ""
+
+msgctxt "A label for the \"union\" symbol."
+msgid "Union"
+msgstr ""
+
+msgctxt "A label for the \"partial differential\" symbol."
+msgid "Partial differential"
+msgstr ""
+
+msgctxt "A label for the \"for all\" symbol."
+msgid "For all"
+msgstr ""
+
+msgctxt "A label for the \"there exists\" symbol."
+msgid "There exists"
+msgstr ""
+
+msgctxt "A label for the \"empty set\" symbol."
+msgid "Empty set"
+msgstr ""
+
+msgctxt "A label for the \"nabla\" symbol."
+msgid "Nabla"
+msgstr ""
+
+msgctxt "A label for the \"asterisk operator\" symbol."
+msgid "Asterisk operator"
+msgstr ""
+
+msgctxt "A label for the \"proportional to\" symbol."
+msgid "Proportional to"
+msgstr ""
+
+msgctxt "A label for the \"angle\" symbol."
+msgid "Angle"
+msgstr ""
+
+msgctxt "A label for the \"vulgar fraction one quarter\" symbol."
+msgid "Vulgar fraction one quarter"
+msgstr ""
+
+msgctxt "A label for the \"vulgar fraction one half\" symbol."
+msgid "Vulgar fraction one half"
+msgstr ""
+
+msgctxt "A label for the \"vulgar fraction three quarters\" symbol."
+msgid "Vulgar fraction three quarters"
+msgstr ""
+
+msgctxt "A label for the \"single left-pointing angle quotation mark\" symbol."
+msgid "Single left-pointing angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"single right-pointing angle quotation mark\" symbol."
+msgid "Single right-pointing angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"left-pointing double angle quotation mark\" symbol."
+msgid "Left-pointing double angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"right-pointing double angle quotation mark\" symbol."
+msgid "Right-pointing double angle quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"left single quotation mark\" symbol."
+msgid "Left single quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"right single quotation mark\" symbol."
+msgid "Right single quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"left double quotation mark\" symbol."
+msgid "Left double quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"right double quotation mark\" symbol."
+msgid "Right double quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"single low-9 quotation mark\" symbol."
+msgid "Single low-9 quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"double low-9 quotation mark\" symbol."
+msgid "Double low-9 quotation mark"
+msgstr ""
+
+msgctxt "A label for the \"inverted exclamation mark\" symbol."
+msgid "Inverted exclamation mark"
+msgstr ""
+
+msgctxt "A label for the \"inverted question mark\" symbol."
+msgid "Inverted question mark"
+msgstr ""
+
+msgctxt "A label for the \"two dot leader\" symbol."
+msgid "Two dot leader"
+msgstr ""
+
+msgctxt "A label for the \"horizontal ellipsis\" symbol."
+msgid "Horizontal ellipsis"
+msgstr ""
+
+msgctxt "A label for the \"double dagger\" symbol."
+msgid "Double dagger"
+msgstr ""
+
+msgctxt "A label for the \"per mille sign\" symbol."
+msgid "Per mille sign"
+msgstr ""
+
+msgctxt "A label for the \"per ten thousand sign\" symbol."
+msgid "Per ten thousand sign"
+msgstr ""
+
+msgctxt "A label for the \"double exclamation mark\" symbol."
+msgid "Double exclamation mark"
+msgstr ""
+
+msgctxt "A label for the \"question exclamation mark\" symbol."
+msgid "Question exclamation mark"
+msgstr ""
+
+msgctxt "A label for the \"exclamation question mark\" symbol."
+msgid "Exclamation question mark"
+msgstr ""
+
+msgctxt "A label for the \"double question mark\" symbol."
+msgid "Double question mark"
+msgstr ""
+
+msgctxt "A label for the \"copyright sign\" symbol."
+msgid "Copyright sign"
+msgstr ""
+
+msgctxt "A label for the \"registered sign\" symbol."
+msgid "Registered sign"
+msgstr ""
+
+msgctxt "A label for the \"trade mark sign\" symbol."
+msgid "Trade mark sign"
+msgstr ""
+
+msgctxt "A label for the \"section sign\" symbol."
+msgid "Section sign"
+msgstr ""
+
+msgctxt "A label for the \"paragraph sign\" symbol."
+msgid "Paragraph sign"
+msgstr ""
+
+msgctxt "A label for the \"reversed paragraph sign\" symbol."
+msgid "Reversed paragraph sign"
+msgstr ""
+
+msgctxt "A label for a tooltip showing when the user hovers over the plugin icon."
+msgid "Character categories"
+msgstr ""

+ 40 - 40
packages/ckeditor5-table/lang/translations/zh.po

@@ -38,7 +38,7 @@ msgstr "刪除欄"
 
 
 msgctxt "Label for the select the entire table column button."
 msgctxt "Label for the select the entire table column button."
 msgid "Select column"
 msgid "Select column"
-msgstr ""
+msgstr "選擇欄"
 
 
 msgctxt "Label for the table column dropdown button."
 msgctxt "Label for the table column dropdown button."
 msgid "Column"
 msgid "Column"
@@ -62,7 +62,7 @@ msgstr "刪除列"
 
 
 msgctxt "Label for the select the entire table row button."
 msgctxt "Label for the select the entire table row button."
 msgid "Select row"
 msgid "Select row"
-msgstr ""
+msgstr "選擇列"
 
 
 msgctxt "Label for the table row dropdown button."
 msgctxt "Label for the table row dropdown button."
 msgid "Row"
 msgid "Row"
@@ -98,152 +98,152 @@ msgstr "合併儲存格"
 
 
 msgctxt "The label used by assistive technologies describing a table toolbar attached to a table widget."
 msgctxt "The label used by assistive technologies describing a table toolbar attached to a table widget."
 msgid "Table toolbar"
 msgid "Table toolbar"
-msgstr ""
+msgstr "表格工具"
 
 
 msgctxt "The label describing the form allowing to specify the properties of a selected table."
 msgctxt "The label describing the form allowing to specify the properties of a selected table."
 msgid "Table properties"
 msgid "Table properties"
-msgstr ""
+msgstr "表格屬性"
 
 
 msgctxt "The label describing the form allowing to specify the properties of a selected table cell."
 msgctxt "The label describing the form allowing to specify the properties of a selected table cell."
 msgid "Cell properties"
 msgid "Cell properties"
-msgstr ""
+msgstr "儲存格屬性"
 
 
 msgctxt "The label describing a group of border–related form fields (border style, color, etc.)."
 msgctxt "The label describing a group of border–related form fields (border style, color, etc.)."
 msgid "Border"
 msgid "Border"
-msgstr ""
+msgstr "邊框"
 
 
 msgctxt "The label for the dropdown that allows configuring the border style of a table or a table cell."
 msgctxt "The label for the dropdown that allows configuring the border style of a table or a table cell."
 msgid "Style"
 msgid "Style"
-msgstr ""
+msgstr "樣式"
 
 
 msgctxt "The label for the input that allows configuring the width of a table or a table cell or the width of a border."
 msgctxt "The label for the input that allows configuring the width of a table or a table cell or the width of a border."
 msgid "Width"
 msgid "Width"
-msgstr ""
+msgstr "寬度"
 
 
 msgctxt "The label for the input that allows configuring the height of a table or a table cell."
 msgctxt "The label for the input that allows configuring the height of a table or a table cell."
 msgid "Height"
 msgid "Height"
-msgstr ""
+msgstr "高度"
 
 
 msgctxt "The label for the input that allows configuring the border color of a table or a table cell."
 msgctxt "The label for the input that allows configuring the border color of a table or a table cell."
 msgid "Color"
 msgid "Color"
-msgstr ""
+msgstr "文字顏色"
 
 
 msgctxt "The label for the input that allows configuring the background color of a table or a table cell."
 msgctxt "The label for the input that allows configuring the background color of a table or a table cell."
 msgid "Background"
 msgid "Background"
-msgstr ""
+msgstr "背景顏色"
 
 
 msgctxt "The label for the input that allows configuring the padding of a table cell."
 msgctxt "The label for the input that allows configuring the padding of a table cell."
 msgid "Padding"
 msgid "Padding"
-msgstr ""
+msgstr "儲存格留白"
 
 
 msgctxt "The label describing a group of form fields that allows setting dimensions of a table or a table cell."
 msgctxt "The label describing a group of form fields that allows setting dimensions of a table or a table cell."
 msgid "Dimensions"
 msgid "Dimensions"
-msgstr ""
+msgstr "尺寸"
 
 
 msgctxt "The label for the group of toolbars that allows configuring the text alignment in a table cell."
 msgctxt "The label for the group of toolbars that allows configuring the text alignment in a table cell."
 msgid "Table cell text alignment"
 msgid "Table cell text alignment"
-msgstr ""
+msgstr "儲存格文字對齊"
 
 
 msgctxt "The label for the toolbar that allows configuring the alignment of a table."
 msgctxt "The label for the toolbar that allows configuring the alignment of a table."
 msgid "Alignment"
 msgid "Alignment"
-msgstr ""
+msgstr "對齊"
 
 
 msgctxt "The label used by assistive technologies describing a toolbar that allows configuring the horizontal text alignment in a table cell."
 msgctxt "The label used by assistive technologies describing a toolbar that allows configuring the horizontal text alignment in a table cell."
 msgid "Horizontal text alignment toolbar"
 msgid "Horizontal text alignment toolbar"
-msgstr ""
+msgstr "水平對齊"
 
 
 msgctxt "The label used by assistive technologies describing a toolbar that allows configuring the vertical text alignment in a table cell."
 msgctxt "The label used by assistive technologies describing a toolbar that allows configuring the vertical text alignment in a table cell."
 msgid "Vertical text alignment toolbar"
 msgid "Vertical text alignment toolbar"
-msgstr ""
+msgstr "垂直對齊"
 
 
 msgctxt "The label used by assistive technologies describing a toolbar that allows configuring the alignment of a table."
 msgctxt "The label used by assistive technologies describing a toolbar that allows configuring the alignment of a table."
 msgid "Table alignment toolbar"
 msgid "Table alignment toolbar"
-msgstr ""
+msgstr "表格對齊"
 
 
 msgctxt "The label for the border style dropdown when no style is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when no style is applied to a table or a table cell."
 msgid "None"
 msgid "None"
-msgstr ""
+msgstr ""
 
 
 msgctxt "The label for the border style dropdown when the solid border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the solid border is applied to a table or a table cell."
 msgid "Solid"
 msgid "Solid"
-msgstr ""
+msgstr "實線"
 
 
 msgctxt "The label for the border style dropdown when the dotted border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the dotted border is applied to a table or a table cell."
 msgid "Dotted"
 msgid "Dotted"
-msgstr ""
+msgstr "點線"
 
 
 msgctxt "The label for the border style dropdown when the dashed border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the dashed border is applied to a table or a table cell."
 msgid "Dashed"
 msgid "Dashed"
-msgstr ""
+msgstr "虛線"
 
 
 msgctxt "The label for the border style dropdown when the double border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the double border is applied to a table or a table cell."
 msgid "Double"
 msgid "Double"
-msgstr ""
+msgstr "雙線"
 
 
 msgctxt "The label for the border style dropdown when the groove border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the groove border is applied to a table or a table cell."
 msgid "Groove"
 msgid "Groove"
-msgstr ""
+msgstr "內凹線"
 
 
 msgctxt "The label for the border style dropdown when the ridge border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the ridge border is applied to a table or a table cell."
 msgid "Ridge"
 msgid "Ridge"
-msgstr ""
+msgstr "凸起線"
 
 
 msgctxt "The label for the border style dropdown when the inset border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the inset border is applied to a table or a table cell."
 msgid "Inset"
 msgid "Inset"
-msgstr ""
+msgstr "內邊線"
 
 
 msgctxt "The label for the border style dropdown when the outset border is applied to a table or a table cell."
 msgctxt "The label for the border style dropdown when the outset border is applied to a table or a table cell."
 msgid "Outset"
 msgid "Outset"
-msgstr ""
+msgstr "外框線"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the left."
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the left."
 msgid "Align cell text to the left"
 msgid "Align cell text to the left"
-msgstr ""
+msgstr "靠左對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the center."
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the center."
 msgid "Align cell text to the center"
 msgid "Align cell text to the center"
-msgstr ""
+msgstr "置中對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the right."
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the right."
 msgid "Align cell text to the right"
 msgid "Align cell text to the right"
-msgstr ""
+msgstr "靠右對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that justifies the table cell text."
 msgctxt "The label used by assistive technologies describing a button that justifies the table cell text."
 msgid "Justify cell text"
 msgid "Justify cell text"
-msgstr ""
+msgstr "分散對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the top."
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the top."
 msgid "Align cell text to the top"
 msgid "Align cell text to the top"
-msgstr ""
+msgstr "向上對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the middle."
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the middle."
 msgid "Align cell text to the middle"
 msgid "Align cell text to the middle"
-msgstr ""
+msgstr "置中對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the bottom."
 msgctxt "The label used by assistive technologies describing a button that aligns the table cell text to the bottom."
 msgid "Align cell text to the bottom"
 msgid "Align cell text to the bottom"
-msgstr ""
+msgstr "向下對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table to the left."
 msgctxt "The label used by assistive technologies describing a button that aligns the table to the left."
 msgid "Align table to the left"
 msgid "Align table to the left"
-msgstr ""
+msgstr "靠左對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that centers the table."
 msgctxt "The label used by assistive technologies describing a button that centers the table."
 msgid "Center table"
 msgid "Center table"
-msgstr ""
+msgstr "置中對齊"
 
 
 msgctxt "The label used by assistive technologies describing a button that aligns the table to the right."
 msgctxt "The label used by assistive technologies describing a button that aligns the table to the right."
 msgid "Align table to the right"
 msgid "Align table to the right"
-msgstr ""
+msgstr "靠右對齊"
 
 
 msgctxt "The localized error string that can be displayed next to color (background, border) fields that have an invalid value"
 msgctxt "The localized error string that can be displayed next to color (background, border) fields that have an invalid value"
 msgid "The color is invalid. Try \"#FF0000\" or \"rgb(255,0,0)\" or \"red\"."
 msgid "The color is invalid. Try \"#FF0000\" or \"rgb(255,0,0)\" or \"red\"."
-msgstr ""
+msgstr "顏色代碼錯誤。試試看 \"#FF0000\" 或 \"rgb(255, 0, 0)\" 或 \"red\"。"
 
 
 msgctxt "The localized error string that can be displayed next to length (padding, border width) fields that have an invalid value."
 msgctxt "The localized error string that can be displayed next to length (padding, border width) fields that have an invalid value."
 msgid "The value is invalid. Try \"10px\" or \"2em\" or simply \"2\"."
 msgid "The value is invalid. Try \"10px\" or \"2em\" or simply \"2\"."
-msgstr ""
+msgstr "尺寸代碼錯誤。試試看 \"10px\" 或 \"2em\" 或簡單寫 \"2\"。"
 
 
 msgctxt "The label used by assistive technologies describing a button that opens a color picker, where user can choose a configured color for a certain properties (eg.: background color, color, border-color etc.)."
 msgctxt "The label used by assistive technologies describing a button that opens a color picker, where user can choose a configured color for a certain properties (eg.: background color, color, border-color etc.)."
 msgid "Color picker"
 msgid "Color picker"
-msgstr ""
+msgstr "顏色選擇"

+ 2 - 2
packages/ckeditor5-table/src/tableediting.js

@@ -64,7 +64,6 @@ export default class TableEditing extends Plugin {
 		schema.register( 'table', {
 		schema.register( 'table', {
 			allowWhere: '$block',
 			allowWhere: '$block',
 			allowAttributes: [ 'headingRows', 'headingColumns' ],
 			allowAttributes: [ 'headingRows', 'headingColumns' ],
-			isLimit: true,
 			isObject: true,
 			isObject: true,
 			isBlock: true
 			isBlock: true
 		} );
 		} );
@@ -77,7 +76,8 @@ export default class TableEditing extends Plugin {
 		schema.register( 'tableCell', {
 		schema.register( 'tableCell', {
 			allowIn: 'tableRow',
 			allowIn: 'tableRow',
 			allowAttributes: [ 'colspan', 'rowspan' ],
 			allowAttributes: [ 'colspan', 'rowspan' ],
-			isObject: true
+			isLimit: true,
+			isSelectable: true
 		} );
 		} );
 
 
 		// Allow all $block content inside table cell.
 		// Allow all $block content inside table cell.

+ 42 - 0
packages/ckeditor5-table/tests/converters/upcasttable.js

@@ -500,6 +500,48 @@ describe( 'upcastTable()', () => {
 		} );
 		} );
 	} );
 	} );
 
 
+	describe( 'inline contents', () => {
+		it( 'should upcast inline element inside a table cell', () => {
+			model.schema.register( 'inline', { allowWhere: '$text', isInline: true } );
+			model.schema.extend( '$text', { allowIn: 'inline' } );
+			editor.conversion.elementToElement( { model: 'inline', view: 'span' } );
+
+			editor.setData(
+				'<table>' +
+					'<tr>' +
+						'<td>' +
+							'<span>foo</span>' +
+						'</td>' +
+					'</tr>' +
+				'</table>'
+			);
+
+			expectModel( modelTable( [
+				[ '<paragraph><inline>foo</inline></paragraph>' ]
+			] ) );
+		} );
+
+		it( 'should upcast inline object inside a table cell', () => {
+			model.schema.register( 'inline', { allowWhere: '$text', isInline: true, isObject: true } );
+			model.schema.extend( '$text', { allowIn: 'inline' } );
+			editor.conversion.elementToElement( { model: 'inline', view: 'span' } );
+
+			editor.setData(
+				'<table>' +
+					'<tr>' +
+						'<td>' +
+							'<span>foo</span>' +
+						'</td>' +
+					'</tr>' +
+				'</table>'
+			);
+
+			expectModel( modelTable( [
+				[ '<paragraph><inline>foo</inline></paragraph>' ]
+			] ) );
+		} );
+	} );
+
 	describe( 'handling redundant whitespacing between table elements', () => {
 	describe( 'handling redundant whitespacing between table elements', () => {
 		it( 'table without thead/tbody/tfoot', () => {
 		it( 'table without thead/tbody/tfoot', () => {
 			editor.setData(
 			editor.setData(

+ 10 - 0
packages/ckeditor5-table/tests/table-integration.js

@@ -185,5 +185,15 @@ describe( 'Table feature – integration', () => {
 				[ '<blockQuote><paragraph>Foo[]Bar</paragraph></blockQuote>' ]
 				[ '<blockQuote><paragraph>Foo[]Bar</paragraph></blockQuote>' ]
 			] ) );
 			] ) );
 		} );
 		} );
+
+		it( 'should not make the Model#hasContent() method return "true" when an empty table cell is selected', () => {
+			setModelData( editor.model, '<table>' +
+				'<tableRow>' +
+					'[<tableCell><paragraph></paragraph></tableCell>]' +
+				'</tableRow>' +
+			'</table>' );
+
+			expect( editor.model.hasContent( editor.model.document.selection.getFirstRange() ) ).to.be.false;
+		} );
 	} );
 	} );
 } );
 } );

+ 2 - 1
packages/ckeditor5-table/tests/tableediting.js

@@ -52,7 +52,6 @@ describe( 'TableEditing', () => {
 		expect( model.schema.isRegistered( 'table' ) ).to.be.true;
 		expect( model.schema.isRegistered( 'table' ) ).to.be.true;
 		expect( model.schema.isObject( 'table' ) ).to.be.true;
 		expect( model.schema.isObject( 'table' ) ).to.be.true;
 		expect( model.schema.isBlock( 'table' ) ).to.be.true;
 		expect( model.schema.isBlock( 'table' ) ).to.be.true;
-		expect( model.schema.isLimit( 'table' ) ).to.be.true;
 
 
 		expect( model.schema.checkChild( [ '$root' ], 'table' ) ).to.be.true;
 		expect( model.schema.checkChild( [ '$root' ], 'table' ) ).to.be.true;
 		expect( model.schema.checkAttribute( [ '$root', 'table' ], 'headingRows' ) ).to.be.true;
 		expect( model.schema.checkAttribute( [ '$root', 'table' ], 'headingRows' ) ).to.be.true;
@@ -68,6 +67,8 @@ describe( 'TableEditing', () => {
 		// Table cell:
 		// Table cell:
 		expect( model.schema.isRegistered( 'tableCell' ) ).to.be.true;
 		expect( model.schema.isRegistered( 'tableCell' ) ).to.be.true;
 		expect( model.schema.isLimit( 'tableCell' ) ).to.be.true;
 		expect( model.schema.isLimit( 'tableCell' ) ).to.be.true;
+		expect( model.schema.isObject( 'tableCell' ) ).to.be.false;
+		expect( model.schema.isSelectable( 'tableCell' ) ).to.be.true;
 
 
 		expect( model.schema.checkChild( [ '$root' ], 'tableCell' ) ).to.be.false;
 		expect( model.schema.checkChild( [ '$root' ], 'tableCell' ) ).to.be.false;
 		expect( model.schema.checkChild( [ 'table' ], 'tableCell' ) ).to.be.false;
 		expect( model.schema.checkChild( [ 'table' ], 'tableCell' ) ).to.be.false;

+ 57 - 48
packages/ckeditor5-table/tests/tablekeyboard.js

@@ -14,12 +14,11 @@ import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtest
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
 import ImageEditing from '@ckeditor/ckeditor5-image/src/image/imageediting';
 import MediaEmbedEditing from '@ckeditor/ckeditor5-media-embed/src/mediaembedediting';
 import MediaEmbedEditing from '@ckeditor/ckeditor5-media-embed/src/mediaembedediting';
-import ImageCaptionEditing from '@ckeditor/ckeditor5-image/src/imagecaption/imagecaptionediting';
-import HorizontalLineEditing from '@ckeditor/ckeditor5-horizontal-line/src/horizontallineediting';
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 import Image from '@ckeditor/ckeditor5-image/src/image';
 import Image from '@ckeditor/ckeditor5-image/src/image';
 import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
 import ImageCaption from '@ckeditor/ckeditor5-image/src/imagecaption';
 import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalline';
 import HorizontalLine from '@ckeditor/ckeditor5-horizontal-line/src/horizontalline';
+import MediaEmbed from '@ckeditor/ckeditor5-media-embed/src/mediaembed';
 
 
 import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import { getCode } from '@ckeditor/ckeditor5-utils/src/keyboard';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -28,30 +27,29 @@ import global from '@ckeditor/ckeditor5-utils/src/dom/global';
 import env from '@ckeditor/ckeditor5-utils/src/env';
 import env from '@ckeditor/ckeditor5-utils/src/env';
 
 
 describe( 'TableKeyboard', () => {
 describe( 'TableKeyboard', () => {
-	let editor, model, modelRoot, tableSelection, tableKeyboard, selection;
+	let editor, model, modelRoot, tableSelection, tableKeyboard, selection, editorElement;
 
 
 	const imageUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAUCAQAAADRyVAeAAAAKklEQVR42u3PAQ0AAAwCI' +
 	const imageUrl = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAGQAAAAUCAQAAADRyVAeAAAAKklEQVR42u3PAQ0AAAwCI' +
 		'O0f+u/hoAHNZUJFRERERERERERERERERLYiD9N4FAFj2iK6AAAAAElFTkSuQmCC';
 		'O0f+u/hoAHNZUJFRERERERERERERERERLYiD9N4FAFj2iK6AAAAAElFTkSuQmCC';
 
 
-	beforeEach( () => {
-		return VirtualTestEditor
-			.create( {
-				plugins: [ TableEditing, TableKeyboard, TableSelection, Paragraph, ImageEditing, ImageCaptionEditing, MediaEmbedEditing,
-					HorizontalLineEditing ]
-			} )
-			.then( newEditor => {
-				editor = newEditor;
-
-				model = editor.model;
-				selection = model.document.selection;
-				modelRoot = model.document.getRoot();
-				tableSelection = editor.plugins.get( TableSelection );
-				tableKeyboard = editor.plugins.get( TableKeyboard );
-			} );
+	beforeEach( async () => {
+		editorElement = global.document.createElement( 'div' );
+		global.document.body.appendChild( editorElement );
+
+		editor = await ClassicTestEditor.create( editorElement, {
+			plugins: [ Table, Paragraph, Image, ImageCaption, HorizontalLine, MediaEmbed ]
+		} );
+
+		model = editor.model;
+		selection = model.document.selection;
+		modelRoot = model.document.getRoot();
+		tableSelection = editor.plugins.get( TableSelection );
+		tableKeyboard = editor.plugins.get( TableKeyboard );
 	} );
 	} );
 
 
-	afterEach( () => {
-		editor.destroy();
+	afterEach( async () => {
+		editorElement.remove();
+		await editor.destroy();
 	} );
 	} );
 
 
 	it( 'should have pluginName', () => {
 	it( 'should have pluginName', () => {
@@ -2053,10 +2051,12 @@ describe( 'TableKeyboard', () => {
 					} );
 					} );
 
 
 					it( 'should navigate to the cell on the left', () => {
 					it( 'should navigate to the cell on the left', () => {
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( leftArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( leftArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01', '02' ],
 							[ '00', '01', '02' ],
@@ -2066,10 +2066,12 @@ describe( 'TableKeyboard', () => {
 					} );
 					} );
 
 
 					it( 'should navigate to the cell on the right', () => {
 					it( 'should navigate to the cell on the right', () => {
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( rightArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( rightArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01', '02' ],
 							[ '00', '01', '02' ],
@@ -2079,10 +2081,12 @@ describe( 'TableKeyboard', () => {
 					} );
 					} );
 
 
 					it( 'should navigate to the cell above', () => {
 					it( 'should navigate to the cell above', () => {
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( upArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( upArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01[]', '02' ],
 							[ '00', '01[]', '02' ],
@@ -2092,10 +2096,12 @@ describe( 'TableKeyboard', () => {
 					} );
 					} );
 
 
 					it( 'should navigate to the cell below', () => {
 					it( 'should navigate to the cell below', () => {
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( downArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( downArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01', '02' ],
 							[ '00', '01', '02' ],
@@ -2113,10 +2119,12 @@ describe( 'TableKeyboard', () => {
 							[ '20', '21', '22' ]
 							[ '20', '21', '22' ]
 						] ) );
 						] ) );
 
 
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', leftArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( leftArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( leftArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( leftArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( leftArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01', '02' ],
 							[ '00', '01', '02' ],
@@ -2132,10 +2140,12 @@ describe( 'TableKeyboard', () => {
 							[ '20', '21', '22' ]
 							[ '20', '21', '22' ]
 						] ) );
 						] ) );
 
 
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', rightArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( rightArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( rightArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( rightArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( rightArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01', '02' ],
 							[ '00', '01', '02' ],
@@ -2151,10 +2161,12 @@ describe( 'TableKeyboard', () => {
 							[ '20', '21', '22' ]
 							[ '20', '21', '22' ]
 						] ) );
 						] ) );
 
 
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', upArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( upArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( upArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( upArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( upArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01[]', '02' ],
 							[ '00', '01[]', '02' ],
@@ -2170,10 +2182,12 @@ describe( 'TableKeyboard', () => {
 							[ '20', '21', '22' ]
 							[ '20', '21', '22' ]
 						] ) );
 						] ) );
 
 
+						// Twice because of fake caret next to widget.
+						editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub );
 						editor.editing.view.document.fire( 'keydown', downArrowDomEvtDataStub );
 
 
-						sinon.assert.calledOnce( downArrowDomEvtDataStub.preventDefault );
-						sinon.assert.calledOnce( downArrowDomEvtDataStub.stopPropagation );
+						sinon.assert.called( downArrowDomEvtDataStub.preventDefault );
+						sinon.assert.called( downArrowDomEvtDataStub.stopPropagation );
 
 
 						assertEqualMarkup( getModelData( model ), modelTable( [
 						assertEqualMarkup( getModelData( model ), modelTable( [
 							[ '00', '01', '02' ],
 							[ '00', '01', '02' ],
@@ -2185,18 +2199,9 @@ describe( 'TableKeyboard', () => {
 			} );
 			} );
 
 
 			describe( 'with selection not at the boundary of a cell', () => {
 			describe( 'with selection not at the boundary of a cell', () => {
-				let editorElement, editor, model, styleElement;
+				let styleElement;
 
 
 				beforeEach( async () => {
 				beforeEach( async () => {
-					editorElement = global.document.createElement( 'div' );
-					global.document.body.appendChild( editorElement );
-
-					editor = await ClassicTestEditor.create( editorElement, {
-						plugins: [ Table, Paragraph, Image, ImageCaption, HorizontalLine ]
-					} );
-
-					model = editor.model;
-
 					styleElement = global.document.createElement( 'style' );
 					styleElement = global.document.createElement( 'style' );
 					styleElement.type = 'text/css';
 					styleElement.type = 'text/css';
 					styleElement.appendChild( global.document.createTextNode(
 					styleElement.appendChild( global.document.createTextNode(
@@ -2220,9 +2225,7 @@ describe( 'TableKeyboard', () => {
 				} );
 				} );
 
 
 				afterEach( async () => {
 				afterEach( async () => {
-					editorElement.remove();
 					styleElement.remove();
 					styleElement.remove();
-					await editor.destroy();
 				} );
 				} );
 
 
 				describe( 'simple cell text content', () => {
 				describe( 'simple cell text content', () => {
@@ -3065,6 +3068,8 @@ describe( 'TableKeyboard', () => {
 		} );
 		} );
 
 
 		describe( 'for right-to-left content language', () => {
 		describe( 'for right-to-left content language', () => {
+			let editor, model, modelRoot, tableSelection;
+
 			beforeEach( () => {
 			beforeEach( () => {
 				return VirtualTestEditor
 				return VirtualTestEditor
 					.create( {
 					.create( {
@@ -3080,6 +3085,10 @@ describe( 'TableKeyboard', () => {
 					} );
 					} );
 			} );
 			} );
 
 
+			afterEach( () => {
+				return editor.destroy();
+			} );
+
 			describe( 'with the table cell selected from outside', () => {
 			describe( 'with the table cell selected from outside', () => {
 				beforeEach( () => {
 				beforeEach( () => {
 					setModelData( model, modelTable( [
 					setModelData( model, modelTable( [

+ 1 - 1
packages/ckeditor5-typing/tests/utils/inlinehighlight.js

@@ -30,7 +30,7 @@ describe( 'inlineHighlight', () => {
 		model.schema.extend( '$text', { allowAttributes: 'linkHref' } );
 		model.schema.extend( '$text', { allowAttributes: 'linkHref' } );
 
 
 		editor.conversion.for( 'editingDowncast' )
 		editor.conversion.for( 'editingDowncast' )
-			.attributeToElement( { model: 'linkHref', view: ( href, writer ) => {
+			.attributeToElement( { model: 'linkHref', view: ( href, { writer } ) => {
 				return writer.createAttributeElement( 'a', { href } );
 				return writer.createAttributeElement( 'a', { href } );
 			} } );
 			} } );
 
 

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