Преглед изворни кода

Merge branch 'master' into t/63

Maciej Gołaszewski пре 7 година
родитељ
комит
58568ca94a
74 измењених фајлова са 2517 додато и 710 уклоњено
  1. 3 0
      packages/ckeditor5-table/.travis.yml
  2. 35 0
      packages/ckeditor5-table/CHANGELOG.md
  3. 1 1
      packages/ckeditor5-table/CONTRIBUTING.md
  4. 4 0
      packages/ckeditor5-table/README.md
  5. 2 2
      packages/ckeditor5-table/docs/_snippets/features/table.js
  6. 4 0
      packages/ckeditor5-table/docs/api/table.md
  7. 117 8
      packages/ckeditor5-table/docs/features/table.md
  8. 2 2
      packages/ckeditor5-table/lang/contexts.json
  9. 78 0
      packages/ckeditor5-table/lang/translations/ar.po
  10. 6 6
      packages/ckeditor5-table/lang/translations/cs.po
  11. 6 6
      packages/ckeditor5-table/lang/translations/de-ch.po
  12. 6 6
      packages/ckeditor5-table/lang/translations/de.po
  13. 6 6
      packages/ckeditor5-table/lang/translations/en-au.po
  14. 6 6
      packages/ckeditor5-table/lang/translations/en.po
  15. 6 6
      packages/ckeditor5-table/lang/translations/et.po
  16. 78 0
      packages/ckeditor5-table/lang/translations/fa.po
  17. 78 0
      packages/ckeditor5-table/lang/translations/fi.po
  18. 78 0
      packages/ckeditor5-table/lang/translations/fr.po
  19. 78 0
      packages/ckeditor5-table/lang/translations/gl.po
  20. 6 6
      packages/ckeditor5-table/lang/translations/hr.po
  21. 6 6
      packages/ckeditor5-table/lang/translations/hu.po
  22. 6 6
      packages/ckeditor5-table/lang/translations/it.po
  23. 78 0
      packages/ckeditor5-table/lang/translations/ja.po
  24. 78 0
      packages/ckeditor5-table/lang/translations/ku.po
  25. 6 6
      packages/ckeditor5-table/lang/translations/nb.po
  26. 78 0
      packages/ckeditor5-table/lang/translations/ne.po
  27. 6 6
      packages/ckeditor5-table/lang/translations/nl.po
  28. 78 0
      packages/ckeditor5-table/lang/translations/no.po
  29. 78 0
      packages/ckeditor5-table/lang/translations/pl.po
  30. 14 14
      packages/ckeditor5-table/lang/translations/pt-br.po
  31. 78 0
      packages/ckeditor5-table/lang/translations/ru.po
  32. 78 0
      packages/ckeditor5-table/lang/translations/sk.po
  33. 78 0
      packages/ckeditor5-table/lang/translations/sq.po
  34. 6 6
      packages/ckeditor5-table/lang/translations/tr.po
  35. 6 6
      packages/ckeditor5-table/lang/translations/uk.po
  36. 6 6
      packages/ckeditor5-table/lang/translations/zh-cn.po
  37. 19 16
      packages/ckeditor5-table/package.json
  38. 12 12
      packages/ckeditor5-table/src/commands/insertcolumncommand.js
  39. 6 7
      packages/ckeditor5-table/src/commands/inserttablecommand.js
  40. 0 0
      packages/ckeditor5-table/src/commands/mergecellcommand.js
  41. 4 4
      packages/ckeditor5-table/src/commands/removerowcommand.js
  42. 1 2
      packages/ckeditor5-table/src/commands/setheaderrowcommand.js
  43. 31 25
      packages/ckeditor5-table/src/converters/downcast.js
  44. 112 0
      packages/ckeditor5-table/src/converters/table-cell-content-post-fixer.js
  45. 28 30
      packages/ckeditor5-table/src/converters/table-layout-post-fixer.js
  46. 75 68
      packages/ckeditor5-table/src/converters/tablecell-post-fixer.js
  47. 20 19
      packages/ckeditor5-table/src/converters/upcasttable.js
  48. 3 3
      packages/ckeditor5-table/src/table.js
  49. 14 9
      packages/ckeditor5-table/src/tableediting.js
  50. 63 118
      packages/ckeditor5-table/src/tabletoolbar.js
  51. 4 4
      packages/ckeditor5-table/src/tableui.js
  52. 21 19
      packages/ckeditor5-table/src/tableutils.js
  53. 0 52
      packages/ckeditor5-table/src/ui/utils.js
  54. 3 3
      packages/ckeditor5-table/tests/_utils/utils.js
  55. 7 7
      packages/ckeditor5-table/tests/commands/insertcolumncommand.js
  56. 29 1
      packages/ckeditor5-table/tests/commands/inserttablecommand.js
  57. 1 1
      packages/ckeditor5-table/tests/converters/downcast.js
  58. 124 0
      packages/ckeditor5-table/tests/converters/table-cell-content-post-fixer.js
  59. 8 9
      packages/ckeditor5-table/tests/converters/table-layout-post-fixer.js
  60. 95 27
      packages/ckeditor5-table/tests/converters/tablecell-post-fixer.js
  61. 107 0
      packages/ckeditor5-table/tests/converters/upcasttable.js
  62. 1 1
      packages/ckeditor5-table/tests/integration.js
  63. 4 2
      packages/ckeditor5-table/tests/manual/table.js
  64. 8 6
      packages/ckeditor5-table/tests/manual/table.md
  65. 1 1
      packages/ckeditor5-table/tests/manual/tableblockcontent.js
  66. 2 3
      packages/ckeditor5-table/tests/manual/tableblockcontent.md
  67. 25 0
      packages/ckeditor5-table/tests/table-integration.js
  68. 15 6
      packages/ckeditor5-table/tests/tableediting.js
  69. 383 133
      packages/ckeditor5-table/tests/tabletoolbar.js
  70. 7 7
      packages/ckeditor5-table/tests/tableui.js
  71. 1 1
      packages/ckeditor5-table/theme/icons/table-column.svg
  72. 1 1
      packages/ckeditor5-table/theme/icons/table-merge-cell.svg
  73. 1 1
      packages/ckeditor5-table/theme/icons/table-row.svg
  74. 1 1
      packages/ckeditor5-table/theme/icons/table.svg

+ 3 - 0
packages/ckeditor5-table/.travis.yml

@@ -12,6 +12,9 @@ node_js:
 - '8'
 cache:
 - node_modules
+branches:
+   except:
+   - stable
 before_install:
 - export DISPLAY=:99.0
 - sh -e /etc/init.d/xvfb start

+ 35 - 0
packages/ckeditor5-table/CHANGELOG.md

@@ -1,6 +1,41 @@
 Changelog
 =========
 
+## [11.0.0](https://github.com/ckeditor/ckeditor5-table/compare/v10.1.0...v11.0.0) (2018-10-08)
+
+### Features
+
+* Implemented the table post–fixer which bulletproofs the feature in various complex use–cases (e.g. pasting and real-time collaborative editing). Closes [#13](https://github.com/ckeditor/ckeditor5-table/issues/13). ([01f9a3b](https://github.com/ckeditor/ckeditor5-table/commit/01f9a3b))
+* Introduced the toolbar for the table widget (previously it was available only for single cells). Changed the toolbar configuration option from `table.toolbar` to `table.contentToolbar`. Closes [#113](https://github.com/ckeditor/ckeditor5-table/issues/113). Closes [#106](https://github.com/ckeditor/ckeditor5-table/issues/106). ([9f9486d](https://github.com/ckeditor/ckeditor5-table/commit/9f9486d))
+
+  Other: `config.table.toolbar` is marked as depracted. Use `config.table.contentToolbar` instead.
+* Introduced a support for block content inside tables. Closes [#56](https://github.com/ckeditor/ckeditor5-table/issues/56). ([cdf718e](https://github.com/ckeditor/ckeditor5-table/commit/cdf718e))
+
+### Bug fixes
+
+* A table cell should always have a `<paragraph>` in the model. Closes [#125](https://github.com/ckeditor/ckeditor5-table/issues/125). ([1eb5d6d](https://github.com/ckeditor/ckeditor5-table/commit/1eb5d6d))
+* Downcast converter for table attributes should work with not converted child elements. Closes [#92](https://github.com/ckeditor/ckeditor5-table/issues/92). ([a3ea18d](https://github.com/ckeditor/ckeditor5-table/commit/a3ea18d))
+* Merging down rowspanned cell from the head with a cell in the body is now disabled. Closes [#86](https://github.com/ckeditor/ckeditor5-table/issues/86). ([cb77e38](https://github.com/ckeditor/ckeditor5-table/commit/cb77e38))
+* The upcast conversion will now properly parse inline content in table cell into single paragraph. Closes [ckeditor/ckeditor5#1246](https://github.com/ckeditor/ckeditor5/issues/1246). ([ea1e16d](https://github.com/ckeditor/ckeditor5-table/commit/ea1e16d))
+* Toggling headers should always include the column or row the selection is anchored to. Closes [#34](https://github.com/ckeditor/ckeditor5-table/issues/34). ([bce6766](https://github.com/ckeditor/ckeditor5-table/commit/bce6766))
+
+### Other changes
+
+* Aligned `TableToolbar` to the widget toolbar repository. Closes [#107](https://github.com/ckeditor/ckeditor5-table/issues/107). ([e276e66](https://github.com/ckeditor/ckeditor5-table/commit/e276e66))
+* Media should not be allowed inside table cells for now. Closes [#124](https://github.com/ckeditor/ckeditor5-table/issues/124). ([2f2fe4a](https://github.com/ckeditor/ckeditor5-table/commit/2f2fe4a))
+* Table feature should insert table the same way as other widget features do. Closes [#27](https://github.com/ckeditor/ckeditor5-table/issues/27). ([77d96a4](https://github.com/ckeditor/ckeditor5-table/commit/77d96a4))
+* The table cell view post-fixer should use changed elements from the view to make fixes. Closes [#130](https://github.com/ckeditor/ckeditor5-table/issues/130). ([efc53c9](https://github.com/ckeditor/ckeditor5-table/commit/efc53c9))
+* Updated the table icon which used to feel bulky with a lighter design. Closes [#117](https://github.com/ckeditor/ckeditor5-table/issues/117). ([cd6f5ff](https://github.com/ckeditor/ckeditor5-table/commit/cd6f5ff))
+* Updated translations. ([de47767](https://github.com/ckeditor/ckeditor5-table/commit/de47767))
+
+### BREAKING CHANGES
+
+* The `config.table.toolbar` was renamed to `config.table.contentToolbar`.
+* The `injectTablePostFixer()` function from  `table/converters/table-post-fixer` is now `injectTableLayoutPostFixer()`and is moved to `table/converters/table-layout-post-fixer` module.
+* The `TableUtils#createTable()` method now accepts model `Writer` instance instead of `Position`. The method no longer inserts created table to the model - use returned value instead.
+* Removed `table/commands/utils~getParentTable()` method. Use `table/commands/utils~findAncestor()` instead.
+
+
 ## [10.1.0](https://github.com/ckeditor/ckeditor5-table/compare/v10.0.0...v10.1.0) (2018-07-18)
 
 ### Features

+ 1 - 1
packages/ckeditor5-table/CONTRIBUTING.md

@@ -1,4 +1,4 @@
 Contributing
 ========================================
 
-Information about contributing can be found on the following page: <https://github.com/ckeditor/ckeditor5/blob/master/CONTRIBUTING.md>.
+See the [official contributors' guide to CKEditor 5](https://ckeditor.com/docs/ckeditor5/latest/framework/guides/contributing/contributing.html) to learn more.

+ 4 - 0
packages/ckeditor5-table/README.md

@@ -12,6 +12,10 @@ CKEditor 5 table feature
 
 This package implements table support for CKEditor 5.
 
+## Demo
+
+Check out the [demo in the Table feature](https://ckeditor.com/docs/ckeditor5/latest/features/table.html#demo) guide.
+
 ## Documentation
 
 See the [`@ckeditor/ckeditor5-table` package](https://ckeditor.com/docs/ckeditor5/latest/api/table.html) page in [CKEditor 5 documentation](https://ckeditor.com/docs/ckeditor5/latest/).

+ 2 - 2
packages/ckeditor5-table/docs/_snippets/features/table.js

@@ -14,10 +14,10 @@ ClassicEditor
 			items: [
 				'insertTable', '|', 'heading', '|', 'bold', 'italic', '|', 'undo', 'redo'
 			],
-			viewportTopOffset: 60
+			viewportTopOffset: window.getViewportTopOffsetConfig()
 		},
 		table: {
-			toolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
+			contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
 		}
 	} )
 	.then( editor => {

+ 4 - 0
packages/ckeditor5-table/docs/api/table.md

@@ -8,6 +8,10 @@ category: api-reference
 
 This package implements the table feature for CKEditor 5.
 
+## Demo
+
+Check out the {@link features/table#demo demo in the Table feature} guide.
+
 ## Documentation
 
 See the {@link features/table Table feature} guide and the documentation for the following plugins:

+ 117 - 8
packages/ckeditor5-table/docs/features/table.md

@@ -13,13 +13,17 @@ The {@link module:table/table~Table} feature offers table creation and editing t
 
 ## Installation
 
+<info-box info>
+	This feature is enabled by default in all builds. The installation instructions are for developers interested in building their own, custom rich text editor.
+</info-box>
+
 To add this feature to your editor, install the [`@ckeditor/ckeditor5-table`](https://www.npmjs.com/package/@ckeditor/ckeditor5-table) package:
 
 ```bash
 npm install --save @ckeditor/ckeditor5-table
 ```
 
-Then add `'Table'` and `'TableToolbar'` to your plugin list and configure the table toolbar:
+Then add `Table` and `TableToolbar` plugins to your plugin list and configure the table toolbar:
 
 ```js
 import Table from '@ckeditor/ckeditor5-table/src/table';
@@ -27,18 +31,121 @@ import TableToolbar from '@ckeditor/ckeditor5-table/src/tabletoolbar';
 
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
-		plugins: [ Table, TableToolbar, ... ],
-		toolbar: [ 'insertTable', ... ]
+		plugins: [ Table, TableToolbar, Bold, ... ],
+		toolbar: [ 'insertTable', ... ],
 		table: {
-			toolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
+			contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
 		}
 	} )
 	.then( ... )
 	.catch( ... );
 ```
 
+## Block vs inline content in table cells
+
+The table feature allows creating block content (like paragraphs, lists, headings, etc.) in table cells. However, if a table cell contains just one paragraph and this paragraph has no special attributes (like text alignment), this cell content is considered "inline" and the paragraph is not rendered.
+
+This means that a table cell can be in two states: with inline content or with block content. The reason for this differentiation is that most tables contain only inline content (e.g. in the [demo](#demo) above) and it is common for "data tables" to not contain any block content. In such scenario, printing out `<p>` elements would be semantically incorrect and also unnecessary. There are, however, scenarios where the user wants to create, for example, a list inside the table and then support for block content is necessary, too.
+
+<info-box>
+	"Rendering" here means the view layer. In the model a cell is always filled with at least a `<paragraph>`. It is because of consistency, as since a cell always has some block content, the text is never directly inside `<tableCell>`. This also allows features like <kbd>Enter</kbd> support to work out of the box (since a `<paragraph>` exists in the model, it can be split despite the fact that it is not present in the view).
+</info-box>
+
+### Inline content
+
+The following is the model representation of table cells with inline content only (a single `<paragraph>` inside):
+
+```html
+<table>
+	<tableRow>
+		<tableCell>
+			<paragraph>Foo</paragraph>
+		</tableCell>
+		<tableCell>
+			<paragraph>Bar</paragraph>
+		</tableCell>
+	</tableRow>
+</table>
+```
+
+The above model structure will be rendered to the {@link module:editor-classic/classiceditor~ClassicEditor#getData data} as:
+
+```html
+<figure class="table">
+	<table>
+		<tbody>
+			<tr>
+				<td>Foo</td>
+				<td>Bar</td>
+			</tr>
+		</tbody>
+	</table>
+</figure>
+```
+
+In the editing view (the editable container in which the user edits the content) additional `<span>` elements are created to compensate for the hidden `<paragraph>` elements:
+
+```html
+<figure class="table">
+	<table>
+		<tbody>
+			<tr>
+				<td><span>Foo</span></td>
+				<td><span>Bar</span></td>
+			</tr>
+		</tbody>
+	</table>
+</figure>
+```
+
+### Block content
+
+If a table cell contains any other block content than a single `<paragraph>` with no attributes, these block elements will be rendered.
+
+The following is a sample table with some block content (model representation):
+
+```html
+<table>
+	<tableRow>
+		<tableCell>
+			<paragraph>Foo</paragraph>
+			<paragraph>Bar</paragraph>
+		</tableCell>
+		<tableCell>
+			<heading1>Some title</heading1>
+		</tableCell>
+		<tableCell>
+			<paragraph textAlign="right">Baz</paragraph>
+		</tableCell>
+	</tableRow>
+</table>
+```
+
+The above model structure will be rendered to the data and to the editing view as:
+
+```html
+<figure class="table">
+	<table>
+		<tbody>
+			<tr>
+				<td>
+					<p>Foo</p>
+					<p>Bar</p>
+				</td>
+				<td>
+					<h2>Some title</h2>
+				</td>
+				<td>
+					<p style="text-align: right;">Baz</p>
+				</td>
+			</tr>
+		</tbody>
+	</table>
+</figure>
+```
+
 <info-box info>
-	At the moment by default the table feature is available only in the {@link builds/guides/overview#document-editor document editor build}. Read more about {@link builds/guides/integration/installing-plugins installing plugins} if you want to add it to other editor builds.
+	At the moment it is not possible to completely disallow block content in tables. See the [discussion on GitHub](https://github.com/ckeditor/ckeditor5-table/issues/101) about adding a configuration option that would enable that. 👍 if you need this feature.
 </info-box>
 
 ## Common API
@@ -53,8 +160,8 @@ The {@link module:table/table~Table} plugin registers the following UI component
 And the following commands:
 
 * The `'insertTable'` command implemented by {@link module:table/commands/inserttablecommand~InsertTableCommand}.
-* The `'insertTableColumnBefore'` command implemented by {@link module:table/commands/insertcolumncommand~InsertColumnCommand}.
-* The `'insertTableColumnAfter'` command implemented by {@link module:table/commands/insertcolumncommand~InsertColumnCommand}.
+* The `'insertTableColumnLeft'` command implemented by {@link module:table/commands/insertcolumncommand~InsertColumnCommand}.
+* The `'insertTableColumnRight'` command implemented by {@link module:table/commands/insertcolumncommand~InsertColumnCommand}.
 * The `'insertTableRowAbove'` command implemented by {@link module:table/commands/insertrowcommand~InsertRowCommand}.
 * The `'insertTableRowBelow'` command implemented by {@link module:table/commands/insertrowcommand~InsertRowCommand}.
 * The `'removeTableColumn'` command implemented by {@link module:table/commands/removecolumncommand~RemoveColumnCommand}.
@@ -68,7 +175,9 @@ And the following commands:
 * The `'splitTableCellVertically'` command implemented by {@link module:table/commands/splitcellcommand~SplitCellCommand}.
 * The `'splitTableCellHorizontally'` command implemented by {@link module:table/commands/splitcellcommand~SplitCellCommand}.
 
-The {@link module:table/tabletoolbar~TableToolbar} plugin introduces the balloon toolbar for tables. The toolbar shows up when a table cell is selected and is anchored to the table. It is possible to {@link module:table/table~TableConfig#toolbar configure} its content. Normally, it contains the table-related tools such as `'tableColumn'`, `'tableRow'`, and `'mergeTableCells'` dropdowns.
+The {@link module:table/tabletoolbar~TableToolbar} plugin introduces two balloon toolbars for tables.
+* The content toolbar shows up when table cell is selected and is anchored to the table. It is possible to {@link module:table/table~TableConfig#contentToolbar configure} its content. Normally, it contains the table-related tools such as `'tableColumn'`, `'tableRow'`, and `'mergeTableCells'` dropdowns.
+* The table toolbar shows up when the whole table is selected, for instance using the widget handler. It is possible to {@link module:table/table~TableConfig#tableToolbar configure} its content.
 
 ## Contribute
 

+ 2 - 2
packages/ckeditor5-table/lang/contexts.json

@@ -1,8 +1,8 @@
 {
 	"Insert table": "Label for the insert table toolbar button.",
 	"Header column": "Label for the set/unset table header column button.",
-	"Insert column before": "Label for the insert table column before the current one button.",
-	"Insert column after": "Label for the insert table column after the current one button.",
+	"Insert column left": "Label for the insert table column to the left of the current one button.",
+	"Insert column right": "Label for the insert table column to the right of the current one button.",
 	"Delete column": "Label for the delete table column button.",
 	"Column": "Label for the table column dropdown button.",
 	"Header row": "Label for the set/unset table header row button.",

+ 78 - 0
packages/ckeditor5-table/lang/translations/ar.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Arabic (https://www.transifex.com/ckeditor/teams/11143/ar/)\n"
+"Language: ar\n"
+"Plural-Forms: nplurals=6; plural=n==0 ? 0 : n==1 ? 1 : n==2 ? 2 : n%100>=3 && n%100<=10 ? 3 : n%100>=11 && n%100<=99 ? 4 : 5;\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "إدراج جدول"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "عمود عنوان"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "حذف العمود"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "عمود"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "صف عنوان"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "ادراج صف بعد"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "ادراج صف قبل"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "حذف الصف"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "صف"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "دمج الخلايا للأعلى"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "دمج الخلايا لليمين"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "دمج الخلايا للأسفل"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "دمج الخلايا لليسار"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "فصل الخلايا بشكل عمودي"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "فصل الخلايا بشكل افقي"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "دمج الخلايا"

+ 6 - 6
packages/ckeditor5-table/lang/translations/cs.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Sloupec záhlaví"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Vložit sloupec před"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Vložit sloupec za"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/de-ch.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Kopfspalte"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Spalte links einfügen"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Spalte rechts einfügen"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/de.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Kopfspalte"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Spalte links einfügen"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Spalte rechts einfügen"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/en-au.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Header column"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Insert column before"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Insert column after"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/en.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Header column"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Insert column before"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr "Insert column left"
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Insert column after"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr "Insert column right"
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/et.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Päise veerg"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Sisesta veerg ettepoole"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Sisesta veerg tahapoole"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 78 - 0
packages/ckeditor5-table/lang/translations/fa.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Persian (https://www.transifex.com/ckeditor/teams/11143/fa/)\n"
+"Language: fa\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "درج جدول"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "ستون سربرگ"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "حذف ستون"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "ستون"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "سطر سربرگ"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "درج سطر در پایین"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "درج سطر در بالا"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "حذف سطر"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "سطر"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "ادغام سلول بالا"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "ادغام سلول راست"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "ادغام سلول پایین"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "ادغام سلول چپ"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "تقسیم عمودی سلول"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "تقسیم افقی سلول"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "ادغام سلول ها"

+ 78 - 0
packages/ckeditor5-table/lang/translations/fi.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Finnish (https://www.transifex.com/ckeditor/teams/11143/fi/)\n"
+"Language: fi\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Lisää taulukko"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Otsikkosarake"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Poista sarake"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Sarake"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Otsikkorivi"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Lisää rivi alle"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Lisää rivi ylle"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Poista rivi"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Rivi"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Yhdistä solu ylös"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Yhdistä solu oikealle"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Yhdistä solu alas"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Yhdistä solu vasemmalle"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Jaa solu pystysuunnassa"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Jaa solu vaakasuunnassa"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Yhdistä tai jaa soluja"

+ 78 - 0
packages/ckeditor5-table/lang/translations/fr.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: French (https://www.transifex.com/ckeditor/teams/11143/fr/)\n"
+"Language: fr\n"
+"Plural-Forms: nplurals=2; plural=(n > 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Insérer un tableau"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Colonne d'entête"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Supprimer la colonne"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Colonne"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Rangée d'entête"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Insérer une rangée en-dessous"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Insérer une rangée au-dessus"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Supprimer la rangée"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Rangée"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Fusionner la cellule au-dessus"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Fusionner la cellule à droite"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Fusionner la cellule en-dessous"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Fusionner la cellule à gauche"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Séparer la cellule verticalement"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Séparer la cellule horizontalement"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Fusionner les cellules"

+ 78 - 0
packages/ckeditor5-table/lang/translations/gl.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Galician (https://www.transifex.com/ckeditor/teams/11143/gl/)\n"
+"Language: gl\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Inserir táboa"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Cabeceira de columna"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Eliminar columna"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Columna"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Cabeceira de fila"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Inserir fila embaixo"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Inserir fila enriba"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Eliminar fila"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Fila"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Combinar cela cara arriba"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Combinar cela cara a dereita"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Combinar cela cara  abaixo"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Combinar cela cara a esquerda"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Dividir cela en vertical"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Dividir cela en horizontal"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Combinar celas"

+ 6 - 6
packages/ckeditor5-table/lang/translations/hr.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Kolona zaglavlja"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Ubaci kolonu prije"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Ubaci kolonu nakon"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/hu.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Oszlop fejléc"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Oszlop beszúrása elé"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Oszlop beszúrása mögé"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/it.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Intestazione colonna"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Inserisci colonna prima"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Inserisci colonna dopo"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 78 - 0
packages/ckeditor5-table/lang/translations/ja.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Japanese (https://www.transifex.com/ckeditor/teams/11143/ja/)\n"
+"Language: ja\n"
+"Plural-Forms: nplurals=1; plural=0;\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "表の挿入"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "見出し列"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "列を削除"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "列"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "見出し行"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "下に行を挿入"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "上に行を挿入"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "行を削除"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "行"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "上のセルと結合"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "右のセルと結合"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "下のセルと結合"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "左のセルと結合"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "横にセルを分離"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "縦にセルを分離"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "セルを結合"

+ 78 - 0
packages/ckeditor5-table/lang/translations/ku.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Kurdish (https://www.transifex.com/ckeditor/teams/11143/ku/)\n"
+"Language: ku\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "خشتە دابنێ"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "ستوونی دەسپێک"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "سڕینەوەی ستوون"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "ستوون"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "ڕیزی دەسپێک"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "دانانی ڕیز لە ژێرەوە"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "دانانی ڕیز لە سەرەوە"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "سڕینەوەی ڕیز"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "ڕیز"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "تێکەڵکردنی خانەکان بەرەو سەر"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "تێکەڵکردنی خانەکان بەرەو ڕاست"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "تێکەڵکردنی خانەکان بەرەو ژێرەوە"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "تێکەڵکردنی خانەکان بەرەو چەپ"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "بەشکردنی خانەکان بە ئەستوونی"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "بەشکردنی خانەکان بە ئاسۆیی"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "تێکەڵکردنی خانەکان"

+ 6 - 6
packages/ckeditor5-table/lang/translations/nb.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Overskriftkolonne"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Sett inn kolonne før"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Sett inn kolonne etter"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 78 - 0
packages/ckeditor5-table/lang/translations/ne.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Nepali (Nepal) (https://www.transifex.com/ckeditor/teams/11143/ne_NP/)\n"
+"Language: ne_NP\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "तालिका सम्मिलित गर्नुहोस्"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "हेडर स्तम्भ"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "स्तम्भ मेटाउनुहोस्"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "स्तम्भ"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "हेडर पङ्क्ति"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "तल पंक्ति सम्मिलित गर्नुहोस्"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "माथि पंक्ति सम्मिलित गर्नुहोस्"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "पङ्क्ति मेटाउनुहोस्"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "पङ्क्ति"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "कक्ष माथि मर्ज गर्नुहोस्"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "दायाँ कक्ष मर्ज गर्नुहोस्"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "कक्ष तल मर्ज गर्नुहोस्"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "सेल बायाँ मर्ज गर्नुहोस्"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "ठाडो कक्ष विभाजित गर्नुहोस्"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "क्षैतिज कक्ष विभाजित गर्नुहोस्"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "कक्ष मर्ज गर्नुहोस्"

+ 6 - 6
packages/ckeditor5-table/lang/translations/nl.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Titel kolom"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Kolom hiervoor invoegen"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Kolom hierna invoegen"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 78 - 0
packages/ckeditor5-table/lang/translations/no.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Norwegian (https://www.transifex.com/ckeditor/teams/11143/no/)\n"
+"Language: no\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Sett inn tabell"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Overskriftkolonne"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr "Sett inn kolonne til venstre"
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr "Sett inn kolonne til høyre"
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Slett kolonne"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Kolonne"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Overskriftrad"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Sett inn rad under"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Sett inn rad over"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Slett rad"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Rad"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Slå sammen celle over"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Slå sammen celle til høyre"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Slå sammen celle under"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Slå sammen celle til venstre"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Del opp celle vertikalt"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Del opp celle horisontalt"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Slå sammen celler"

+ 78 - 0
packages/ckeditor5-table/lang/translations/pl.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Polish (https://www.transifex.com/ckeditor/teams/11143/pl/)\n"
+"Language: pl\n"
+"Plural-Forms: nplurals=4; plural=(n==1 ? 0 : (n%10>=2 && n%10<=4) && (n%100<12 || n%100>14) ? 1 : n!=1 && (n%10>=0 && n%10<=1) || (n%10>=5 && n%10<=9) || (n%100>=12 && n%100<=14) ? 2 : 3);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Wstaw tabelę"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Kolumna nagłówka"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr "Wstaw kolumnę z lewej"
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr "Wstaw kolumnę z prawej"
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Usuń kolumnę"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Kolumna"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Wiersz nagłówka"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Wstaw wiersz poniżej"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Wstaw wiersz ponad"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Usuń wiersz"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Wiersz"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Scal komórkę w górę"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Scal komórkę w prawo"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Scal komórkę w dół"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Scal komórkę w lewo"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Podziel komórkę pionowo"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Podziel komórkę poziomo"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Scal komórki"

+ 14 - 14
packages/ckeditor5-table/lang/translations/pt-br.po

@@ -11,15 +11,15 @@ msgstr "Inserir tabela"
 
 msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
-msgstr ""
+msgstr "Coluna de cabeçalho"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Inserir coluna antes"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Inserir coluna depois"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"
@@ -31,7 +31,7 @@ msgstr "Coluna"
 
 msgctxt "Label for the set/unset table header row button."
 msgid "Header row"
-msgstr ""
+msgstr "Linha de cabeçalho"
 
 msgctxt "Label for the insert row below button."
 msgid "Insert row below"
@@ -51,27 +51,27 @@ msgstr "Linha"
 
 msgctxt "Label for the merge table cell up button."
 msgid "Merge cell up"
-msgstr ""
+msgstr "Mesclar acima"
 
 msgctxt "Label for the merge table cell right button."
 msgid "Merge cell right"
-msgstr ""
+msgstr "Mesclar à direita"
 
 msgctxt "Label for the merge table cell down button."
 msgid "Merge cell down"
-msgstr ""
+msgstr "Mesclar abaixo"
 
 msgctxt "Label for the merge table cell left button."
 msgid "Merge cell left"
-msgstr ""
+msgstr "Mesclar à esquerda"
 
 msgctxt "Label for the split table cell vertically button."
 msgid "Split cell vertically"
-msgstr ""
+msgstr "Dividir verticalmente"
 
 msgctxt "Label for the split table cell horizontally button."
 msgid "Split cell horizontally"
-msgstr ""
+msgstr "Dividir horizontalmente"
 
 msgctxt "Label for the merge table cells button."
 msgid "Merge cells"

+ 78 - 0
packages/ckeditor5-table/lang/translations/ru.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Russian (https://www.transifex.com/ckeditor/teams/11143/ru/)\n"
+"Language: ru\n"
+"Plural-Forms: nplurals=4; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<12 || n%100>14) ? 1 : n%10==0 || (n%10>=5 && n%10<=9) || (n%100>=11 && n%100<=14)? 2 : 3);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Вставить таблицу"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Столбец заголовков"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Удалить столбец"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Столбец"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Строка заголовков"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Вставить строку выше"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Вставить строку ниже"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Удалить строку"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Строка"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Объединить с ячейкой сверху"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Объединить с ячейкой справа"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Объединить с ячейкой снизу"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Объединить с ячейкой слева"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Разделить ячейку вертикально"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Разделить ячейку горизонтально"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Объединить ячейки"

+ 78 - 0
packages/ckeditor5-table/lang/translations/sk.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Slovak (https://www.transifex.com/ckeditor/teams/11143/sk/)\n"
+"Language: sk\n"
+"Plural-Forms: nplurals=4; plural=(n % 1 == 0 && n == 1 ? 0 : n % 1 == 0 && n >= 2 && n <= 4 ? 1 : n % 1 != 0 ? 2: 3);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Vložiť tabuľku"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Stĺpec hlavičky"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Odstrániť stĺpec"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Stĺpec"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Riadok hlavičky"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Vložiť riadok pod"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Vložiť riadok nad"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Odstrániť riadok"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Riadok"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Zlúčiť bunku hore"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Zlúčiť bunku vpravo"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Zlúčiť bunku dole"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Zlúčiť bunku vľavo"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Rozdeliť bunku zvislo"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Rozdeliť bunku vodorovne"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Zlúčiť bunky"

+ 78 - 0
packages/ckeditor5-table/lang/translations/sq.po

@@ -0,0 +1,78 @@
+# Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+msgid ""
+msgstr ""
+"Language-Team: Albanian (https://www.transifex.com/ckeditor/teams/11143/sq/)\n"
+"Language: sq\n"
+"Plural-Forms: nplurals=2; plural=(n != 1);\n"
+
+msgctxt "Label for the insert table toolbar button."
+msgid "Insert table"
+msgstr "Shto tabelë"
+
+msgctxt "Label for the set/unset table header column button."
+msgid "Header column"
+msgstr "Kolona e kokës"
+
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
+
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
+
+msgctxt "Label for the delete table column button."
+msgid "Delete column"
+msgstr "Gris kolonën"
+
+msgctxt "Label for the table column dropdown button."
+msgid "Column"
+msgstr "Kolona"
+
+msgctxt "Label for the set/unset table header row button."
+msgid "Header row"
+msgstr "Rreshti i kokës"
+
+msgctxt "Label for the insert row below button."
+msgid "Insert row below"
+msgstr "Shto rresht poshtë"
+
+msgctxt "Label for the insert row above button."
+msgid "Insert row above"
+msgstr "Shto rresht sipër"
+
+msgctxt "Label for the delete table row button."
+msgid "Delete row"
+msgstr "Grish rreshtin"
+
+msgctxt "Label for the table row dropdown button."
+msgid "Row"
+msgstr "Rreshti"
+
+msgctxt "Label for the merge table cell up button."
+msgid "Merge cell up"
+msgstr "Bashko kutizat sipër"
+
+msgctxt "Label for the merge table cell right button."
+msgid "Merge cell right"
+msgstr "Bashko kutizat djathtas"
+
+msgctxt "Label for the merge table cell down button."
+msgid "Merge cell down"
+msgstr "Bashko kutizat poshtë"
+
+msgctxt "Label for the merge table cell left button."
+msgid "Merge cell left"
+msgstr "Bashko kutizat majtas"
+
+msgctxt "Label for the split table cell vertically button."
+msgid "Split cell vertically"
+msgstr "Ndajë kutizat vertikalisht"
+
+msgctxt "Label for the split table cell horizontally button."
+msgid "Split cell horizontally"
+msgstr "Ndaj kutizat horizontalisht"
+
+msgctxt "Label for the merge table cells button."
+msgid "Merge cells"
+msgstr "Bashko kutizat"

+ 6 - 6
packages/ckeditor5-table/lang/translations/tr.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Başlık kolonu"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Öncesine kolon ekle"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Sonrasına kolon ekle"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/uk.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "Заголовок стовпця"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "Вставити стовпець перед"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "Вставити стовпець після"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 6 - 6
packages/ckeditor5-table/lang/translations/zh-cn.po

@@ -13,13 +13,13 @@ msgctxt "Label for the set/unset table header column button."
 msgid "Header column"
 msgstr "标题列"
 
-msgctxt "Label for the insert table column before the current one button."
-msgid "Insert column before"
-msgstr "在前面插入列"
+msgctxt "Label for the insert table column to the left of the current one button."
+msgid "Insert column left"
+msgstr ""
 
-msgctxt "Label for the insert table column after the current one button."
-msgid "Insert column after"
-msgstr "在后面插入列"
+msgctxt "Label for the insert table column to the right of the current one button."
+msgid "Insert column right"
+msgstr ""
 
 msgctxt "Label for the delete table column button."
 msgid "Delete column"

+ 19 - 16
packages/ckeditor5-table/package.json

@@ -1,30 +1,33 @@
 {
   "name": "@ckeditor/ckeditor5-table",
-  "version": "10.1.0",
+  "version": "11.0.0",
   "description": "Table feature for CKEditor 5.",
   "keywords": [
     "ckeditor",
     "ckeditor5",
     "ckeditor 5",
-    "ckeditor5-feature"
+    "ckeditor5-feature",
+    "ckeditor5-plugin"
   ],
   "dependencies": {
-    "@ckeditor/ckeditor5-core": "^11.0.0",
-    "@ckeditor/ckeditor5-engine": "^10.2.0",
-    "@ckeditor/ckeditor5-ui": "^11.0.0",
-    "@ckeditor/ckeditor5-widget": "^10.2.0"
+    "@ckeditor/ckeditor5-core": "^11.0.1",
+    "@ckeditor/ckeditor5-engine": "^11.0.0",
+    "@ckeditor/ckeditor5-ui": "^11.1.0",
+    "@ckeditor/ckeditor5-widget": "^10.3.0"
   },
   "devDependencies": {
-    "@ckeditor/ckeditor5-alignment": "^10.0.2",
-    "@ckeditor/ckeditor5-block-quote": "^10.0.2",
-    "@ckeditor/ckeditor5-clipboard": "^10.0.2",
-    "@ckeditor/ckeditor5-editor-classic": "^11.0.0",
-    "@ckeditor/ckeditor5-image": "^10.2.0",
-    "@ckeditor/ckeditor5-list": "^11.0.1",
-    "@ckeditor/ckeditor5-paragraph": "^10.0.2",
-    "@ckeditor/ckeditor5-undo": "^10.0.2",
-    "@ckeditor/ckeditor5-utils": "^10.2.0",
-    "eslint": "^4.15.0",
+    "@ckeditor/ckeditor5-alignment": "^10.0.3",
+    "@ckeditor/ckeditor5-block-quote": "^10.1.0",
+    "@ckeditor/ckeditor5-clipboard": "^10.0.3",
+    "@ckeditor/ckeditor5-editor-classic": "^11.0.1",
+    "@ckeditor/ckeditor5-image": "^11.0.0",
+    "@ckeditor/ckeditor5-list": "^11.0.2",
+    "@ckeditor/ckeditor5-media-embed": "^10.0.0",
+    "@ckeditor/ckeditor5-paragraph": "^10.0.3",
+    "@ckeditor/ckeditor5-typing": "^11.0.1",
+    "@ckeditor/ckeditor5-undo": "^10.0.3",
+    "@ckeditor/ckeditor5-utils": "^11.0.0",
+    "eslint": "^5.5.0",
     "eslint-config-ckeditor5": "^1.0.7",
     "husky": "^0.14.3",
     "lint-staged": "^7.0.0"

+ 12 - 12
packages/ckeditor5-table/src/commands/insertcolumncommand.js

@@ -14,16 +14,16 @@ import TableUtils from '../tableutils';
 /**
  * The insert column command.
  *
- * The command is registered by {@link module:table/tableediting~TableEditing} as `'insertTableColumnBefore'` and
- * `'insertTableColumnAfter'` editor commands.
+ * The command is registered by {@link module:table/tableediting~TableEditing} as `'insertTableColumnLeft'` and
+ * `'insertTableColumnRight'` editor commands.
  *
- * To insert a column before the selected cell, execute the following command:
+ * To insert a column to the left of the selected cell, execute the following command:
  *
- *		editor.execute( 'insertTableColumnBefore' );
+ *		editor.execute( 'insertTableColumnLeft' );
  *
- * To insert a column after the selected cell, execute the following command:
+ * To insert a column to the right of the selected cell, execute the following command:
  *
- *		editor.execute( 'insertTableColumnAfter' );
+ *		editor.execute( 'insertTableColumnRight' );
  *
  * @extends module:core/command~Command
  */
@@ -33,8 +33,8 @@ export default class InsertColumnCommand extends Command {
 	 *
 	 * @param {module:core/editor/editor~Editor} editor An editor on which this command will be used.
 	 * @param {Object} options
-	 * @param {String} [options.order="after"] The order of insertion relative to the column in which the caret is located.
-	 * Possible values: `"after"` and `"before"`.
+	 * @param {String} [options.order="right"] The order of insertion relative to the column in which the caret is located.
+	 * Possible values: `"left"` and `"right"`.
 	 */
 	constructor( editor, options = {} ) {
 		super( editor );
@@ -45,7 +45,7 @@ export default class InsertColumnCommand extends Command {
 		 * @readonly
 		 * @member {String} module:table/commands/insertcolumncommand~InsertColumnCommand#order
 		 */
-		this.order = options.order || 'after';
+		this.order = options.order || 'right';
 	}
 
 	/**
@@ -62,8 +62,8 @@ export default class InsertColumnCommand extends Command {
 	/**
 	 * Executes the command.
 	 *
-	 * Depending on the command's {@link #order} value, it inserts a column `'before'` or `'after'` the column in which the selection is
-	 * set.
+	 * Depending on the command's {@link #order} value, it inserts a column to the `'left'` or `'right'` of the column
+	 * in which the selection is set.
 	 *
 	 * @fires execute
 	 */
@@ -78,7 +78,7 @@ export default class InsertColumnCommand extends Command {
 		const table = tableCell.parent.parent;
 
 		const { column } = tableUtils.getCellLocation( tableCell );
-		const insertAt = this.order === 'after' ? column + 1 : column;
+		const insertAt = this.order === 'right' ? column + 1 : column;
 
 		tableUtils.insertColumns( table, { columns: 1, at: insertAt } );
 	}

+ 6 - 7
packages/ckeditor5-table/src/commands/inserttablecommand.js

@@ -8,7 +8,7 @@
  */
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
+import { findOptimalInsertionPosition } from '@ckeditor/ckeditor5-widget/src/utils';
 import TableUtils from '../tableutils';
 
 /**
@@ -54,15 +54,14 @@ export default class InsertTableCommand extends Command {
 		const rows = parseInt( options.rows ) || 2;
 		const columns = parseInt( options.columns ) || 2;
 
-		const firstPosition = selection.getFirstPosition();
-
-		const isRoot = firstPosition.parent === firstPosition.root;
-		const insertPosition = isRoot ? Position.createAt( firstPosition ) : Position.createAfter( firstPosition.parent );
+		const insertPosition = findOptimalInsertionPosition( selection, model );
 
 		model.change( writer => {
-			const table = tableUtils.createTable( insertPosition, rows, columns );
+			const table = tableUtils.createTable( writer, rows, columns );
+
+			model.insertContent( table, insertPosition );
 
-			writer.setSelection( Position.createAt( table.getChild( 0 ).getChild( 0 ).getChild( 0 ) ) );
+			writer.setSelection( writer.createPositionAt( table.getNodeByPath( [ 0, 0, 0 ] ), 0 ) );
 		} );
 	}
 }

+ 0 - 0
packages/ckeditor5-table/src/commands/mergecellcommand.js


+ 4 - 4
packages/ckeditor5-table/src/commands/removerowcommand.js

@@ -8,8 +8,6 @@
  */
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 
 import TableWalker from '../tablewalker';
 import { findAncestor, updateNumericAttribute } from './utils';
@@ -87,9 +85,11 @@ export default class RemoveRowCommand extends Command {
 			for ( const { row, column, cell } of [ ...tableWalker ] ) {
 				if ( cellsToMove.has( column ) ) {
 					const { cell: cellToMove, rowspanToSet } = cellsToMove.get( column );
-					const targetPosition = previousCell ? Position.createAfter( previousCell ) : Position.createAt( table.getChild( row ) );
+					const targetPosition = previousCell ?
+						writer.createPositionAfter( previousCell ) :
+						writer.createPositionAt( table.getChild( row ), 0 );
 
-					writer.move( Range.createOn( cellToMove ), targetPosition );
+					writer.move( writer.createRangeOn( cellToMove ), targetPosition );
 					updateNumericAttribute( 'rowspan', rowspanToSet, cellToMove, writer );
 
 					previousCell = cellToMove;

+ 1 - 2
packages/ckeditor5-table/src/commands/setheaderrowcommand.js

@@ -8,7 +8,6 @@
  */
 
 import Command from '@ckeditor/ckeditor5-core/src/command';
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
 
 import { createEmptyTableCell, findAncestor, updateNumericAttribute } from './utils';
 import TableWalker from '../tablewalker';
@@ -169,7 +168,7 @@ function splitHorizontally( tableCell, headingRows, writer ) {
 
 		if ( columnIndex !== undefined && columnIndex === column && row === endRow ) {
 			const tableRow = table.getChild( row );
-			const tableCellPosition = Position.createFromParentAndOffset( tableRow, cellIndex );
+			const tableCellPosition = writer.createPositionAt( tableRow, cellIndex );
 
 			createEmptyTableCell( writer, tableCellPosition, attributes );
 		}

+ 31 - 25
packages/ckeditor5-table/src/converters/downcast.js

@@ -7,8 +7,6 @@
  * @module table/converters/downcast
  */
 
-import ViewPosition from '@ckeditor/ckeditor5-engine/src/view/position';
-import ViewRange from '@ckeditor/ckeditor5-engine/src/view/range';
 import TableWalker from './../tablewalker';
 import { toWidgetEditable } from '@ckeditor/ckeditor5-widget/src/utils';
 import { toTableWidget } from '../utils';
@@ -38,7 +36,7 @@ export function downcastInsertTable( options = {} ) {
 
 		const figureElement = conversionApi.writer.createContainerElement( 'figure', { class: 'table' } );
 		const tableElement = conversionApi.writer.createContainerElement( 'table' );
-		conversionApi.writer.insert( ViewPosition.createAt( figureElement ), tableElement );
+		conversionApi.writer.insert( conversionApi.writer.createPositionAt( figureElement, 0 ), tableElement );
 
 		let tableWidget;
 
@@ -65,7 +63,7 @@ export function downcastInsertTable( options = {} ) {
 			// Consume table cell - it will be always consumed as we convert whole table at once.
 			conversionApi.consumable.consume( cell, 'insert' );
 
-			const insertPosition = ViewPosition.createAt( trElement, 'end' );
+			const insertPosition = conversionApi.writer.createPositionAt( trElement, 'end' );
 
 			createViewTableCellElement( tableWalkerValue, tableAttributes, insertPosition, conversionApi, options );
 		}
@@ -113,7 +111,7 @@ export function downcastInsertRow( options = {} ) {
 			// Consume table cell - it will be always consumed as we convert whole row at once.
 			conversionApi.consumable.consume( tableWalkerValue.cell, 'insert' );
 
-			const insertPosition = ViewPosition.createAt( trElement, 'end' );
+			const insertPosition = conversionApi.writer.createPositionAt( trElement, 'end' );
 
 			createViewTableCellElement( tableWalkerValue, tableAttributes, insertPosition, conversionApi, options );
 		}
@@ -151,7 +149,7 @@ export function downcastInsertCell( options = {} ) {
 		for ( const tableWalkerValue of tableWalker ) {
 			if ( tableWalkerValue.cell === tableCell ) {
 				const trElement = conversionApi.mapper.toViewElement( tableRow );
-				const insertPosition = ViewPosition.createAt( trElement, tableRow.getChildIndex( tableCell ) );
+				const insertPosition = conversionApi.writer.createPositionAt( trElement, tableRow.getChildIndex( tableCell ) );
 
 				createViewTableCellElement( tableWalkerValue, tableAttributes, insertPosition, conversionApi, options );
 
@@ -215,7 +213,7 @@ export function downcastTableHeadingRowsChange( options = {} ) {
 				.reverse(); // The rows will be moved from <thead> to <tbody> in reverse order at the beginning of a <tbody>.
 
 			const viewTableBody = getOrCreateTableSection( 'tbody', viewTable, conversionApi );
-			moveViewRowsToTableSection( rowsToMove, viewTableBody, conversionApi );
+			moveViewRowsToTableSection( rowsToMove, viewTableBody, conversionApi, 0 );
 
 			// Check if cells moved from <thead> to <tbody> requires renaming to <td> as this depends on current heading columns attribute.
 			const tableWalker = new TableWalker( table, { startRow: newRows ? newRows - 1 : newRows, endRow: oldRows - 1 } );
@@ -286,23 +284,25 @@ export function downcastRemoveRow() {
 	return dispatcher => dispatcher.on( 'remove:tableRow', ( evt, data, conversionApi ) => {
 		// Prevent default remove converter.
 		evt.stop();
+		const viewWriter = conversionApi.writer;
+		const mapper = conversionApi.mapper;
 
-		const viewStart = conversionApi.mapper.toViewPosition( data.position ).getLastMatchingPosition( value => !value.item.is( 'tr' ) );
+		const viewStart = mapper.toViewPosition( data.position ).getLastMatchingPosition( value => !value.item.is( 'tr' ) );
 		const viewItem = viewStart.nodeAfter;
 		const tableSection = viewItem.parent;
 
 		// Remove associated <tr> from the view.
-		const removeRange = ViewRange.createOn( viewItem );
-		const removed = conversionApi.writer.remove( removeRange );
+		const removeRange = viewWriter.createRangeOn( viewItem );
+		const removed = viewWriter.remove( removeRange );
 
-		for ( const child of ViewRange.createIn( removed ).getItems() ) {
-			conversionApi.mapper.unbindViewElement( child );
+		for ( const child of viewWriter.createRangeIn( removed ).getItems() ) {
+			mapper.unbindViewElement( child );
 		}
 
 		// Check if table section has any children left - if not remove it from the view.
 		if ( !tableSection.childCount ) {
 			// No need to unbind anything as table section is not represented in the model.
-			conversionApi.writer.remove( ViewRange.createOn( tableSection ) );
+			viewWriter.remove( viewWriter.createRangeOn( tableSection ) );
 		}
 	}, { priority: 'higher' } );
 }
@@ -316,6 +316,7 @@ export function downcastRemoveRow() {
 // @param {Object} conversionApi
 // @param {Boolean} asWidget
 function renameViewTableCell( tableCell, desiredCellElementName, conversionApi, asWidget ) {
+	const viewWriter = conversionApi.writer;
 	const viewCell = conversionApi.mapper.toViewElement( tableCell );
 
 	// View cell might be not yet converted - skip it as it will be properly created by cell converter later on.
@@ -326,14 +327,14 @@ function renameViewTableCell( tableCell, desiredCellElementName, conversionApi,
 	let renamedCell;
 
 	if ( asWidget ) {
-		const editable = conversionApi.writer.createEditableElement( desiredCellElementName, viewCell.getAttributes() );
-		renamedCell = toWidgetEditable( editable, conversionApi.writer );
+		const editable = viewWriter.createEditableElement( desiredCellElementName, viewCell.getAttributes() );
+		renamedCell = toWidgetEditable( editable, viewWriter );
 
-		conversionApi.writer.insert( ViewPosition.createAfter( viewCell ), renamedCell );
-		conversionApi.writer.move( ViewRange.createIn( viewCell ), ViewPosition.createAt( renamedCell ) );
-		conversionApi.writer.remove( ViewRange.createOn( viewCell ) );
+		viewWriter.insert( viewWriter.createPositionAfter( viewCell ), renamedCell );
+		viewWriter.move( viewWriter.createRangeIn( viewCell ), viewWriter.createPositionAt( renamedCell, 0 ) );
+		viewWriter.remove( viewWriter.createRangeOn( viewCell ) );
 	} else {
-		renamedCell = conversionApi.writer.rename( viewCell, desiredCellElementName );
+		renamedCell = viewWriter.rename( desiredCellElementName, viewCell );
 	}
 
 	conversionApi.mapper.bindElements( tableCell, renamedCell );
@@ -381,7 +382,7 @@ function createViewTableCellElement( tableWalkerValue, tableAttributes, insertPo
 
 	if ( isSingleParagraph ) {
 		const innerParagraph = tableCell.getChild( 0 );
-		const paragraphInsertPosition = ViewPosition.createAt( cellElement, 'end' );
+		const paragraphInsertPosition = conversionApi.writer.createPositionAt( cellElement, 'end' );
 
 		conversionApi.consumable.consume( innerParagraph, 'insert' );
 
@@ -423,7 +424,7 @@ function getOrCreateTr( tableRow, rowIndex, tableSection, conversionApi ) {
 		const headingRows = tableRow.parent.getAttribute( 'headingRows' ) || 0;
 		const offset = headingRows > 0 && rowIndex >= headingRows ? rowIndex - headingRows : rowIndex;
 
-		const position = ViewPosition.createAt( tableSection, offset );
+		const position = conversionApi.writer.createPositionAt( tableSection, offset );
 		conversionApi.writer.insert( position, trElement );
 	}
 
@@ -497,7 +498,9 @@ function getExistingTableSectionElement( sectionName, tableElement ) {
 function createTableSection( sectionName, tableElement, conversionApi ) {
 	const tableChildElement = conversionApi.writer.createContainerElement( sectionName );
 
-	conversionApi.writer.insert( ViewPosition.createAt( tableElement, sectionName == 'tbody' ? 'end' : 'start' ), tableChildElement );
+	const insertPosition = conversionApi.writer.createPositionAt( tableElement, sectionName == 'tbody' ? 'end' : 0 );
+
+	conversionApi.writer.insert( insertPosition, tableChildElement );
 
 	return tableChildElement;
 }
@@ -511,7 +514,7 @@ function removeTableSectionIfEmpty( sectionName, tableElement, conversionApi ) {
 	const tableSection = getExistingTableSectionElement( sectionName, tableElement );
 
 	if ( tableSection && tableSection.childCount === 0 ) {
-		conversionApi.writer.remove( ViewRange.createOn( tableSection ) );
+		conversionApi.writer.remove( conversionApi.writer.createRangeOn( tableSection ) );
 	}
 }
 
@@ -522,14 +525,17 @@ function removeTableSectionIfEmpty( sectionName, tableElement, conversionApi ) {
 // @param {Array.<module:engine/model/element~Element>} rowsToMove
 // @param {module:engine/view/element~Element} viewTableSection
 // @param {Object} conversionApi
-// @param {Number|'end'|'before'|'after'} [offset=0] Offset or one of the flags.
+// @param {Number|'end'|'before'|'after'} offset Offset or one of the flags.
 function moveViewRowsToTableSection( rowsToMove, viewTableSection, conversionApi, offset ) {
 	for ( const tableRow of rowsToMove ) {
 		const viewTableRow = conversionApi.mapper.toViewElement( tableRow );
 
 		// View table row might be not yet converted - skip it as it will be properly created by cell converter later on.
 		if ( viewTableRow ) {
-			conversionApi.writer.move( ViewRange.createOn( viewTableRow ), ViewPosition.createAt( viewTableSection, offset ) );
+			conversionApi.writer.move(
+				conversionApi.writer.createRangeOn( viewTableRow ),
+				conversionApi.writer.createPositionAt( viewTableSection, offset )
+			);
 		}
 	}
 }

+ 112 - 0
packages/ckeditor5-table/src/converters/table-cell-content-post-fixer.js

@@ -0,0 +1,112 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+/**
+ * @module table/converters/table-cell-content-post-fixer
+ */
+
+/**
+ * Injects a table cell post-fixer into the model.
+ *
+ * The role of the table post-fixer is to ensure that the table cells have the correct content
+ * after a {@link module:engine/model/model~Model#change `change()`} block was executed.
+ *
+ * A table cells must contains at least one block as a child. The empty table cell will have empty `<paragraph>` as a child.
+ *
+ *        <table>
+ *            <tableRow>
+ *                <tableCell></tableCell>
+ *            </tableRow>
+ *        </table>
+ *
+ * Will be fixed to:
+ *
+ *        <table>
+ *            <tableRow>
+ *                <tableCell><paragraph></paragraph></tableCell>
+ *            </tableRow>
+ *        </table>
+ *
+ * @param {module:engine/model/model~Model} model
+ */
+export default function injectTableCellContentPostFixer( model ) {
+	model.document.registerPostFixer( writer => tableCellContentsPostFixer( writer, model ) );
+}
+
+// The table cell contents post-fixer.
+//
+// @param {module:engine/model/writer~Writer} writer
+// @param {module:engine/model/model~Model} model
+function tableCellContentsPostFixer( writer, model ) {
+	const changes = model.document.differ.getChanges();
+
+	let wasFixed = false;
+
+	for ( const entry of changes ) {
+		// Enforce paragraph in tableCell even after other feature remove its contents.
+		if ( entry.type == 'remove' && entry.position.parent.is( 'tableCell' ) ) {
+			wasFixed = fixTableCellContent( entry.position.parent, writer ) || wasFixed;
+		}
+
+		// Analyze table cells on insertion.
+		if ( entry.type == 'insert' ) {
+			if ( entry.name == 'table' ) {
+				wasFixed = fixTable( entry.position.nodeAfter, writer ) || wasFixed;
+			}
+
+			if ( entry.name == 'tableRow' ) {
+				wasFixed = fixTableRow( entry.position.nodeAfter, writer ) || wasFixed;
+			}
+
+			if ( entry.name == 'tableCell' ) {
+				wasFixed = fixTableCellContent( entry.position.nodeAfter, writer ) || wasFixed;
+			}
+		}
+	}
+
+	return wasFixed;
+}
+
+// Fixes all table cells in a table.
+//
+// @param {module:engine/model/element~Element} table
+// @param {module:engine/model/writer~Writer} writer
+function fixTable( table, writer ) {
+	let wasFixed = false;
+
+	for ( const row of table.getChildren() ) {
+		wasFixed = fixTableRow( row, writer ) || wasFixed;
+	}
+
+	return wasFixed;
+}
+
+// Fixes all table cells in a table row.
+//
+// @param {module:engine/model/element~Element} tableRow
+// @param {module:engine/model/writer~Writer} writer
+function fixTableRow( tableRow, writer ) {
+	let wasFixed = false;
+
+	for ( const tableCell of tableRow.getChildren() ) {
+		wasFixed = fixTableCellContent( tableCell, writer ) || wasFixed;
+	}
+
+	return wasFixed;
+}
+
+// Fixes all table cell content by adding paragraph to a table cell without any child.
+//
+// @param {module:engine/model/element~Element} table
+// @param {module:engine/model/writer~Writer} writer
+function fixTableCellContent( tableCell, writer ) {
+	if ( tableCell.childCount == 0 ) {
+		writer.insertElement( 'paragraph', tableCell );
+
+		return true;
+	}
+
+	return false;
+}

+ 28 - 30
packages/ckeditor5-table/src/converters/table-post-fixer.js → packages/ckeditor5-table/src/converters/table-layout-post-fixer.js

@@ -4,23 +4,23 @@
  */
 
 /**
- * @module table/converters/table-post-fixer
+ * @module table/converters/table-layout-post-fixer
  */
 
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
 import { createEmptyTableCell, findAncestor, updateNumericAttribute } from './../commands/utils';
 import TableWalker from './../tablewalker';
 
 /**
- * Injects a table post-fixer into the model.
+ * Injects a table layout post-fixer into the model.
  *
- * The role of the table post-fixer is to ensure that the table rows have the correct structure
+ * The role of the table layout post-fixer is to ensure that the table rows have the correct structure
  * after a {@link module:engine/model/model~Model#change `change()`} block was executed.
  *
  * The correct structure means that:
  *
  * * All table rows have the same size.
  * * None of a table cells that extend vertically beyond their section (either header or body).
+ * * A table cell has always at least one element as child.
  *
  * If the table structure is not correct, the post-fixer will automatically correct it in two steps:
  *
@@ -35,12 +35,12 @@ import TableWalker from './../tablewalker';
  *
  *		<table headingRows="1">
  *			<tableRow>
- *				<tableCell rowspan="2">FOO</tableCell>
- *				<tableCell colspan="2">BAR</tableCell>
+ *				<tableCell rowspan="2"><paragraph>FOO</paragraph></tableCell>
+ *				<tableCell colspan="2"><paragraph>BAR</paragraph></tableCell>
  *			</tableRow>
  *			<tableRow>
- *				<tableCell>BAZ</tableCell>
- *				<tableCell>XYZ</tableCell>
+ *				<tableCell><paragraph>BAZ</paragraph></tableCell>
+ *				<tableCell><paragraph>XYZ</paragraph></tableCell>
  *			</tableRow>
  *		</table>
  *
@@ -55,8 +55,8 @@ import TableWalker from './../tablewalker';
  *			</thead>
  *			<tbody>
  *				<tr>
- *					<td>BAZ<td>
- *					<td>XYZ<td>
+ *					<td>BAZ</td>
+ *					<td>XYZ</td>
  *				</tr>
  *			</tbody>
  *		</table>
@@ -90,8 +90,8 @@ import TableWalker from './../tablewalker';
  *			</thead>
  *			<tbody>
  *				<tr>
- *					<td>BAZ<td>
- *					<td>XYZ<td>
+ *					<td>BAZ</td>
+ *					<td>XYZ</td>
  *				</tr>
  *			</tbody>
  *		</table>
@@ -113,8 +113,8 @@ import TableWalker from './../tablewalker';
  *					<td>12</td>
  *				</tr>
  *				<tr>
- *					<td>21<td>
- *					<td>22<td>
+ *					<td>21</td>
+ *					<td>22</td>
  *				</tr>
  *			</tbody>
  *		</table>
@@ -156,8 +156,8 @@ import TableWalker from './../tablewalker';
  *					<td>(empty, inserted by A)</td>
  *				</tr>
  *				<tr>
- *					<td>21<td>
- *					<td>22<td>
+ *					<td>21</td>
+ *					<td>22</td>
  *					<td>(empty, inserted by A)</td>
  *				</tr>
  *				<tr>
@@ -197,32 +197,31 @@ import TableWalker from './../tablewalker';
  *				<tr>
  *					<td>11</td>
  *					<td>12</td>
- *					<td>(empty, inserted by a post-fixer after undo)<td>
+ *					<td>(empty, inserted by a post-fixer after undo)</td>
  *				</tr>
  *				<tr>
- *					<td>21<td>
- *					<td>22<td>
- *					<td>(empty, inserted by a post-fixer after undo)<td>
+ *					<td>21</td>
+ *					<td>22</td>
+ *					<td>(empty, inserted by a post-fixer after undo)</td>
  *				</tr>
  *				<tr>
- *					<td>(empty, inserted by B)<td>
- *					<td>(empty, inserted by B)<td>
- *					<td>(empty, inserted by a post-fixer)<td>
+ *					<td>(empty, inserted by B)</td>
+ *					<td>(empty, inserted by B)</td>
+ *					<td>(empty, inserted by a post-fixer)</td>
  *				</tr>
  *			</tbody>
  *		</table>
- *
  * @param {module:engine/model/model~Model} model
  */
-export default function injectTablePostFixer( model ) {
-	model.document.registerPostFixer( writer => tablePostFixer( writer, model ) );
+export default function injectTableLayoutPostFixer( model ) {
+	model.document.registerPostFixer( writer => tableLayoutPostFixer( writer, model ) );
 }
 
-// The table post-fixer.
+// The table layout post-fixer.
 //
 // @param {module:engine/model/writer~Writer} writer
 // @param {module:engine/model/model~Model} model
-function tablePostFixer( writer, model ) {
+function tableLayoutPostFixer( writer, model ) {
 	const changes = model.document.differ.getChanges();
 
 	let wasFixed = false;
@@ -233,7 +232,6 @@ function tablePostFixer( writer, model ) {
 	for ( const entry of changes ) {
 		let table;
 
-		// Fix table on table insert.
 		if ( entry.name == 'table' && entry.type == 'insert' ) {
 			table = entry.position.nodeAfter;
 		}
@@ -304,7 +302,7 @@ function fixTableRowsSizes( table, writer ) {
 
 			if ( columnsToInsert ) {
 				for ( let i = 0; i < columnsToInsert; i++ ) {
-					createEmptyTableCell( writer, Position.createAt( table.getChild( rowIndex ), 'end' ) );
+					createEmptyTableCell( writer, writer.createPositionAt( table.getChild( rowIndex ), 'end' ) );
 				}
 
 				wasFixed = true;

+ 75 - 68
packages/ckeditor5-table/src/converters/tablecell-post-fixer.js

@@ -71,7 +71,7 @@
  * @param {module:engine/controller/editingcontroller~EditingController} editing
  */
 export default function injectTableCellPostFixer( model, editing ) {
-	editing.view.document.registerPostFixer( writer => tableCellPostFixer( writer, model, editing.mapper ) );
+	editing.view.document.registerPostFixer( writer => tableCellPostFixer( writer, model, editing.mapper, editing.view ) );
 }
 
 // The table cell post-fixer.
@@ -79,94 +79,101 @@ export default function injectTableCellPostFixer( model, editing ) {
 // @param {module:engine/view/writer~Writer} writer
 // @param {module:engine/model/model~Model} model
 // @param {module:engine/conversion/mapper~Mapper} mapper
-function tableCellPostFixer( writer, model, mapper ) {
-	const changes = model.document.differ.getChanges();
+function tableCellPostFixer( writer, model, mapper, view ) {
 	let wasFixed = false;
 
-	// While this is view post fixer only nodes that changed are worth investigating.
-	for ( const entry of changes ) {
-		// Attribute change - check if it is single paragraph inside table cell that has attributes changed.
-		if ( entry.type == 'attribute' && entry.range.start.parent.name == 'tableCell' ) {
-			const tableCell = entry.range.start.parent;
-
-			if ( tableCell.childCount === 1 ) {
-				const singleChild = tableCell.getChild( 0 );
-				const renameTo = Array.from( singleChild.getAttributes() ).length ? 'p' : 'span';
-
-				wasFixed = renameParagraphIfDifferent( singleChild, renameTo, writer, model, mapper ) || wasFixed;
-			}
-		} else {
-			// Check all nodes inside table cell on insert/remove operations (also other blocks).
-			const parent = entry.position && entry.position.parent;
-
-			if ( parent && parent.is( 'tableCell' ) ) {
-				const renameTo = parent.childCount > 1 ? 'p' : 'span';
-
-				for ( const child of parent.getChildren() ) {
-					wasFixed = renameParagraphIfDifferent( child, renameTo, writer, model, mapper ) || wasFixed;
-				}
-			}
-		}
+	const elementsToCheck = getElementsToCheck( view );
+
+	for ( const element of elementsToCheck ) {
+		wasFixed = ensureProperElementName( element, mapper, writer ) || wasFixed;
+	}
+
+	// Selection in the view might not be updated to renamed elements. Happens mostly when other feature inserts paragraph to the table cell
+	// (ie. when deleting table cell contents) and sets selection to it while table-post fixer changes view <p> to <span> element.
+	// The view.selection would have outdated nodes.
+	if ( wasFixed ) {
+		updateRangesInViewSelection( model.document.selection, mapper, writer );
 	}
 
 	return wasFixed;
 }
 
-// Renames associated view element to a desired one. It will only rename if:
-// - model element is a paragraph
-// - view element is converted (mapped)
-// - view element has different name then requested.
+// Returns view elements changed in current view.change() block.
 //
-// @param {module:engine/model/element~Element} modelElement
-// @param {String} desiredElementName
-// @param {module:engine/view/writer~Writer} writer
-// @param {module:engine/model/model~Model} model
-// @param {module:engine/conversion/mapper~Mapper} mapper
-function renameParagraphIfDifferent( modelElement, desiredElementName, writer, model, mapper ) {
-	// Only rename paragraph elements.
-	if ( !modelElement.is( 'paragraph' ) ) {
-		return false;
-	}
+// **Note**: Currently it uses private property of the view: _renderer to get changed view elements to check.
+//
+// @param {module:engine/view/view~View} view
+function getElementsToCheck( view ) {
+	const elementsWithChangedAttributes = Array.from( view._renderer.markedAttributes )
+		.filter( el => !!el.parent )
+		.filter( isSpanOrP )
+		.filter( el => isTdOrTh( el.parent ) );
+
+	const changedChildren = Array.from( view._renderer.markedChildren )
+		.filter( el => !!el.parent )
+		.filter( isTdOrTh )
+		.reduce( ( prev, element ) => {
+			const childrenToCheck = Array.from( element.getChildren() ).filter( isSpanOrP );
+
+			return [ ...prev, ...childrenToCheck ];
+		}, [] );
+
+	return [ ...elementsWithChangedAttributes, ...changedChildren ];
+}
 
-	const viewElement = mapper.toViewElement( modelElement );
+// This method checks if view element for model's <paragraph> was properly converter.
+// Paragraph should be either
+// - span: for single paragraph with no attributes.
+// - p   : in other cases.
+function ensureProperElementName( currentViewElement, mapper, writer ) {
+	const modelParagraph = mapper.toModelElement( currentViewElement );
+	const expectedViewElementName = getExpectedElementName( modelParagraph.parent, modelParagraph );
 
-	// Only rename converted elements which aren't desired ones.
-	if ( !viewElement || viewElement.name === desiredElementName ) {
-		return false;
-	}
+	if ( currentViewElement.name !== expectedViewElementName ) {
+		// Unbind current view element as it should be cleared from mapper.
+		mapper.unbindViewElement( currentViewElement );
 
-	// After renaming element in the view by a post-fixer the selection would have references to the previous element.
-	const selection = model.document.selection;
-	const shouldFixSelection = checkSelectionForRenamedElement( selection, modelElement );
+		const renamedViewElement = writer.rename( expectedViewElementName, currentViewElement );
 
-	// Unbind current view element as it should be cleared from mapper.
-	mapper.unbindViewElement( viewElement );
-	const renamedViewElement = writer.rename( viewElement, desiredElementName );
-	// Bind paragraph inside table cell to the renamed view element.
-	mapper.bindElements( modelElement, renamedViewElement );
+		// Bind paragraph inside table cell to the renamed view element.
+		mapper.bindElements( modelParagraph, renamedViewElement );
 
-	if ( shouldFixSelection ) {
-		// Re-create view selection based on model selection.
-		updateRangesInViewSelection( selection, mapper, writer );
+		return true;
 	}
 
-	return true;
+	return false;
 }
 
-// Checks if model selection contains renamed element.
+// Expected view element name depends on model elements:
+// - <paragraph> with any attribute set should be rendered as <p>
+// - all <paragraphs> in <tableCell> that has more then one children should be rendered as <p>
+// - an only <paragraph> child with no attributes should be rendered as <span>
 //
-// @param {module:engine/model/selection~Selection} selection
-// @param {module:engine/model/element~Element} modelElement
-// @returns {boolean}
-function checkSelectionForRenamedElement( selection, modelElement ) {
-	return !![ ...selection.getSelectedBlocks() ].find( block => block === modelElement );
+// @param {module:engine/model/element~Element} tableCell
+// @param {module:engine/model/element~Element} paragraph
+// @returns {String}
+function getExpectedElementName( tableCell, paragraph ) {
+	const isOnlyChild = tableCell.childCount > 1;
+	const hasAttributes = !![ ...paragraph.getAttributes() ].length;
+
+	return ( isOnlyChild || hasAttributes ) ? 'p' : 'span';
 }
 
-// Re-create view selection from model selection.
+// Method to filter out <span> and <p> elements.
 //
-// @param {module:engine/model/selection~Selection} selection
-// @param {module:engine/view/writer~Writer} writer
-// @param {module:engine/conversion/mapper~Mapper} mapper
+// @param {module:engine/view/element~Element} element
+function isSpanOrP( element ) {
+	return element.is( 'p' ) || element.is( 'span' );
+}
+
+// Method to filter out <td> and <th> elements.
+//
+// @param {module:engine/view/element~Element} element
+function isTdOrTh( element ) {
+	return element.is( 'td' ) || element.is( 'th' );
+}
+
+// Resets view selections based on model selection.
 function updateRangesInViewSelection( selection, mapper, writer ) {
 	const fixedRanges = Array.from( selection.getRanges() )
 		.map( range => mapper.toViewRange( range ) );

+ 20 - 19
packages/ckeditor5-table/src/converters/upcasttable.js

@@ -7,8 +7,6 @@
  * @module table/converters/upcasttable
  */
 
-import ModelRange from '@ckeditor/ckeditor5-engine/src/model/range';
-import ModelPosition from '@ckeditor/ckeditor5-engine/src/model/position';
 import { createEmptyTableCell } from '../commands/utils';
 
 /**
@@ -56,24 +54,24 @@ export default function upcastTable() {
 
 			if ( rows.length ) {
 				// Upcast table rows in proper order (heading rows first).
-				rows.forEach( row => conversionApi.convertItem( row, ModelPosition.createAt( table, 'end' ) ) );
+				rows.forEach( row => conversionApi.convertItem( row, conversionApi.writer.createPositionAt( table, 'end' ) ) );
 			} else {
 				// Create one row and one table cell for empty table.
 				const row = conversionApi.writer.createElement( 'tableRow' );
-				conversionApi.writer.insert( row, ModelPosition.createAt( table, 'end' ) );
+				conversionApi.writer.insert( row, conversionApi.writer.createPositionAt( table, 'end' ) );
 
-				createEmptyTableCell( conversionApi.writer, ModelPosition.createAt( row, 'end' ) );
+				createEmptyTableCell( conversionApi.writer, conversionApi.writer.createPositionAt( row, 'end' ) );
 			}
 
 			// Set conversion result range.
-			data.modelRange = new ModelRange(
+			data.modelRange = conversionApi.writer.createRange(
 				// Range should start before inserted element
-				ModelPosition.createBefore( table ),
+				conversionApi.writer.createPositionBefore( table ),
 				// Should end after but we need to take into consideration that children could split our
 				// element, so we need to move range after parent of the last converted child.
 				// before: <allowed>[]</allowed>
 				// after: <allowed>[<converted><child></child></converted><child></child><converted>]</converted></allowed>
-				ModelPosition.createAfter( table )
+				conversionApi.writer.createPositionAfter( table )
 			);
 
 			// Now we need to check where the modelCursor should be.
@@ -82,7 +80,7 @@ export default function upcastTable() {
 			// before: <allowed><notAllowed>[]</notAllowed></allowed>
 			// after:  <allowed><notAllowed></notAllowed><converted></converted><notAllowed>[]</notAllowed></allowed>
 			if ( splitResult.cursorParent ) {
-				data.modelCursor = ModelPosition.createAt( splitResult.cursorParent );
+				data.modelCursor = conversionApi.writer.createPositionAt( splitResult.cursorParent, 0 );
 
 				// Otherwise just continue after inserted element.
 			} else {
@@ -115,24 +113,23 @@ export function upcastTableCell( elementName ) {
 			conversionApi.writer.insert( tableCell, splitResult.position );
 			conversionApi.consumable.consume( viewTableCell, { name: true } );
 
-			for ( const child of viewTableCell.getChildren() ) {
-				const { modelCursor } = conversionApi.convertItem( child, ModelPosition.createAt( tableCell, 'end' ) );
+			const modelCursor = conversionApi.writer.createPositionAt( tableCell, 0 );
+			conversionApi.convertChildren( viewTableCell, modelCursor );
 
-				// Ensure empty paragraph in table cell.
-				if ( modelCursor.parent.name == 'tableCell' && !modelCursor.parent.childCount ) {
-					conversionApi.writer.insertElement( 'paragraph', modelCursor );
-				}
+			// Ensure a paragraph in the model for empty table cells.
+			if ( !tableCell.childCount ) {
+				conversionApi.writer.insertElement( 'paragraph', modelCursor );
 			}
 
 			// Set conversion result range.
-			data.modelRange = new ModelRange(
+			data.modelRange = conversionApi.writer.createRange(
 				// Range should start before inserted element
-				ModelPosition.createBefore( tableCell ),
+				conversionApi.writer.createPositionBefore( tableCell ),
 				// Should end after but we need to take into consideration that children could split our
 				// element, so we need to move range after parent of the last converted child.
 				// before: <allowed>[]</allowed>
 				// after: <allowed>[<converted><child></child></converted><child></child><converted>]</converted></allowed>
-				ModelPosition.createAfter( tableCell )
+				conversionApi.writer.createPositionAfter( tableCell )
 			);
 
 			// Continue after inserted element.
@@ -183,7 +180,11 @@ function scanTable( viewTable ) {
 				firstTheadElement = tableChild;
 			}
 
-			for ( const tr of Array.from( tableChild.getChildren() ) ) {
+			// There might be some extra empty text nodes between the `tr`s.
+			// Make sure further code operates on `tr`s only. (#145)
+			const trs = Array.from( tableChild.getChildren() ).filter( el => el.is( 'element', 'tr' ) );
+
+			for ( const tr of trs ) {
 				// This <tr> is a child of a first <thead> element.
 				if ( tr.parent.name === 'thead' && tr.parent === firstTheadElement ) {
 					tableMeta.headingRows++;

+ 3 - 3
packages/ckeditor5-table/src/table.js

@@ -18,11 +18,11 @@ import '../theme/table.css';
 /**
  * The table plugin.
  *
- * It loads the {@link module:table/tableediting~TableEditing table editing feature}
- * and {@link module:table/tableui~TableUI table UI feature}.
- *
  * For a detailed overview, check the {@glink features/table Table feature documentation}.
  *
+ * This is a "glue" plugin which loads the {@link module:table/tableediting~TableEditing table editing feature}
+ * and {@link module:table/tableui~TableUI table UI feature}.
+ *
  * @extends module:core/plugin~Plugin
  */
 export default class Table extends Plugin {

+ 14 - 9
packages/ckeditor5-table/src/tableediting.js

@@ -9,7 +9,6 @@
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import { upcastElementToElement } from '@ckeditor/ckeditor5-engine/src/conversion/upcast-converters';
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 
 import upcastTable, { upcastTableCell } from './converters/upcasttable';
 import {
@@ -33,7 +32,8 @@ import MergeCellsCommand from './commands/mergecellscommand';
 import { findAncestor } from './commands/utils';
 import TableUtils from '../src/tableutils';
 
-import injectTablePostFixer from './converters/table-post-fixer';
+import injectTableLayoutPostFixer from './converters/table-layout-post-fixer';
+import injectTableCellContentsPostFixer from './converters/table-cell-content-post-fixer';
 import injectTableCellPostFixer from './converters/tablecell-post-fixer';
 
 import '../theme/tableediting.css';
@@ -81,9 +81,13 @@ export default class TableEditing extends Plugin {
 			}
 		} );
 
-		// Disallow image in table cell.
+		// Disallow image and media in table cell.
 		schema.addChildCheck( ( context, childDefinition ) => {
-			if ( childDefinition.name == 'image' && Array.from( context.getNames() ).includes( 'table' ) ) {
+			if ( !Array.from( context.getNames() ).includes( 'table' ) ) {
+				return;
+			}
+
+			if ( childDefinition.name == 'image' || childDefinition.name == 'media' ) {
 				return false;
 			}
 		} );
@@ -124,8 +128,8 @@ export default class TableEditing extends Plugin {
 		editor.commands.add( 'insertTable', new InsertTableCommand( editor ) );
 		editor.commands.add( 'insertTableRowAbove', new InsertRowCommand( editor, { order: 'above' } ) );
 		editor.commands.add( 'insertTableRowBelow', new InsertRowCommand( editor, { order: 'below' } ) );
-		editor.commands.add( 'insertTableColumnBefore', new InsertColumnCommand( editor, { order: 'before' } ) );
-		editor.commands.add( 'insertTableColumnAfter', new InsertColumnCommand( editor, { order: 'after' } ) );
+		editor.commands.add( 'insertTableColumnLeft', new InsertColumnCommand( editor, { order: 'left' } ) );
+		editor.commands.add( 'insertTableColumnRight', new InsertColumnCommand( editor, { order: 'right' } ) );
 
 		editor.commands.add( 'removeTableRow', new RemoveRowCommand( editor ) );
 		editor.commands.add( 'removeTableColumn', new RemoveColumnCommand( editor ) );
@@ -138,7 +142,8 @@ export default class TableEditing extends Plugin {
 		editor.commands.add( 'setTableColumnHeader', new SetHeaderColumnCommand( editor ) );
 		editor.commands.add( 'setTableRowHeader', new SetHeaderRowCommand( editor ) );
 
-		injectTablePostFixer( model );
+		injectTableLayoutPostFixer( model );
+		injectTableCellContentsPostFixer( model );
 
 		// Handle tab key navigation.
 		this.editor.keystrokes.set( 'Tab', ( ...args ) => this._handleTabOnSelectedTable( ...args ), { priority: 'low' } );
@@ -178,7 +183,7 @@ export default class TableEditing extends Plugin {
 			cancel();
 
 			editor.model.change( writer => {
-				writer.setSelection( Range.createIn( selectedElement.getChild( 0 ).getChild( 0 ).getChild( 0 ) ) );
+				writer.setSelection( writer.createRangeIn( selectedElement.getChild( 0 ).getChild( 0 ).getChild( 0 ) ) );
 			} );
 		}
 	}
@@ -246,7 +251,7 @@ export default class TableEditing extends Plugin {
 			}
 
 			editor.model.change( writer => {
-				writer.setSelection( Range.createIn( cellToFocus ) );
+				writer.setSelection( writer.createRangeIn( cellToFocus ) );
 			} );
 		};
 	}

+ 63 - 118
packages/ckeditor5-table/src/tabletoolbar.js

@@ -8,20 +8,20 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import ToolbarView from '@ckeditor/ckeditor5-ui/src/toolbar/toolbarview';
-import ContextualBalloon from '@ckeditor/ckeditor5-ui/src/panel/balloon/contextualballoon';
-import { isTableWidgetSelected, isTableContentSelected } from './utils';
-import { repositionContextualBalloon, getBalloonPositionData } from './ui/utils';
-
-const balloonClassName = 'ck-toolbar-container';
+import { isTableContentSelected, isTableWidgetSelected } from './utils';
+import WidgetToolbarRepository from '@ckeditor/ckeditor5-widget/src/widgettoolbarrepository';
 
 /**
- * The table toolbar class. It creates a table toolbar that shows up when the table widget is selected.
+ * The table toolbar class. It creates toolbars for the table feature and its content (for now only for a table cell content).
+ *
+ * Table toolbar shows up when a table widget is selected. Its components (e.g. buttons) are created based on the
+ * {@link module:table/table~TableConfig#toolbar `table.tableToolbar` configuration option}.
  *
- * Toolbar components are created using the editor {@link module:ui/componentfactory~ComponentFactory ComponentFactory}
- * based on the {@link module:core/editor/editor~Editor#config configuration} stored under `table.toolbar`.
+ * Table content toolbar shows up when the selection is inside the content of a table. It creates its component based on the
+ * {@link module:table/table~TableConfig#contentToolbar `table.contentToolbar` configuration option}.
  *
- * The toolbar uses the {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon}.
+ * Note that the old {@link module:table/table~TableConfig#toolbar `table.toolbar` configuration option} is deprecated
+ * and will be removed in the next major release.
  *
  * @extends module:core/plugin~Plugin
  */
@@ -30,7 +30,7 @@ export default class TableToolbar extends Plugin {
 	 * @inheritDoc
 	 */
 	static get requires() {
-		return [ ContextualBalloon ];
+		return [ WidgetToolbarRepository ];
 	}
 
 	/**
@@ -40,129 +40,56 @@ export default class TableToolbar extends Plugin {
 		return 'TableToolbar';
 	}
 
-	/**
-	 * @inheritDoc
-	 */
-	init() {
-		const editor = this.editor;
-		const balloonToolbar = editor.plugins.get( 'BalloonToolbar' );
-
-		// If the `BalloonToolbar` plugin is loaded, it should be disabled for tables
-		// which have their own toolbar to avoid duplication.
-		// https://github.com/ckeditor/ckeditor5-image/issues/110
-		if ( balloonToolbar ) {
-			this.listenTo( balloonToolbar, 'show', evt => {
-				if ( isTableWidgetSelected( editor.editing.view.document.selection ) ) {
-					evt.stop();
-				}
-			}, { priority: 'high' } );
-		}
-	}
-
 	/**
 	 * @inheritDoc
 	 */
 	afterInit() {
 		const editor = this.editor;
-		const toolbarConfig = editor.config.get( 'table.toolbar' );
-
-		// Don't add the toolbar if there is no configuration.
-		if ( !toolbarConfig || !toolbarConfig.length ) {
-			return;
-		}
-
-		/**
-		 * A contextual balloon plugin instance.
-		 *
-		 * @private
-		 * @member {module:ui/panel/balloon/contextualballoon~ContextualBalloon}
-		 */
-		this._balloon = this.editor.plugins.get( 'ContextualBalloon' );
-
-		/**
-		 * A toolbar view instance used to display the buttons specific for table editing.
-		 *
-		 * @protected
-		 * @type {module:ui/toolbar/toolbarview~ToolbarView}
-		 */
-		this._toolbar = new ToolbarView();
-
-		// Add buttons to the toolbar.
-		this._toolbar.fillFromConfig( toolbarConfig, editor.ui.componentFactory );
+		const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
 
-		// Show balloon panel each time table widget is selected.
-		this.listenTo( editor.ui, 'update', () => {
-			this._checkIsVisible();
-		} );
+		const tableContentToolbarItems = editor.config.get( 'table.contentToolbar' );
+		const deprecatedTableContentToolbarItems = editor.config.get( 'table.toolbar' );
 
-		// There is no render method after focus is back in editor, we need to check if balloon panel should be visible.
-		this.listenTo( editor.ui.focusTracker, 'change:isFocused', () => {
-			this._checkIsVisible();
-		}, { priority: 'low' } );
-	}
-
-	/**
-	 * Checks whether the toolbar should show up or hide depending on the current selection.
-	 *
-	 * @private
-	 */
-	_checkIsVisible() {
-		const editor = this.editor;
-		const viewSelection = editor.editing.view.document.selection;
+		const tableToolbarItems = editor.config.get( 'table.tableToolbar' );
 
-		if ( !editor.ui.focusTracker.isFocused || !isTableContentSelected( viewSelection ) ) {
-			this._hideToolbar();
-		} else {
-			this._showToolbar();
+		if ( deprecatedTableContentToolbarItems ) {
+			// eslint-disable-next-line
+			console.warn(
+				'`config.table.toolbar` is deprecated and will be removed in the next major release.' +
+				' Use `config.table.contentToolbar` instead.'
+			);
 		}
-	}
 
-	/**
-	 * Shows the {@link #_toolbar} in the {@link #_balloon}.
-	 *
-	 * @private
-	 */
-	_showToolbar() {
-		const editor = this.editor;
-
-		if ( this._isVisible ) {
-			repositionContextualBalloon( editor );
-		} else if ( !this._balloon.hasView( this._toolbar ) ) {
-			this._balloon.add( {
-				view: this._toolbar,
-				position: getBalloonPositionData( editor ),
-				balloonClassName
+		if ( tableContentToolbarItems || deprecatedTableContentToolbarItems ) {
+			widgetToolbarRepository.register( 'tableContent', {
+				items: tableContentToolbarItems || deprecatedTableContentToolbarItems,
+				visibleWhen: isTableContentSelected,
 			} );
 		}
-	}
 
-	/**
-	 * Removes the {@link #_toolbar} from the {@link #_balloon}.
-	 *
-	 * @private
-	 */
-	_hideToolbar() {
-		if ( !this._isVisible ) {
-			return;
+		if ( tableToolbarItems ) {
+			widgetToolbarRepository.register( 'table', {
+				items: tableToolbarItems,
+				visibleWhen: isTableWidgetSelected,
+			} );
 		}
-
-		this._balloon.remove( this._toolbar );
-	}
-
-	/**
-	 * Returns `true` when the {@link #_toolbar} is the visible view in the {@link #_balloon}.
-	 *
-	 * @private
-	 * @type {Boolean}
-	 */
-	get _isVisible() {
-		return this._balloon.visibleView == this._toolbar;
 	}
 }
 
 /**
- * Items to be placed in the table toolbar.
- * This option is used by the {@link module:table/tabletoolbar~TableToolbar} feature.
+ * Items to be placed in the table content toolbar.
+ *
+ * **Note:** This configuration option is deprecated! Use {@link module:table/table~TableConfig#contentToolbar} instead.
+ *
+ * Read more about configuring toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
+ *
+ * @deprecated
+ * @member {Array.<String>} module:table/table~TableConfig#toolbar
+ */
+
+/**
+ * Items to be placed in the table content toolbar.
+ * The {@link module:table/tabletoolbar~TableToolbar} plugin is required to make this toolbar working.
  *
  * Assuming that you use the {@link module:table/tableui~TableUI} feature, the following toolbar items will be available
  * in {@link module:ui/componentfactory~ComponentFactory}:
@@ -174,7 +101,7 @@ export default class TableToolbar extends Plugin {
  * You can thus configure the toolbar like this:
  *
  *		const tableConfig = {
- *			toolbar: [ 'tableRow', 'tableColumn', 'mergeTableCells' ]
+ *			contentToolbar: [ 'tableRow', 'tableColumn', 'mergeTableCells' ]
  *		};
  *
  * Of course, the same buttons can also be used in the
@@ -182,5 +109,23 @@ export default class TableToolbar extends Plugin {
  *
  * Read more about configuring toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
  *
- * @member {Array.<String>} module:table/table~TableConfig#toolbar
+ * @member {Array.<String>} module:table/table~TableConfig#contentToolbar
+ */
+
+/**
+ * Items to be placed in the table toolbar.
+ * The {@link module:table/tabletoolbar~TableToolbar} plugin is required to make this toolbar working.
+ *
+ * You can thus configure the toolbar like this:
+ *
+ *		const tableConfig = {
+ *			tableToolbar: [ 'blockQuote' ]
+ *		};
+ *
+ * Of course, the same buttons can also be used in the
+ * {@link module:core/editor/editorconfig~EditorConfig#toolbar main editor toolbar}.
+ *
+ * Read more about configuring toolbar in {@link module:core/editor/editorconfig~EditorConfig#toolbar}.
+ *
+ * @member {Array.<String>} module:table/table~TableConfig#tableToolbar
  */

+ 4 - 4
packages/ckeditor5-table/src/tableui.js

@@ -86,15 +86,15 @@ export default class TableUI extends Plugin {
 				{
 					type: 'button',
 					model: {
-						commandName: 'insertTableColumnBefore',
-						label: t( 'Insert column before' )
+						commandName: 'insertTableColumnLeft',
+						label: t( 'Insert column left' )
 					}
 				},
 				{
 					type: 'button',
 					model: {
-						commandName: 'insertTableColumnAfter',
-						label: t( 'Insert column after' )
+						commandName: 'insertTableColumnRight',
+						label: t( 'Insert column right' )
 					}
 				},
 				{

+ 21 - 19
packages/ckeditor5-table/src/tableutils.js

@@ -8,7 +8,6 @@
  */
 
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
-import Position from '@ckeditor/ckeditor5-engine/src/model/position';
 
 import TableWalker from './tablewalker';
 import { createEmptyTableCell, updateNumericAttribute } from './commands/utils';
@@ -69,25 +68,28 @@ export default class TableUtils extends Plugin {
 	}
 
 	/**
-	 * Creates an empty table at a given position.
+	 * Creates an empty table with proper structure. The table needs to be inserted into the model,
+	 * ie. using {@link module:engine/model/model~Model#insertContent} function.
 	 *
-	 * @param {module:engine/model/position~Position} position The position where the table will be inserted.
+	 *		model.change( ( writer ) => {
+	 *			// Create a table of 2 rows and 7 columns:
+	 *			const table = tableUtils.createTable( writer, 2, 7);
+	 *
+	 *			// Insert table to the model at the best position taking current selection:
+	 *			model.insertContent( table );
+	 *		}
+	 *
+	 * @param {module:engine/model/writer~Writer} writer The model writer.
 	 * @param {Number} rows The number of rows to create.
 	 * @param {Number} columns The number of columns to create.
 	 * @returns {module:engine/model/element~Element} The created table element.
 	 */
-	createTable( position, rows, columns ) {
-		const model = this.editor.model;
+	createTable( writer, rows, columns ) {
+		const table = writer.createElement( 'table' );
 
-		return model.change( writer => {
-			const table = writer.createElement( 'table' );
+		createEmptyRows( writer, table, 0, rows, columns );
 
-			writer.insert( table, position );
-
-			createEmptyRows( writer, table, 0, rows, columns );
-
-			return table;
-		} );
+		return table;
 	}
 
 	/**
@@ -209,7 +211,7 @@ export default class TableUtils extends Plugin {
 			// Inserting at the end and at the beginning of a table doesn't require to calculate anything special.
 			if ( insertAt === 0 || tableColumns === insertAt ) {
 				for ( const tableRow of table.getChildren() ) {
-					createCells( columnsToInsert, writer, Position.createAt( tableRow, insertAt ? 'end' : 0 ) );
+					createCells( columnsToInsert, writer, writer.createPositionAt( tableRow, insertAt ? 'end' : 0 ) );
 				}
 
 				return;
@@ -240,7 +242,7 @@ export default class TableUtils extends Plugin {
 				} else {
 					// It's either cell at this column index or spanned cell by a rowspanned cell from row above.
 					// In table above it's cell "e" and a spanned position from row below (empty cell between cells "g" and "h")
-					const insertPosition = Position.createFromParentAndOffset( table.getChild( row ), cellIndex );
+					const insertPosition = writer.createPositionAt( table.getChild( row ), cellIndex );
 
 					createCells( columnsToInsert, writer, insertPosition );
 				}
@@ -322,7 +324,7 @@ export default class TableUtils extends Plugin {
 				}
 
 				const cellsToInsert = colspan > numberOfCells ? numberOfCells - 1 : colspan - 1;
-				createCells( cellsToInsert, writer, Position.createAfter( tableCell ), newCellsAttributes );
+				createCells( cellsToInsert, writer, writer.createPositionAfter( tableCell ), newCellsAttributes );
 			}
 
 			// Second check - the cell has colspan of 1 or we need to create more cells then the currently one spans over.
@@ -360,7 +362,7 @@ export default class TableUtils extends Plugin {
 					newCellsAttributes.rowspan = rowspan;
 				}
 
-				createCells( cellsToInsert, writer, Position.createAfter( tableCell ), newCellsAttributes );
+				createCells( cellsToInsert, writer, writer.createPositionAfter( tableCell ), newCellsAttributes );
 
 				const headingColumns = table.getAttribute( 'headingColumns' ) || 0;
 
@@ -480,7 +482,7 @@ export default class TableUtils extends Plugin {
 					const isInEvenlySplitRow = ( row + splitCellRow + updatedSpan ) % newCellsSpan === 0;
 
 					if ( isAfterSplitCell && isOnSameColumn && isInEvenlySplitRow ) {
-						const position = Position.createFromParentAndOffset( table.getChild( row ), cellIndex );
+						const position = writer.createPositionAt( table.getChild( row ), cellIndex );
 
 						createCells( 1, writer, position, newCellsAttributes );
 					}
@@ -560,7 +562,7 @@ function createEmptyRows( writer, table, insertAt, rows, tableCellToInsert, attr
 
 		writer.insert( tableRow, table, insertAt );
 
-		createCells( tableCellToInsert, writer, Position.createAt( tableRow, 'end' ), attributes );
+		createCells( tableCellToInsert, writer, writer.createPositionAt( tableRow, 'end' ), attributes );
 	}
 }
 

+ 0 - 52
packages/ckeditor5-table/src/ui/utils.js

@@ -1,52 +0,0 @@
-/**
- * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
- * For licensing, see LICENSE.md.
- */
-
-/**
- * @module table/ui/utils
- */
-
-import BalloonPanelView from '@ckeditor/ckeditor5-ui/src/panel/balloon/balloonpanelview';
-import { findAncestor } from '../commands/utils';
-
-/**
- * A helper utility that positions the
- * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} instance
- * with respect to the table in the editor content, if one is selected.
- *
- * @param {module:core/editor/editor~Editor} editor The editor instance.
- */
-export function repositionContextualBalloon( editor ) {
-	const balloon = editor.plugins.get( 'ContextualBalloon' );
-
-	balloon.updatePosition( getBalloonPositionData( editor ) );
-}
-
-/**
- * Returns the positioning options that control the geometry of the
- * {@link module:ui/panel/balloon/contextualballoon~ContextualBalloon contextual balloon} with respect
- * to the selected element in the editor content.
- *
- * @param {module:core/editor/editor~Editor} editor The editor instance.
- * @returns {module:utils/dom/position~Options}
- */
-export function getBalloonPositionData( editor ) {
-	const editingView = editor.editing.view;
-	const defaultPositions = BalloonPanelView.defaultPositions;
-	const viewSelection = editingView.document.selection;
-
-	const parentTable = findAncestor( 'table', viewSelection.getFirstPosition() );
-
-	return {
-		target: editingView.domConverter.viewToDom( parentTable ),
-		positions: [
-			defaultPositions.northArrowSouth,
-			defaultPositions.northArrowSouthWest,
-			defaultPositions.northArrowSouthEast,
-			defaultPositions.southArrowNorth,
-			defaultPositions.southArrowNorthWest,
-			defaultPositions.southArrowNorthEast
-		]
-	};
-}

+ 3 - 3
packages/ckeditor5-table/tests/_utils/utils.js

@@ -15,6 +15,8 @@ import {
 } from '../../src/converters/downcast';
 import upcastTable, { upcastTableCell } from '../../src/converters/upcasttable';
 
+const WIDGET_TABLE_CELL_CLASS = 'ck-editor__editable ck-editor__nested-editable';
+
 /**
  * Returns a model representation of a table shorthand notation:
  *
@@ -267,9 +269,7 @@ function makeRows( tableData, options ) {
 				const attributes = isObject ? tableCellData : {};
 
 				if ( asWidget ) {
-					attributes.class = attributes.class ? ' ' + attributes.class : '';
-					attributes.class = 'ck-editor__editable ck-editor__nested-editable' + attributes.class;
-
+					attributes.class = WIDGET_TABLE_CELL_CLASS + ( attributes.class ? ` ${ attributes.class }` : '' );
 					attributes.contenteditable = 'true';
 				}
 

+ 7 - 7
packages/ckeditor5-table/tests/commands/insertcolumncommand.js

@@ -31,7 +31,7 @@ describe( 'InsertColumnCommand', () => {
 		return editor.destroy();
 	} );
 
-	describe( 'order=after', () => {
+	describe( 'order=right', () => {
 		beforeEach( () => {
 			command = new InsertColumnCommand( editor );
 		} );
@@ -49,7 +49,7 @@ describe( 'InsertColumnCommand', () => {
 		} );
 
 		describe( 'execute()', () => {
-			it( 'should insert column in given table after selection\'s column', () => {
+			it( 'should insert column in given table to the right of the selection\'s column', () => {
 				setData( model, modelTable( [
 					[ '11[]', '12' ],
 					[ '21', '22' ]
@@ -63,7 +63,7 @@ describe( 'InsertColumnCommand', () => {
 				] ) );
 			} );
 
-			it( 'should insert column in given table after selection\'s column (selection in block content)', () => {
+			it( 'should insert column in given table to the right of the selection\'s column (selection in block content)', () => {
 				setData( model, modelTable( [
 					[ '11', '<paragraph>12[]</paragraph>', '13' ]
 				] ) );
@@ -155,9 +155,9 @@ describe( 'InsertColumnCommand', () => {
 		} );
 	} );
 
-	describe( 'order=before', () => {
+	describe( 'order=left', () => {
 		beforeEach( () => {
-			command = new InsertColumnCommand( editor, { order: 'before' } );
+			command = new InsertColumnCommand( editor, { order: 'left' } );
 		} );
 
 		describe( 'isEnabled', () => {
@@ -173,7 +173,7 @@ describe( 'InsertColumnCommand', () => {
 		} );
 
 		describe( 'execute()', () => {
-			it( 'should insert column in given table before selection\'s column', () => {
+			it( 'should insert column in given table to the left of the selection\'s column', () => {
 				setData( model, modelTable( [
 					[ '11', '12[]' ],
 					[ '21', '22' ]
@@ -187,7 +187,7 @@ describe( 'InsertColumnCommand', () => {
 				] ) );
 			} );
 
-			it( 'should insert column in given table before selection\'s column (selection in block content)', () => {
+			it( 'should insert column in given table to the left of the selection\'s column (selection in block content)', () => {
 				setData( model, modelTable( [
 					[ '11', '<paragraph>12[]</paragraph>', '13' ]
 				] ) );

+ 29 - 1
packages/ckeditor5-table/tests/commands/inserttablecommand.js

@@ -72,7 +72,7 @@ describe( 'InsertTableCommand', () => {
 				] ) );
 			} );
 
-			it( 'should insert table with two rows and two columns after non-empty paragraph', () => {
+			it( 'should insert table with two rows and two columns after non-empty paragraph if selection is at the end', () => {
 				setData( model, '<paragraph>foo[]</paragraph>' );
 
 				command.execute();
@@ -100,6 +100,34 @@ describe( 'InsertTableCommand', () => {
 					] )
 				);
 			} );
+
+			it( 'should insert table before after non-empty paragraph if selection is inside', () => {
+				setData( model, '<paragraph>f[]oo</paragraph>' );
+
+				command.execute();
+
+				expect( formatTable( getData( model ) ) ).to.equal(
+					formattedModelTable( [
+						[ '[]', '' ],
+						[ '', '' ]
+					] ) +
+					'<paragraph>foo</paragraph>'
+				);
+			} );
+
+			it( 'should replace empty paragraph with table', () => {
+				setData( model, '<paragraph>[]</paragraph>' );
+
+				command.execute( { rows: 3, columns: 4 } );
+
+				expect( formatTable( getData( model ) ) ).to.equal(
+					formattedModelTable( [
+						[ '[]', '', '', '' ],
+						[ '', '', '', '' ],
+						[ '', '', '', '' ]
+					] )
+				);
+			} );
 		} );
 	} );
 } );

+ 1 - 1
packages/ckeditor5-table/tests/converters/downcast.js

@@ -25,7 +25,7 @@ function paragraphInTableCell() {
 
 				if ( viewElement && viewElement.name === 'span' ) {
 					conversionApi.mapper.unbindModelElement( tableCell );
-					conversionApi.writer.rename( viewElement, 'p' );
+					conversionApi.writer.rename( 'p', viewElement );
 					conversionApi.mapper.bindElements( child, viewElement );
 				}
 			}

+ 124 - 0
packages/ckeditor5-table/tests/converters/table-cell-content-post-fixer.js

@@ -0,0 +1,124 @@
+/**
+ * @license Copyright (c) 2003-2018, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+import { getData as getModelData, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+
+import TableEditing from '../../src/tableediting';
+import { formatTable } from './../_utils/utils';
+import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
+
+describe( 'Table cell content post-fixer', () => {
+	let editor, model, root;
+
+	beforeEach( () => {
+		return VirtualTestEditor
+			.create( {
+				plugins: [ TableEditing, Paragraph, UndoEditing ]
+			} )
+			.then( newEditor => {
+				editor = newEditor;
+				model = editor.model;
+				root = model.document.getRoot();
+			} );
+	} );
+
+	afterEach( () => {
+		editor.destroy();
+	} );
+
+	it( 'should add a paragraph to an empty table cell (on table insert)', () => {
+		setModelData( model,
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		);
+
+		expect( formatTable( getModelData( model, { withoutSelection: true } ) ) ).to.equal( formatTable(
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		) );
+	} );
+
+	it( 'should add a paragraph to an empty table cell (on row insert)', () => {
+		setModelData( model,
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		);
+
+		// Insert table row with one table cell
+		model.change( writer => {
+			writer.insertElement( 'tableRow', writer.createPositionAfter( root.getNodeByPath( [ 0, 0 ] ) ) );
+			writer.insertElement( 'tableCell', writer.createPositionAt( root.getNodeByPath( [ 0, 1 ] ), 0 ) );
+		} );
+
+		expect( formatTable( getModelData( model, { withoutSelection: true } ) ) ).to.equal( formatTable(
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+				'</tableRow>' +
+				'<tableRow>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		) );
+	} );
+
+	it( 'should add a paragraph to an empty table cell (on table cell insert)', () => {
+		setModelData( model,
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		);
+
+		// Insert table row with one table cell
+		model.change( writer => {
+			writer.insertElement( 'tableCell', writer.createPositionAt( root.getNodeByPath( [ 0, 0 ] ), 'end' ) );
+		} );
+
+		expect( formatTable( getModelData( model, { withoutSelection: true } ) ) ).to.equal( formatTable(
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		) );
+	} );
+
+	it( 'should add a paragraph to an empty table cell (after remove)', () => {
+		setModelData( model,
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell><paragraph>foo</paragraph></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		);
+
+		// Remove paragraph from table cell.
+		model.change( writer => {
+			writer.remove( writer.createRangeIn( root.getNodeByPath( [ 0, 0, 0 ] ) ) );
+		} );
+
+		expect( formatTable( getModelData( model, { withoutSelection: true } ) ) ).to.equal( formatTable(
+			'<table>' +
+				'<tableRow>' +
+					'<tableCell><paragraph></paragraph></tableCell>' +
+				'</tableRow>' +
+			'</table>'
+		) );
+	} );
+} );

+ 8 - 9
packages/ckeditor5-table/tests/converters/table-post-fixer.js → packages/ckeditor5-table/tests/converters/table-layout-post-fixer.js

@@ -3,7 +3,6 @@
  * For licensing, see LICENSE.md.
  */
 
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import { getData as getModelData, parse, setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
@@ -12,7 +11,7 @@ import TableEditing from '../../src/tableediting';
 import { formatTable, formattedModelTable, modelTable } from './../_utils/utils';
 import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
 
-describe( 'Table post-fixer', () => {
+describe( 'Table layout post-fixer', () => {
 	let editor, model, root;
 
 	beforeEach( () => {
@@ -40,7 +39,7 @@ describe( 'Table post-fixer', () => {
 			] ), model.schema );
 
 			model.change( writer => {
-				writer.remove( Range.createIn( root ) );
+				writer.remove( writer.createRangeIn( root ) );
 				writer.insert( parsed, root );
 			} );
 
@@ -59,7 +58,7 @@ describe( 'Table post-fixer', () => {
 			] ), model.schema );
 
 			model.change( writer => {
-				writer.remove( Range.createIn( root ) );
+				writer.remove( writer.createRangeIn( root ) );
 				writer.insert( parsed, root );
 			} );
 
@@ -78,7 +77,7 @@ describe( 'Table post-fixer', () => {
 			] ), model.schema );
 
 			model.change( writer => {
-				writer.remove( Range.createIn( root ) );
+				writer.remove( writer.createRangeIn( root ) );
 				writer.insert( parsed, root );
 			} );
 
@@ -97,7 +96,7 @@ describe( 'Table post-fixer', () => {
 			], { headingRows: 2 } ), model.schema );
 
 			model.change( writer => {
-				writer.remove( Range.createIn( root ) );
+				writer.remove( writer.createRangeIn( root ) );
 				writer.insert( parsed, root );
 			} );
 
@@ -116,7 +115,7 @@ describe( 'Table post-fixer', () => {
 			], { headingRows: 1 } ), model.schema );
 
 			model.change( writer => {
-				writer.remove( Range.createIn( root ) );
+				writer.remove( writer.createRangeIn( root ) );
 				writer.insert( parsed, root );
 			} );
 
@@ -144,7 +143,7 @@ describe( 'Table post-fixer', () => {
 			const parsed = parse( tableA + tableB + tableC, model.schema );
 
 			model.change( writer => {
-				writer.remove( Range.createIn( root ) );
+				writer.remove( writer.createRangeIn( root ) );
 				writer.insert( parsed, root );
 			} );
 
@@ -173,7 +172,7 @@ describe( 'Table post-fixer', () => {
 
 			expect( () => {
 				model.change( writer => {
-					writer.remove( Range.createIn( root ) );
+					writer.remove( writer.createRangeIn( root ) );
 				} );
 			} ).to.not.throw();
 

+ 95 - 27
packages/ckeditor5-table/tests/converters/tablecell-post-fixer.js

@@ -3,32 +3,36 @@
  * For licensing, see LICENSE.md.
  */
 
-import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
+/* globals document */
+
 import { getData as getViewData } from '@ckeditor/ckeditor5-engine/src/dev-utils/view';
-import { setData as setModelData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
 
-import { defaultConversion, defaultSchema, formatTable, formattedViewTable, modelTable } from '../_utils/utils';
+import { defaultConversion, defaultSchema, formatTable, formattedViewTable, viewTable } from '../_utils/utils';
 import injectTableCellPostFixer from '../../src/converters/tablecell-post-fixer';
 
 import env from '@ckeditor/ckeditor5-utils/src/env';
 import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
+import ClassicTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/classictesteditor';
 
 describe( 'TableCell post-fixer', () => {
-	let editor, model, doc, root, viewDocument;
+	let editor, model, doc, root, view;
 
 	testUtils.createSinonSandbox();
 
 	beforeEach( () => {
+		const element = document.createElement( 'div' );
+		document.body.appendChild( element );
+
 		// Most tests assume non-edge environment but we do not set `contenteditable=false` on Edge so stub `env.isEdge`.
 		testUtils.sinon.stub( env, 'isEdge' ).get( () => false );
 
-		return VirtualTestEditor.create()
+		return ClassicTestEditor.create( element )
 			.then( newEditor => {
 				editor = newEditor;
 				model = editor.model;
 				doc = model.document;
 				root = doc.getRoot( 'main' );
-				viewDocument = editor.editing.view;
+				view = editor.editing.view;
 
 				defaultSchema( model.schema );
 				defaultConversion( editor.conversion, true );
@@ -46,7 +50,7 @@ describe( 'TableCell post-fixer', () => {
 	} );
 
 	it( 'should rename <span> to <p> when adding more <paragraph> elements to the same table cell', () => {
-		setModelData( model, modelTable( [ [ '00[]' ] ] ) );
+		editor.setData( viewTable( [ [ '<p>00</p>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -60,13 +64,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.setSelection( nodeByPath.nextSibling, 0 );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<p>00</p><p></p>' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should rename <span> to <p> on adding other block element to the same table cell', () => {
-		setModelData( model, modelTable( [ [ '00[]' ] ] ) );
+		editor.setData( viewTable( [ [ '<p>00</p>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -80,13 +84,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.setSelection( nodeByPath.nextSibling, 0 );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<p>00</p><div></div>' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should properly rename the same element on consecutive changes', () => {
-		setModelData( model, modelTable( [ [ '00[]' ] ] ) );
+		editor.setData( viewTable( [ [ '<p>00</p>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -98,7 +102,7 @@ describe( 'TableCell post-fixer', () => {
 			writer.setSelection( nodeByPath.nextSibling, 0 );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<p>00</p><p></p>' ]
 		], { asWidget: true } ) );
 
@@ -106,13 +110,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.remove( table.getNodeByPath( [ 0, 0, 1 ] ) );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '00' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should rename <span> to <p> when setting attribute on <paragraph>', () => {
-		setModelData( model, modelTable( [ [ '<paragraph>00[]</paragraph>' ] ] ) );
+		editor.setData( '<table><tr><td><p>00</p></td></tr></table>' );
 
 		const table = root.getChild( 0 );
 
@@ -120,13 +124,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.setAttribute( 'foo', 'bar', table.getNodeByPath( [ 0, 0, 0 ] ) );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<p foo="bar">00</p>' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should rename <p> to <span> when removing all but one paragraph inside table cell', () => {
-		setModelData( model, modelTable( [ [ '<paragraph>00[]</paragraph><paragraph>foo</paragraph>' ] ] ) );
+		editor.setData( viewTable( [ [ '<p>00</p><p>foo</p>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -134,13 +138,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.remove( table.getNodeByPath( [ 0, 0, 1 ] ) );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '00' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should rename <p> to <span> when removing attribute from <paragraph>', () => {
-		setModelData( model, modelTable( [ [ '<paragraph foo="bar">00[]</paragraph>' ] ] ) );
+		editor.setData( '<table><tr><td><p foo="bar">00</p></td></tr></table>' );
 
 		const table = root.getChild( 0 );
 
@@ -148,13 +152,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.removeAttribute( 'foo', table.getNodeByPath( [ 0, 0, 0 ] ) );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<span>00</span>' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should keep <p> in the view when <paragraph> attribute value is changed', () => {
-		setModelData( model, modelTable( [ [ '<paragraph foo="bar">00[]</paragraph>' ] ] ) );
+		editor.setData( viewTable( [ [ '<p foo="bar">00</p>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -162,13 +166,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.setAttribute( 'foo', 'baz', table.getNodeByPath( [ 0, 0, 0 ] ) );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<p foo="baz">00</p>' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should keep <p> in the view when <paragraph> attribute value is changed (table cell with multiple blocks)', () => {
-		setModelData( model, modelTable( [ [ '<paragraph foo="bar">00[]</paragraph><paragraph>00</paragraph>' ] ] ) );
+		editor.setData( viewTable( [ [ '<p foo="bar">00</p><p>00</p>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -176,13 +180,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.setAttribute( 'foo', 'baz', table.getNodeByPath( [ 0, 0, 0 ] ) );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<p foo="baz">00</p><p>00</p>' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should do nothing on rename <paragraph> to other block', () => {
-		setModelData( model, modelTable( [ [ '00' ] ] ) );
+		editor.setData( viewTable( [ [ '<p>00</p>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -190,13 +194,13 @@ describe( 'TableCell post-fixer', () => {
 			writer.rename( table.getNodeByPath( [ 0, 0, 0 ] ), 'block' );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<div>00</div>' ]
 		], { asWidget: true } ) );
 	} );
 
 	it( 'should do nothing when setting attribute on block item other then <paragraph>', () => {
-		setModelData( model, modelTable( [ [ '<block>foo</block>' ] ] ) );
+		editor.setData( viewTable( [ [ '<div>foo</div>' ] ] ) );
 
 		const table = root.getChild( 0 );
 
@@ -204,8 +208,72 @@ describe( 'TableCell post-fixer', () => {
 			writer.setAttribute( 'foo', 'bar', table.getNodeByPath( [ 0, 0, 0 ] ) );
 		} );
 
-		expect( formatTable( getViewData( viewDocument, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
 			[ '<div foo="bar">foo</div>' ]
 		], { asWidget: true } ) );
 	} );
+
+	it( 'should not crash when view.change() block was called in model.change()', () => {
+		editor.setData( viewTable( [ [ '<p>foobar</p>' ] ] ) );
+
+		const table = root.getChild( 0 );
+
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) )
+			.to.equal( formattedViewTable( [ [ 'foobar' ] ], { asWidget: true } ) );
+
+		expect( () => {
+			model.change( writer => {
+				const tableCell = table.getNodeByPath( [ 0, 0 ] );
+
+				writer.insertElement( 'paragraph', null, writer.createPositionAt( tableCell, 'end' ) );
+				writer.setSelection( writer.createRangeIn( tableCell ) );
+
+				// Do some change in the view while inside model change.
+				editor.editing.view.change( writer => {
+					writer.addClass( 'foo', editor.editing.mapper.toViewElement( tableCell ) );
+				} );
+			} );
+		} ).to.not.throw();
+
+		expect( formatTable( getViewData( view ) ) ).to.equal( formattedViewTable( [
+			[ { class: 'foo', contents: '<p>{foobar</p><p>]</p>' } ]
+		], { asWidget: true } ) );
+	} );
+
+	it( 'should keep <p> in the view when <paragraph> attribute value is changed (table cell with multiple blocks)', () => {
+		editor.setData( viewTable( [ [ '<p>00</p><p>00</p>' ] ] ) );
+
+		const table = root.getChild( 0 );
+
+		model.change( writer => {
+			writer.remove( writer.createRangeOn( table.getNodeByPath( [ 0, 0, 1 ] ) ) );
+		} );
+
+		expect( formatTable( getViewData( view, { withoutSelection: true } ) ) ).to.equal( formattedViewTable( [
+			[ '<span>00</span>' ]
+		], { asWidget: true } ) );
+	} );
+
+	it( 'should update view selection after deleting content', () => {
+		editor.setData( viewTable( [ [ '<p>foo</p><p>bar</p>' ] ] ) );
+
+		const tableCell = root.getNodeByPath( [ 0, 0, 0 ] );
+
+		// Replace table cell contents with paragraph - as model.deleteContent() does.
+		model.change( writer => {
+			writer.remove( writer.createRangeIn( tableCell ) );
+
+			const paragraph = writer.createElement( 'paragraph' );
+
+			writer.insert( paragraph, writer.createPositionAt( tableCell, 0 ) );
+
+			// Set selection to newly created paragraph.
+			writer.setSelection( paragraph, 0 );
+		} );
+
+		const viewRange = view.document.selection.getFirstRange();
+
+		// Trying to map view selection to DOM range shouldn't throw after post-fixer will fix inserted <p> to <span>.
+		expect( () => view.domConverter.viewRangeToDom( viewRange ) ).to.not.throw();
+	} );
 } );

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

@@ -418,6 +418,25 @@ describe( 'upcastTable()', () => {
 			] ) );
 		} );
 
+		it( 'should upcast table inline content to single <paragraph>', () => {
+			editor.model.schema.extend( '$text', { allowAttributes: 'bold' } );
+			editor.conversion.attributeToElement( { model: 'bold', view: 'strong' } );
+
+			editor.setData(
+				'<table>' +
+					'<tbody>' +
+						'<tr>' +
+							'<td>foo <strong>bar</strong></td>' +
+						'</tr>' +
+					'</tbody>' +
+				'</table>'
+			);
+
+			expectModel( modelTable( [
+				[ 'foo <$text bold="true">bar</$text>' ]
+			] ) );
+		} );
+
 		it( 'should upcast table with multiple <p> in table cell', () => {
 			editor.setData(
 				'<table>' +
@@ -454,4 +473,92 @@ describe( 'upcastTable()', () => {
 			] ) );
 		} );
 	} );
+
+	describe( 'handling redundant whitespacing between table elements', () => {
+		it( 'table without thead/tbody/tfoot', () => {
+			editor.setData(
+				'<table> ' +
+					'<tr> <td>1</td></tr>' +
+				'</table>'
+			);
+
+			expectModel(
+				'<table>' +
+					'<tableRow><tableCell><paragraph>1</paragraph></tableCell></tableRow>' +
+				'</table>'
+			);
+		} );
+
+		it( 'table with thead only', () => {
+			editor.setData(
+				'<table>' +
+					'<thead>' +
+						'<tr><td>1</td></tr> ' +
+						'<tr><td>2</td></tr>' +
+						' <tr><td>3</td></tr>' +
+					'</thead>' +
+				'</table>'
+			);
+
+			expectModel(
+				'<table headingRows="3">' +
+					'<tableRow><tableCell><paragraph>1</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>2</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>3</paragraph></tableCell></tableRow>' +
+				'</table>'
+			);
+		} );
+
+		it( 'table with thead and tbody', () => {
+			editor.setData(
+				'<table>' +
+					'<thead>   ' +
+						'<tr><td>1</td></tr>' +
+						'<tr><td>2</td></tr>\n\n   ' +
+					'</thead>' +
+					'<tbody>' +
+						'<tr><td>3</td></tr> ' +
+						'\n\n    <tr><td>4</td></tr>' +
+						'<tr>    <td>5</td></tr> ' +
+					'</tbody>' +
+				'</table>'
+			);
+
+			expectModel(
+				'<table headingRows="2">' +
+					'<tableRow><tableCell><paragraph>1</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>2</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>3</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>4</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>5</paragraph></tableCell></tableRow>' +
+				'</table>'
+			);
+		} );
+
+		it( 'table with tbody and tfoot', () => {
+			editor.setData(
+				'<table>' +
+					'<tbody>' +
+						'     <tr><td>1</td></tr>' +
+						'<tr><td>2</td></tr>' +
+						'<tr><td>3</td></tr> ' +
+						'<tr><td>4</td></tr>\n\n\n' +
+					'</tbody>\n\n' +
+					'   <tfoot>' +
+						'<tr>  <td>5</td>\n\n\n</tr>   ' +
+					'</tfoot>' +
+				'</table>'
+			);
+
+			expectModel(
+				'<table>' +
+					'<tableRow><tableCell><paragraph>1</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>2</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>3</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>4</paragraph></tableCell></tableRow>' +
+					'<tableRow><tableCell><paragraph>5</paragraph></tableCell></tableRow>' +
+				'</table>'
+			);
+		} );
+	} );
 } );

+ 1 - 1
packages/ckeditor5-table/tests/integration.js

@@ -12,7 +12,7 @@ import Table from '../src/table';
 import TableToolbar from '../src/tabletoolbar';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 
-describe( 'TableToolbar integration', () => {
+describe( 'TableContentToolbar integration', () => {
 	describe( 'with the BalloonToolbar', () => {
 		let balloon, balloonToolbar, newEditor, editorElement;
 

+ 4 - 2
packages/ckeditor5-table/tests/manual/table.js

@@ -9,15 +9,17 @@ import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor'
 import ArticlePluginSet from '@ckeditor/ckeditor5-core/tests/_utils/articlepluginset';
 import Table from '../../src/table';
 import TableToolbar from '../../src/tabletoolbar';
+import BlockQuote from '@ckeditor/ckeditor5-block-quote/src/blockquote';
 
 ClassicEditor
 	.create( document.querySelector( '#editor' ), {
-		plugins: [ ArticlePluginSet, Table, TableToolbar ],
+		plugins: [ ArticlePluginSet, Table, TableToolbar, BlockQuote ],
 		toolbar: [
 			'heading', '|', 'insertTable', '|', 'bold', 'italic', 'bulletedList', 'numberedList', 'blockQuote', 'undo', 'redo'
 		],
 		table: {
-			toolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
+			contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ],
+			tableToolbar: [ 'bold', 'italic' ]
 		}
 	} )
 	.then( editor => {

+ 8 - 6
packages/ckeditor5-table/tests/manual/table.md

@@ -2,20 +2,20 @@
 
 1. The data should be loaded with:
   * a complex table with:
-    - one heading row, 
-    - two heading columns, 
+    - one heading row,
+    - two heading columns,
     - merged cells in heading columns section,
   * a table with 2 tbody sections in the DOM - should be rendered as a table with one tbody.
   * a table with no tbody sections in the DOM - should be rendered as a table with one tbody.
   * a table with a thead section between two tbody sections in dom - should be rendered as a table with one thead and on tbody section in proper order: 1, 2, 3.
 
 2. Main toolbar should have insert table dropdown.
-  
-3. While the table widget is selected there should be a toolbar attached to the table with 3 dropdowns:
+
+3. While the table cell is selected there should be a toolbar attached to the table with 3 dropdowns:
   * column dropdown with items:
     - header column,
-    - insert column before,
-    - insert column after,
+    - insert column left,
+    - insert column right,
     - delete column.
   * row dropdown with items:
     - header row,
@@ -30,6 +30,8 @@
     - split cell vertically,
     - split cell horizontally,
 
+4. While the table widget is selected there should be `bold` and `italic` buttons
+
 ### Testing
 
 Inserting table:

+ 1 - 1
packages/ckeditor5-table/tests/manual/tableblockcontent.js

@@ -19,7 +19,7 @@ ClassicEditor
 			'alignment', '|', 'undo', 'redo'
 		],
 		table: {
-			toolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
+			contentToolbar: [ 'tableColumn', 'tableRow', 'mergeTableCells' ]
 		}
 	} )
 	.then( editor => {

+ 2 - 3
packages/ckeditor5-table/tests/manual/tableblockcontent.md

@@ -6,14 +6,13 @@
     * List
     * Heading
     * Block Quote (with inner paragraph)
-    * Image
 
 2. The third column consist blocks with text alignment.
     * Paragraph - should be rendered was `<p>` when alignment is set (apart from default) for single paragraph.
 
 ### Testing
 
-1. Use <kbd>Enter</kbd> in cells with single `<pargraph>`. When two `<paragraph>`'s are in one table cell they should be rendered as `<p>`.
+1. Use <kbd>Enter</kbd> in cells with single `<paragraph>`. When two `<paragraph>`'s are in one table cell they should be rendered as `<p>`.
 2. Undo previous step - the `<p>` element should be changed to `<span>` for single paragraph.
 3. Change `<heading>` to paragraph - it should be rendered as `<p>` element if there are other headings or other block content.
-4. Change one `<heading>` to paragraph and remove other headings. The `<paragraph>` should be rendered as `<span>`. 
+4. Change one `<heading>` to paragraph and remove other headings. The `<paragraph>` should be rendered as `<span>`.

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

@@ -10,6 +10,7 @@ import Clipboard from '@ckeditor/ckeditor5-clipboard/src/clipboard';
 import UndoEditing from '@ckeditor/ckeditor5-undo/src/undoediting';
 import ListEditing from '@ckeditor/ckeditor5-list/src/listediting';
 import BlockQuoteEditing from '@ckeditor/ckeditor5-block-quote/src/blockquoteediting';
+import Typing from '@ckeditor/ckeditor5-typing/src/typing';
 import VirtualTestEditor from '@ckeditor/ckeditor5-core/tests/_utils/virtualtesteditor';
 import {
 	getData as getModelData,
@@ -148,4 +149,28 @@ describe( 'Table feature – integration', () => {
 			expect( editor.data.get( 'otherRoot' ) ).to.equal( viewTable( [ [ 'foo' ] ] ) );
 		} );
 	} );
+
+	describe( 'other', () => {
+		let editor;
+
+		beforeEach( () => {
+			return VirtualTestEditor
+				.create( { plugins: [ Paragraph, TableEditing, ListEditing, BlockQuoteEditing, Widget, Typing ] } )
+				.then( newEditor => {
+					editor = newEditor;
+				} );
+		} );
+
+		it( 'merges elements without throwing errors', () => {
+			setModelData( editor.model, modelTable( [
+				[ '<blockQuote><paragraph>Foo</paragraph></blockQuote><paragraph>[]Bar</paragraph>' ]
+			] ) );
+
+			editor.execute( 'delete' );
+
+			expect( formatTable( getModelData( editor.model ) ) ).to.equal( formattedModelTable( [
+				[ '<blockQuote><paragraph>Foo[]Bar</paragraph></blockQuote>' ]
+			] ) );
+		} );
+	} );
 } );

+ 15 - 6
packages/ckeditor5-table/tests/tableediting.js

@@ -21,7 +21,7 @@ import SplitCellCommand from '../src/commands/splitcellcommand';
 import MergeCellsCommand from '../src/commands/mergecellscommand';
 import SetHeaderRowCommand from '../src/commands/setheaderrowcommand';
 import SetHeaderColumnCommand from '../src/commands/setheadercolumncommand';
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
+import MediaEmbedEditing from '@ckeditor/ckeditor5-media-embed/src/mediaembedediting';
 
 describe( 'TableEditing', () => {
 	let editor, model;
@@ -29,7 +29,7 @@ describe( 'TableEditing', () => {
 	beforeEach( () => {
 		return VirtualTestEditor
 			.create( {
-				plugins: [ TableEditing, Paragraph, ImageEditing ]
+				plugins: [ TableEditing, Paragraph, ImageEditing, MediaEmbedEditing ]
 			} )
 			.then( newEditor => {
 				editor = newEditor;
@@ -90,12 +90,12 @@ describe( 'TableEditing', () => {
 		expect( editor.commands.get( 'insertTableRowBelow' ) ).to.be.instanceOf( InsertRowCommand );
 	} );
 
-	it( 'adds insertColumnBefore command', () => {
-		expect( editor.commands.get( 'insertTableColumnBefore' ) ).to.be.instanceOf( InsertColumnCommand );
+	it( 'adds insertColumnLeft command', () => {
+		expect( editor.commands.get( 'insertTableColumnLeft' ) ).to.be.instanceOf( InsertColumnCommand );
 	} );
 
-	it( 'adds insertColumnAfter command', () => {
-		expect( editor.commands.get( 'insertTableColumnAfter' ) ).to.be.instanceOf( InsertColumnCommand );
+	it( 'adds insertColumnRight command', () => {
+		expect( editor.commands.get( 'insertTableColumnRight' ) ).to.be.instanceOf( InsertColumnCommand );
 	} );
 
 	it( 'adds removeRow command', () => {
@@ -174,6 +174,15 @@ describe( 'TableEditing', () => {
 				expect( getModelData( model, { withoutSelection: true } ) )
 					.to.equal( '<table><tableRow><tableCell><paragraph></paragraph></tableCell></tableRow></table>' );
 			} );
+
+			it( 'should convert table with media', () => {
+				editor.setData(
+					'<table><tbody><tr><td><oembed url="https://www.youtube.com/watch?v=H08tGjXNHO4"></oembed></td></tr></tbody></table>'
+				);
+
+				expect( getModelData( model, { withoutSelection: true } ) )
+					.to.equal( '<table><tableRow><tableCell><paragraph></paragraph></tableCell></tableRow></table>' );
+			} );
 		} );
 	} );
 

+ 383 - 133
packages/ckeditor5-table/tests/tabletoolbar.js

@@ -3,211 +3,461 @@
  * For licensing, see LICENSE.md.
  */
 
-/* global document */
+/* global document, window */
 
-import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import ClassicTestEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
 import TableToolbar from '../src/tabletoolbar';
 import Table from '../src/table';
 import global from '@ckeditor/ckeditor5-utils/src/dom/global';
 import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
 import ButtonView from '@ckeditor/ckeditor5-ui/src/button/buttonview';
 import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
-import Range from '@ckeditor/ckeditor5-engine/src/model/range';
 import View from '@ckeditor/ckeditor5-ui/src/view';
 import { setData } from '@ckeditor/ckeditor5-engine/src/dev-utils/model';
+import WidgetToolbarRepository from '@ckeditor/ckeditor5-widget/src/widgettoolbarrepository';
+import testUtils from '@ckeditor/ckeditor5-core/tests/_utils/utils';
 
 describe( 'TableToolbar', () => {
-	let editor, model, doc, plugin, toolbar, balloon, editorElement;
+	testUtils.createSinonSandbox();
 
-	beforeEach( () => {
-		editorElement = global.document.createElement( 'div' );
-		global.document.body.appendChild( editorElement );
+	describe( 'contentToolbar', () => {
+		let editor, model, doc, widgetToolbarRepository, toolbar, balloon, editorElement;
 
-		return ClassicEditor
-			.create( editorElement, {
-				plugins: [ Paragraph, Table, TableToolbar, FakeButton ],
-				table: {
-					toolbar: [ 'fake_button' ]
-				}
+		beforeEach( () => {
+			editorElement = global.document.createElement( 'div' );
+			global.document.body.appendChild( editorElement );
+
+			return ClassicTestEditor
+				.create( editorElement, {
+					plugins: [ Paragraph, Table, TableToolbar, FakeButton ],
+					table: {
+						contentToolbar: [ 'fake_button' ]
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+					model = newEditor.model;
+					doc = model.document;
+					widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
+					toolbar = widgetToolbarRepository._toolbars.get( 'tableContent' ).view;
+					balloon = editor.plugins.get( 'ContextualBalloon' );
+				} );
+		} );
+
+		afterEach( () => {
+			editorElement.remove();
+
+			return editor.destroy();
+		} );
+
+		it( 'should be loaded', () => {
+			expect( editor.plugins.get( TableToolbar ) ).to.be.instanceOf( TableToolbar );
+		} );
+
+		it( 'should not initialize if there is no configuration', () => {
+			const editorElement = global.document.createElement( 'div' );
+			global.document.body.appendChild( editorElement );
+
+			return ClassicTestEditor.create( editorElement, {
+				plugins: [ TableToolbar ]
 			} )
-			.then( newEditor => {
-				editor = newEditor;
-				model = newEditor.model;
-				doc = model.document;
-				plugin = editor.plugins.get( TableToolbar );
-				toolbar = plugin._toolbar;
-				balloon = editor.plugins.get( 'ContextualBalloon' );
+				.then( editor => {
+					const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
+					expect( widgetToolbarRepository._toolbars.get( 'tableContent' ) ).to.be.undefined;
+
+					editorElement.remove();
+					return editor.destroy();
+				} );
+		} );
+
+		describe( 'toolbar', () => {
+			it( 'should use the config.table.toolbar to create items', () => {
+				expect( toolbar.items ).to.have.length( 1 );
+				expect( toolbar.items.get( 0 ).label ).to.equal( 'fake button' );
 			} );
-	} );
 
-	afterEach( () => {
-		editorElement.remove();
+			it( 'should set proper CSS classes', () => {
+				const spy = sinon.spy( balloon, 'add' );
 
-		return editor.destroy();
-	} );
+				editor.ui.focusTracker.isFocused = true;
 
-	it( 'should be loaded', () => {
-		expect( editor.plugins.get( TableToolbar ) ).to.be.instanceOf( TableToolbar );
-	} );
+				setData( model, '<table><tableRow><tableCell>[]</tableCell></tableRow></table>' );
+
+				sinon.assert.calledWithMatch( spy, {
+					view: toolbar,
+					balloonClassName: 'ck-toolbar-container'
+				} );
+			} );
+		} );
+
+		describe( 'integration with the editor focus', () => {
+			it( 'should show the toolbar when the editor gains focus and the table is selected', () => {
+				editor.ui.focusTracker.isFocused = true;
 
-	it( 'should not initialize if there is no configuration', () => {
-		const editorElement = global.document.createElement( 'div' );
-		global.document.body.appendChild( editorElement );
+				setData( model, '<table><tableRow><tableCell>[]</tableCell></tableRow></table>' );
 
-		return ClassicEditor.create( editorElement, {
-			plugins: [ TableToolbar ]
-		} )
-			.then( editor => {
-				expect( editor.plugins.get( TableToolbar )._toolbar ).to.be.undefined;
+				editor.ui.focusTracker.isFocused = false;
+				expect( balloon.visibleView ).to.be.null;
 
-				editorElement.remove();
-				return editor.destroy();
+				editor.ui.focusTracker.isFocused = true;
+				expect( balloon.visibleView ).to.equal( toolbar );
 			} );
-	} );
 
-	describe( 'toolbar', () => {
-		it( 'should use the config.table.toolbar to create items', () => {
-			expect( toolbar.items ).to.have.length( 1 );
-			expect( toolbar.items.get( 0 ).label ).to.equal( 'fake button' );
+			it( 'should hide the toolbar when the editor loses focus and the table is selected', () => {
+				editor.ui.focusTracker.isFocused = false;
+
+				setData( model, '<table><tableRow><tableCell>[]</tableCell></tableRow></table>' );
+
+				editor.ui.focusTracker.isFocused = true;
+				expect( balloon.visibleView ).to.equal( toolbar );
+
+				editor.ui.focusTracker.isFocused = false;
+				expect( balloon.visibleView ).to.be.null;
+			} );
 		} );
 
-		it( 'should set proper CSS classes', () => {
-			const spy = sinon.spy( balloon, 'add' );
+		describe( 'integration with the editor selection (ui#update event)', () => {
+			beforeEach( () => {
+				editor.ui.focusTracker.isFocused = true;
+			} );
+
+			it( 'should not show the toolbar on ui#update when the table is selected', () => {
+				setData( model, '<paragraph>foo</paragraph>[<table><tableRow><tableCell></tableCell></tableRow></table>]' );
+
+				expect( balloon.visibleView ).to.be.null;
+			} );
+
+			it( 'should show the toolbar on ui#update when the table content is selected', () => {
+				setData(
+					model,
+					'<paragraph>[foo]</paragraph><table><tableRow><tableCell><paragraph>bar</paragraph></tableCell></tableRow></table>'
+				);
+
+				expect( balloon.visibleView ).to.be.null;
 
-			editor.ui.focusTracker.isFocused = true;
+				editor.ui.fire( 'update' );
 
-			setData( model, '<table><tableRow><tableCell>[]</tableCell></tableRow></table>' );
+				expect( balloon.visibleView ).to.be.null;
+
+				model.change( writer => {
+					// Select the <tableCell>[bar]</tableCell>
+					writer.setSelection(
+						writer.createRangeOn( doc.getRoot().getNodeByPath( [ 1, 0, 0, 0 ] ) )
+					);
+				} );
 
-			sinon.assert.calledWithMatch( spy, {
-				view: toolbar,
-				balloonClassName: 'ck-toolbar-container'
+				expect( balloon.visibleView ).to.equal( toolbar );
+
+				// Make sure successive change does not throw, e.g. attempting
+				// to insert the toolbar twice.
+				editor.ui.fire( 'update' );
+				expect( balloon.visibleView ).to.equal( toolbar );
 			} );
-		} );
-	} );
 
-	describe( 'integration with the editor focus', () => {
-		it( 'should show the toolbar when the editor gains focus and the table is selected', () => {
-			editor.ui.focusTracker.isFocused = true;
+			it( 'should not engage when the toolbar is in the balloon yet invisible', () => {
+				setData( model, '<table><tableRow><tableCell><paragraph>x[y]z</paragraph></tableCell></tableRow></table>' );
 
-			setData( model, '<table><tableRow><tableCell>[]</tableCell></tableRow></table>' );
+				expect( balloon.visibleView ).to.equal( toolbar );
 
-			editor.ui.focusTracker.isFocused = false;
-			expect( balloon.visibleView ).to.be.null;
+				// Put anything on top of the ContextualBalloon stack above the table toolbar.
+				const lastView = new View();
+				lastView.element = document.createElement( 'div' );
 
-			editor.ui.focusTracker.isFocused = true;
-			expect( balloon.visibleView ).to.equal( toolbar );
-		} );
+				balloon.add( {
+					view: lastView,
+					position: {
+						target: document.body
+					}
+				} );
+
+				expect( balloon.visibleView ).to.equal( lastView );
+
+				editor.ui.fire( 'update' );
+
+				expect( balloon.visibleView ).to.equal( lastView );
+			} );
+
+			it( 'should hide the toolbar on render if the table is de–selected', () => {
+				setData(
+					model,
+					'<paragraph>foo</paragraph><table><tableRow><tableCell><paragraph>[]</paragraph></tableCell></tableRow></table>'
+				);
 
-		it( 'should hide the toolbar when the editor loses focus and the table is selected', () => {
-			editor.ui.focusTracker.isFocused = false;
+				expect( balloon.visibleView ).to.equal( toolbar );
 
-			setData( model, '<table><tableRow><tableCell>[]</tableCell></tableRow></table>' );
+				model.change( writer => {
+					// Select the <paragraph>[...]</paragraph>
+					writer.setSelection(
+						writer.createRangeIn( doc.getRoot().getChild( 0 ) )
+					);
+				} );
 
-			editor.ui.focusTracker.isFocused = true;
-			expect( balloon.visibleView ).to.equal( toolbar );
+				expect( balloon.visibleView ).to.be.null;
 
-			editor.ui.focusTracker.isFocused = false;
-			expect( balloon.visibleView ).to.be.null;
+				// Make sure successive change does not throw, e.g. attempting
+				// to remove the toolbar twice.
+				editor.ui.fire( 'update' );
+				expect( balloon.visibleView ).to.be.null;
+			} );
 		} );
 	} );
 
-	describe( 'integration with the editor selection (ui#update event)', () => {
-		beforeEach( () => {
-			editor.ui.focusTracker.isFocused = true;
+	describe( 'deprecated toolbar', () => {
+		let editor, editorElement, warnMock;
+
+		it( 'table.toolbar should work as table.contentToolbar', () => {
+			editorElement = global.document.createElement( 'div' );
+			global.document.body.appendChild( editorElement );
+			warnMock = sinon.stub( window.console, 'warn' );
+
+			return ClassicTestEditor
+				.create( editorElement, {
+					plugins: [ Paragraph, Table, TableToolbar, FakeButton ],
+					table: {
+						toolbar: [ 'fake_button' ]
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+
+					const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
+					const toolbarView = widgetToolbarRepository._toolbars.get( 'tableContent' ).view;
+
+					expect( toolbarView.items ).to.have.length( 1 );
+					expect( toolbarView.items.get( 0 ).label ).to.equal( 'fake button' );
+
+					sinon.assert.calledWith(
+						warnMock,
+						'`config.table.toolbar` is deprecated and will be removed in the next major release.' +
+						' Use `config.table.contentToolbar` instead.'
+					);
+				} );
 		} );
 
-		it( 'should not show the toolbar on ui#update when the table is selected', () => {
-			setData( model, '<paragraph>foo</paragraph>[<table><tableRow><tableCell></tableCell></tableRow></table>]' );
+		it( 'table.contentToolbar should be used if both toolbars options are provided', () => {
+			editorElement = global.document.createElement( 'div' );
+			global.document.body.appendChild( editorElement );
+			warnMock = sinon.stub( window.console, 'warn' );
+
+			return ClassicTestEditor
+				.create( editorElement, {
+					plugins: [ Paragraph, Table, TableToolbar, FakeButton, FooButton ],
+					table: {
+						toolbar: [ 'fake_button' ],
+						contentToolbar: [ 'foo_button' ],
+					}
+				} )
+				.then( newEditor => {
+					editor = newEditor;
+
+					const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
+					const toolbarView = widgetToolbarRepository._toolbars.get( 'tableContent' ).view;
+
+					expect( toolbarView.items ).to.have.length( 1 );
+					expect( toolbarView.items.get( 0 ).label ).to.equal( 'foo button' );
+				} );
+		} );
 
-			expect( balloon.visibleView ).to.be.null;
+		afterEach( () => {
+			editorElement.remove();
+
+			return editor.destroy();
 		} );
+	} );
 
-		it( 'should show the toolbar on ui#update when the table content is selected', () => {
-			setData(
-				model,
-				'<paragraph>[foo]</paragraph><table><tableRow><tableCell><paragraph>bar</paragraph></tableCell></tableRow></table>'
-			);
+	describe( 'tableToolbar', () => {
+		let editor, element, widgetToolbarRepository, balloon, toolbar, model;
+
+		beforeEach( () => {
+			element = document.createElement( 'div' );
+			document.body.appendChild( element );
 
-			expect( balloon.visibleView ).to.be.null;
+			return ClassicTestEditor.create( element, {
+				plugins: [ Paragraph, Table, TableToolbar, FakeButton ],
+				table: {
+					tableToolbar: [ 'fake_button' ]
+				}
+			} ).then( _editor => {
+				editor = _editor;
+				widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
+				toolbar = widgetToolbarRepository._toolbars.get( 'table' ).view;
+				balloon = editor.plugins.get( 'ContextualBalloon' );
+				model = editor.model;
+			} );
+		} );
 
-			editor.ui.fire( 'update' );
+		afterEach( () => {
+			return editor.destroy()
+				.then( () => element.remove() );
+		} );
 
-			expect( balloon.visibleView ).to.be.null;
+		describe( 'toolbar', () => {
+			it( 'should not initialize if there is no configuration', () => {
+				const editorElement = global.document.createElement( 'div' );
+				global.document.body.appendChild( editorElement );
+
+				return ClassicTestEditor.create( editorElement, {
+					plugins: [ TableToolbar ]
+				} )
+					.then( editor => {
+						const widgetToolbarRepository = editor.plugins.get( WidgetToolbarRepository );
+						expect( widgetToolbarRepository._toolbars.get( 'table' ) ).to.be.undefined;
+
+						editorElement.remove();
+						return editor.destroy();
+					} );
+			} );
 
-			model.change( writer => {
-				// Select the <tableCell>[bar]</tableCell>
-				writer.setSelection(
-					Range.createOn( doc.getRoot().getChild( 1 ).getChild( 0 ).getChild( 0 ).getChild( 0 ) )
-				);
+			it( 'should use the config.table.tableWidget to create items', () => {
+				expect( toolbar.items ).to.have.length( 1 );
+				expect( toolbar.items.get( 0 ).label ).to.equal( 'fake button' );
 			} );
 
-			expect( balloon.visibleView ).to.equal( toolbar );
+			it( 'should set proper CSS classes', () => {
+				const spy = sinon.spy( balloon, 'add' );
+
+				editor.ui.focusTracker.isFocused = true;
 
-			// Make sure successive change does not throw, e.g. attempting
-			// to insert the toolbar twice.
-			editor.ui.fire( 'update' );
-			expect( balloon.visibleView ).to.equal( toolbar );
+				setData( model, '[<table><tableRow><tableCell></tableCell></tableRow></table>]' );
+
+				sinon.assert.calledWithMatch( spy, {
+					view: toolbar,
+					balloonClassName: 'ck-toolbar-container'
+				} );
+			} );
 		} );
 
-		it( 'should not engage when the toolbar is in the balloon yet invisible', () => {
-			setData( model, '<table><tableRow><tableCell><paragraph>x[y]z</paragraph></tableCell></tableRow></table>' );
+		describe( 'integration with the editor focus', () => {
+			it( 'should show the toolbar when the editor gains focus and the table is selected', () => {
+				editor.ui.focusTracker.isFocused = true;
 
-			expect( balloon.visibleView ).to.equal( toolbar );
+				setData( model, '[<table><tableRow><tableCell></tableCell></tableRow></table>]' );
 
-			// Put anything on top of the ContextualBalloon stack above the table toolbar.
-			const lastView = new View();
-			lastView.element = document.createElement( 'div' );
+				editor.ui.focusTracker.isFocused = false;
+				expect( balloon.visibleView ).to.be.null;
 
-			balloon.add( {
-				view: lastView,
-				position: {
-					target: document.body
-				}
+				editor.ui.focusTracker.isFocused = true;
+				expect( balloon.visibleView ).to.equal( toolbar );
 			} );
 
-			expect( balloon.visibleView ).to.equal( lastView );
+			it( 'should hide the toolbar when the editor loses focus and the table is selected', () => {
+				editor.ui.focusTracker.isFocused = false;
 
-			editor.ui.fire( 'update' );
+				setData( model, '[<table><tableRow><tableCell></tableCell></tableRow></table>]' );
 
-			expect( balloon.visibleView ).to.equal( lastView );
+				editor.ui.focusTracker.isFocused = true;
+				expect( balloon.visibleView ).to.equal( toolbar );
+
+				editor.ui.focusTracker.isFocused = false;
+				expect( balloon.visibleView ).to.be.null;
+			} );
 		} );
 
-		it( 'should hide the toolbar on render if the table is de–selected', () => {
-			setData(
-				model,
-				'<paragraph>foo</paragraph><table><tableRow><tableCell><paragraph>[]</paragraph></tableCell></tableRow></table>'
-			);
+		describe( 'integration with the editor selection', () => {
+			beforeEach( () => {
+				editor.ui.focusTracker.isFocused = true;
+			} );
+
+			it( 'should show the toolbar on ui#update when the table widget is selected', () => {
+				setData( editor.model, '<paragraph>[foo]</paragraph><table><tableRow><tableCell></tableCell></tableRow></table>' );
 
-			expect( balloon.visibleView ).to.equal( toolbar );
+				expect( balloon.visibleView ).to.be.null;
 
-			model.change( writer => {
-				// Select the <paragraph>[...]</paragraph>
-				writer.setSelection(
-					Range.createIn( doc.getRoot().getChild( 0 ) )
-				);
+				editor.ui.fire( 'update' );
+
+				expect( balloon.visibleView ).to.be.null;
+
+				editor.model.change( writer => {
+					// Select the [<table></table>]
+					writer.setSelection( editor.model.document.getRoot().getChild( 1 ), 'on' );
+				} );
+
+				expect( balloon.visibleView ).to.equal( toolbar );
+
+				// Make sure successive change does not throw, e.g. attempting
+				// to insert the toolbar twice.
+				editor.ui.fire( 'update' );
+				expect( balloon.visibleView ).to.equal( toolbar );
 			} );
 
-			expect( balloon.visibleView ).to.be.null;
+			it( 'should not show the toolbar on ui#update when the selection is inside a table cell', () => {
+				setData( editor.model, '<table><tableRow><tableCell>[]</tableCell></tableRow></table>' );
 
-			// Make sure successive change does not throw, e.g. attempting
-			// to remove the toolbar twice.
-			editor.ui.fire( 'update' );
-			expect( balloon.visibleView ).to.be.null;
+				expect( balloon.visibleView ).to.be.null;
+
+				editor.ui.fire( 'update' );
+
+				expect( balloon.visibleView ).to.be.null;
+			} );
+
+			it( 'should not engage when the toolbar is in the balloon yet invisible', () => {
+				setData( model, '<table><tableRow><tableCell></tableCell></tableRow></table>' );
+
+				expect( balloon.visibleView ).to.equal( toolbar );
+
+				const lastView = new View();
+				lastView.element = document.createElement( 'div' );
+
+				balloon.add( {
+					view: lastView,
+					position: {
+						target: document.body
+					}
+				} );
+
+				expect( balloon.visibleView ).to.equal( lastView );
+
+				editor.ui.fire( 'update' );
+
+				expect( balloon.visibleView ).to.equal( lastView );
+			} );
+
+			it( 'should hide the toolbar on ui#update if the table is de–selected', () => {
+				setData( model, '<paragraph>foo</paragraph>[<table><tableRow><tableCell></tableCell></tableRow></table>]' );
+				expect( balloon.visibleView ).to.equal( toolbar );
+
+				model.change( writer => {
+					// Select the <paragraph>[...]</paragraph>
+					writer.setSelection( model.document.getRoot().getChild( 0 ), 'in' );
+				} );
+
+				expect( balloon.visibleView ).to.be.null;
+
+				// Make sure successive change does not throw, e.g. attempting
+				// to remove the toolbar twice.
+				editor.ui.fire( 'update' );
+				expect( balloon.visibleView ).to.be.null;
+			} );
 		} );
 	} );
+} );
 
-	// Plugin that adds fake_button to editor's component factory.
-	class FakeButton extends Plugin {
-		init() {
-			this.editor.ui.componentFactory.add( 'fake_button', locale => {
-				const view = new ButtonView( locale );
+// Plugin that adds fake_button to editor's component factory.
+class FakeButton extends Plugin {
+	init() {
+		this.editor.ui.componentFactory.add( 'fake_button', locale => {
+			const view = new ButtonView( locale );
 
-				view.set( {
-					label: 'fake button'
-				} );
+			view.set( {
+				label: 'fake button'
+			} );
 
-				return view;
+			return view;
+		} );
+	}
+}
+
+// Plugin that adds foo_button to editor's component factory.
+class FooButton extends Plugin {
+	init() {
+		this.editor.ui.componentFactory.add( 'foo_button', locale => {
+			const view = new ButtonView( locale );
+
+			view.set( {
+				label: 'foo button'
 			} );
-		}
+
+			return view;
+		} );
 	}
-} );
+}

+ 7 - 7
packages/ckeditor5-table/tests/tableui.js

@@ -226,20 +226,20 @@ describe( 'TableUI', () => {
 
 			const labels = listView.items.map( item => item instanceof ListSeparatorView ? '|' : item.children.first.label );
 
-			expect( labels ).to.deep.equal( [ 'Header column', '|', 'Insert column before', 'Insert column after', 'Delete column' ] );
+			expect( labels ).to.deep.equal( [ 'Header column', '|', 'Insert column left', 'Insert column right', 'Delete column' ] );
 		} );
 
 		it( 'should bind items in panel to proper commands', () => {
 			const items = dropdown.listView.items;
 
 			const setColumnHeaderCommand = editor.commands.get( 'setTableColumnHeader' );
-			const insertColumnBeforeCommand = editor.commands.get( 'insertTableColumnBefore' );
-			const insertColumnAfterCommand = editor.commands.get( 'insertTableColumnAfter' );
+			const insertColumnLeftCommand = editor.commands.get( 'insertTableColumnLeft' );
+			const insertColumnRightCommand = editor.commands.get( 'insertTableColumnRight' );
 			const removeColumnCommand = editor.commands.get( 'removeTableColumn' );
 
 			setColumnHeaderCommand.isEnabled = true;
-			insertColumnBeforeCommand.isEnabled = true;
-			insertColumnAfterCommand.isEnabled = true;
+			insertColumnLeftCommand.isEnabled = true;
+			insertColumnRightCommand.isEnabled = true;
 			removeColumnCommand.isEnabled = true;
 
 			expect( items.first.children.first.isEnabled ).to.be.true;
@@ -253,12 +253,12 @@ describe( 'TableUI', () => {
 			expect( items.first.children.first.isEnabled ).to.be.false;
 			expect( dropdown.buttonView.isEnabled ).to.be.true;
 
-			insertColumnBeforeCommand.isEnabled = false;
+			insertColumnLeftCommand.isEnabled = false;
 
 			expect( items.get( 2 ).children.first.isEnabled ).to.be.false;
 			expect( dropdown.buttonView.isEnabled ).to.be.true;
 
-			insertColumnAfterCommand.isEnabled = false;
+			insertColumnRightCommand.isEnabled = false;
 			expect( items.get( 3 ).children.first.isEnabled ).to.be.false;
 
 			removeColumnCommand.isEnabled = false;

+ 1 - 1
packages/ckeditor5-table/theme/icons/table-column.svg

@@ -1 +1 @@
-<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g fill="#333" fill-rule="evenodd"><path d="M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z" opacity=".6"/><path d="M18 7v1H2V7h16zm0 5v1H2v-1h16z" fill-rule="nonzero" opacity=".6"/><path d="M14 1v18a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1zm-2 1H8v4h4V2zm0 6H8v4h4V8zm0 6H8v4h4v-4z"/></g></svg>
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z" opacity=".6"/><path d="M18 7v1H2V7h16zm0 5v1H2v-1h16z" opacity=".6"/><path d="M14 1v18a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V1a1 1 0 0 1 1-1h6a1 1 0 0 1 1 1zm-2 1H8v4h4V2zm0 6H8v4h4V8zm0 6H8v4h4v-4z"/></g></svg>

+ 1 - 1
packages/ckeditor5-table/theme/icons/table-merge-cell.svg

@@ -1 +1 @@
-<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g fill="#333" fill-rule="evenodd"><path d="M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z" opacity=".6"/><path d="M7 2h1v16H7V2zm5 0h1v7h-1V2zm6 5v1H2V7h16zM8 12v1H2v-1h6z" fill-rule="nonzero" opacity=".6"/><path d="M7 7h12a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1zm1 2v9h10V9H8z"/></g></svg>
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z" opacity=".6"/><path d="M7 2h1v16H7V2zm5 0h1v7h-1V2zm6 5v1H2V7h16zM8 12v1H2v-1h6z" opacity=".6"/><path d="M7 7h12a1 1 0 0 1 1 1v11a1 1 0 0 1-1 1H7a1 1 0 0 1-1-1V8a1 1 0 0 1 1-1zm1 2v9h10V9H8z"/></g></svg>

+ 1 - 1
packages/ckeditor5-table/theme/icons/table-row.svg

@@ -1 +1 @@
-<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g fill="#333" fill-rule="evenodd"><path d="M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z" opacity=".6"/><path d="M7 2h1v16H7V2zm5 0h1v16h-1V2z" fill-rule="nonzero" opacity=".6"/><path d="M1 6h18a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm1 2v4h4V8H2zm6 0v4h4V8H8zm6 0v4h4V8h-4z"/></g></svg>
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g><path d="M2.5 1h15A1.5 1.5 0 0 1 19 2.5v15a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 17.5v-15A1.5 1.5 0 0 1 2.5 1zM2 2v16h16V2H2z" opacity=".6"/><path d="M7 2h1v16H7V2zm5 0h1v16h-1V2z" opacity=".6"/><path d="M1 6h18a1 1 0 0 1 1 1v6a1 1 0 0 1-1 1H1a1 1 0 0 1-1-1V7a1 1 0 0 1 1-1zm1 2v4h4V8H2zm6 0v4h4V8H8zm6 0v4h4V8h-4z"/></g></svg>

+ 1 - 1
packages/ckeditor5-table/theme/icons/table.svg

@@ -1 +1 @@
-<svg width="20" height="20" viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><g fill="#333" fill-rule="evenodd"><path d="M2.5 3v13.5h15V3H19v13.5a1.5 1.5 0 0 1-1.5 1.5h-15A1.5 1.5 0 0 1 1 16.5V3h1.5z"/><path d="M12 13H8v5H7v-5H1v-1h6V9H1V8h6V4.5H1v-1a2 2 0 0 1 2-2h14a2 2 0 0 1 2 2v1h-6V8h6v1h-6v3h6v1h-6v5h-1v-5zm0-1V9H8v3h4zm0-4V4.5H8V8h4z" fill-rule="nonzero"/></g></svg>
+<svg viewBox="0 0 20 20" xmlns="http://www.w3.org/2000/svg"><path d="M3 6v3h4V6H3zm0 4v3h4v-3H3zm0 4v3h4v-3H3zm5 3h4v-3H8v3zm5 0h4v-3h-4v3zm4-4v-3h-4v3h4zm0-4V6h-4v3h4zm1.5 8a1.5 1.5 0 0 1-1.5 1.5H3A1.5 1.5 0 0 1 1.5 17V4c.222-.863 1.068-1.5 2-1.5h13c.932 0 1.778.637 2 1.5v13zM12 13v-3H8v3h4zm0-4V6H8v3h4z"/></svg>