@ckeditor/ckeditor5-table 31.0.0 → 33.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/LICENSE.md +2 -2
  2. package/build/table.js +3 -3
  3. package/build/table.js.map +1 -0
  4. package/build/translations/cs.js +1 -1
  5. package/build/translations/el.js +1 -0
  6. package/build/translations/es.js +1 -1
  7. package/build/translations/id.js +1 -1
  8. package/build/translations/pt-br.js +1 -1
  9. package/build/translations/uz.js +1 -0
  10. package/build/translations/zh.js +1 -1
  11. package/lang/translations/ar.po +1 -1
  12. package/lang/translations/az.po +1 -1
  13. package/lang/translations/bg.po +1 -1
  14. package/lang/translations/cs.po +4 -4
  15. package/lang/translations/da.po +1 -1
  16. package/lang/translations/de-ch.po +1 -1
  17. package/lang/translations/de.po +1 -1
  18. package/lang/translations/el.po +261 -0
  19. package/lang/translations/en-au.po +1 -1
  20. package/lang/translations/en-gb.po +1 -1
  21. package/lang/translations/en.po +1 -1
  22. package/lang/translations/es.po +43 -43
  23. package/lang/translations/et.po +1 -1
  24. package/lang/translations/fa.po +1 -1
  25. package/lang/translations/fi.po +1 -1
  26. package/lang/translations/fr.po +1 -1
  27. package/lang/translations/gl.po +1 -1
  28. package/lang/translations/hi.po +1 -1
  29. package/lang/translations/hr.po +1 -1
  30. package/lang/translations/hu.po +1 -1
  31. package/lang/translations/id.po +11 -11
  32. package/lang/translations/it.po +1 -1
  33. package/lang/translations/ja.po +1 -1
  34. package/lang/translations/ko.po +1 -1
  35. package/lang/translations/ku.po +1 -1
  36. package/lang/translations/lt.po +1 -1
  37. package/lang/translations/lv.po +1 -1
  38. package/lang/translations/nb.po +1 -1
  39. package/lang/translations/ne.po +1 -1
  40. package/lang/translations/nl.po +1 -1
  41. package/lang/translations/no.po +1 -1
  42. package/lang/translations/pl.po +1 -1
  43. package/lang/translations/pt-br.po +4 -4
  44. package/lang/translations/ro.po +1 -1
  45. package/lang/translations/ru.po +1 -1
  46. package/lang/translations/sk.po +1 -1
  47. package/lang/translations/sq.po +1 -1
  48. package/lang/translations/sr-latn.po +1 -1
  49. package/lang/translations/sr.po +1 -1
  50. package/lang/translations/sv.po +1 -1
  51. package/lang/translations/th.po +1 -1
  52. package/lang/translations/tk.po +1 -1
  53. package/lang/translations/tr.po +1 -1
  54. package/lang/translations/ug.po +1 -1
  55. package/lang/translations/uk.po +1 -1
  56. package/lang/translations/uz.po +261 -0
  57. package/lang/translations/vi.po +1 -1
  58. package/lang/translations/zh-cn.po +1 -1
  59. package/lang/translations/zh.po +4 -4
  60. package/package.json +26 -24
  61. package/src/commands/insertcolumncommand.js +5 -5
  62. package/src/commands/insertrowcommand.js +5 -5
  63. package/src/commands/inserttablecommand.js +1 -1
  64. package/src/commands/mergecellcommand.js +5 -6
  65. package/src/commands/mergecellscommand.js +6 -5
  66. package/src/commands/removecolumncommand.js +9 -8
  67. package/src/commands/removerowcommand.js +6 -7
  68. package/src/commands/selectcolumncommand.js +5 -5
  69. package/src/commands/selectrowcommand.js +6 -6
  70. package/src/commands/setheadercolumncommand.js +6 -5
  71. package/src/commands/setheaderrowcommand.js +8 -5
  72. package/src/commands/splitcellcommand.js +5 -5
  73. package/src/converters/downcast.js +77 -408
  74. package/src/converters/table-caption-post-fixer.js +1 -1
  75. package/src/converters/table-cell-paragraph-post-fixer.js +1 -1
  76. package/src/converters/{table-cell-refresh-post-fixer.js → table-cell-refresh-handler.js} +9 -20
  77. package/src/converters/table-headings-refresh-handler.js +68 -0
  78. package/src/converters/table-layout-post-fixer.js +1 -1
  79. package/src/converters/tableproperties.js +6 -5
  80. package/src/converters/upcasttable.js +7 -3
  81. package/src/index.js +1 -1
  82. package/src/plaintableoutput.js +151 -0
  83. package/src/table.js +1 -1
  84. package/src/tablecaption/tablecaptionediting.js +1 -1
  85. package/src/tablecaption/tablecaptionui.js +3 -3
  86. package/src/tablecaption/toggletablecaptioncommand.js +3 -3
  87. package/src/tablecaption/utils.js +1 -1
  88. package/src/tablecaption.js +1 -1
  89. package/src/tablecellproperties/commands/tablecellbackgroundcolorcommand.js +2 -2
  90. package/src/tablecellproperties/commands/tablecellbordercolorcommand.js +2 -2
  91. package/src/tablecellproperties/commands/tablecellborderstylecommand.js +2 -2
  92. package/src/tablecellproperties/commands/tablecellborderwidthcommand.js +2 -2
  93. package/src/tablecellproperties/commands/tablecellheightcommand.js +2 -2
  94. package/src/tablecellproperties/commands/tablecellhorizontalalignmentcommand.js +2 -2
  95. package/src/tablecellproperties/commands/tablecellpaddingcommand.js +2 -2
  96. package/src/tablecellproperties/commands/tablecellpropertycommand.js +6 -5
  97. package/src/tablecellproperties/commands/tablecellverticalalignmentcommand.js +2 -2
  98. package/src/tablecellproperties/commands/tablecellwidthcommand.js +2 -2
  99. package/src/tablecellproperties/tablecellpropertiesediting.js +42 -35
  100. package/src/tablecellproperties/tablecellpropertiesui.js +1 -1
  101. package/src/tablecellproperties/ui/tablecellpropertiesview.js +11 -1
  102. package/src/tablecellproperties.js +1 -1
  103. package/src/tableclipboard.js +19 -16
  104. package/src/tableediting.js +49 -28
  105. package/src/tablekeyboard.js +7 -6
  106. package/src/tablemouse/mouseeventsobserver.js +1 -1
  107. package/src/tablemouse.js +7 -5
  108. package/src/tableproperties/commands/tablealignmentcommand.js +2 -2
  109. package/src/tableproperties/commands/tablebackgroundcolorcommand.js +2 -2
  110. package/src/tableproperties/commands/tablebordercolorcommand.js +2 -2
  111. package/src/tableproperties/commands/tableborderstylecommand.js +2 -2
  112. package/src/tableproperties/commands/tableborderwidthcommand.js +2 -2
  113. package/src/tableproperties/commands/tableheightcommand.js +2 -2
  114. package/src/tableproperties/commands/tablepropertycommand.js +2 -2
  115. package/src/tableproperties/commands/tablewidthcommand.js +2 -2
  116. package/src/tableproperties/tablepropertiesediting.js +29 -21
  117. package/src/tableproperties/tablepropertiesui.js +1 -1
  118. package/src/tableproperties/ui/tablepropertiesview.js +11 -1
  119. package/src/tableproperties.js +1 -1
  120. package/src/tableselection.js +10 -9
  121. package/src/tabletoolbar.js +1 -1
  122. package/src/tableui.js +1 -1
  123. package/src/tableutils.js +311 -1
  124. package/src/tablewalker.js +1 -1
  125. package/src/ui/colorinputview.js +1 -1
  126. package/src/ui/formrowview.js +1 -1
  127. package/src/ui/inserttableview.js +1 -1
  128. package/src/utils/common.js +1 -1
  129. package/src/utils/structure.js +1 -1
  130. package/src/utils/table-properties.js +1 -1
  131. package/src/utils/ui/contextualballoon.js +1 -1
  132. package/src/utils/ui/table-properties.js +1 -1
  133. package/src/utils/ui/widget.js +1 -1
  134. package/theme/colorinput.css +1 -1
  135. package/theme/form.css +1 -1
  136. package/theme/formrow.css +1 -1
  137. package/theme/inserttable.css +1 -1
  138. package/theme/table.css +2 -2
  139. package/theme/tablecaption.css +1 -1
  140. package/theme/tablecellproperties.css +1 -1
  141. package/theme/tableediting.css +1 -1
  142. package/theme/tableform.css +1 -1
  143. package/theme/tableproperties.css +1 -1
  144. package/theme/tableselection.css +1 -1
  145. package/src/converters/table-heading-rows-refresh-post-fixer.js +0 -72
  146. package/src/utils/selection.js +0 -276
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -40,7 +40,7 @@ export default class TableHeightCommand extends TablePropertyCommand {
40
40
  * @param {String} defaultValue The default value of the attribute.
41
41
  */
42
42
  constructor( editor, defaultValue ) {
43
- super( editor, 'height', defaultValue );
43
+ super( editor, 'tableHeight', defaultValue );
44
44
  }
45
45
 
46
46
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -77,7 +77,7 @@ export default class TablePropertyCommand extends Command {
77
77
  const table = selection.getFirstPosition().findAncestor( 'table' );
78
78
  const valueToSet = this._getValueToSet( value );
79
79
 
80
- model.enqueueChange( batch || 'default', writer => {
80
+ model.enqueueChange( batch, writer => {
81
81
  if ( valueToSet ) {
82
82
  writer.setAttribute( this.attributeName, valueToSet, table );
83
83
  } else {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -40,7 +40,7 @@ export default class TableWidthCommand extends TablePropertyCommand {
40
40
  * @param {String} defaultValue The default value of the attribute.
41
41
  */
42
42
  constructor( editor, defaultValue ) {
43
- super( editor, 'width', defaultValue );
43
+ super( editor, 'tableWidth', defaultValue );
44
44
  }
45
45
 
46
46
  /**
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -34,10 +34,10 @@ const FLOAT_VALUES_REG_EXP = /^(left|none|right)$/;
34
34
  *
35
35
  * Introduces table's model attributes and their conversion:
36
36
  *
37
- * - border: `borderStyle`, `borderColor` and `borderWidth`
38
- * - background color: `backgroundColor`
39
- * - horizontal alignment: `alignment`
40
- * - width & height: `width` & `height`
37
+ * - border: `tableBorderStyle`, `tableBorderColor` and `tableBorderWidth`
38
+ * - background color: `tableBackgroundColor`
39
+ * - horizontal alignment: `tableAlignment`
40
+ * - width & height: `tableWidth` & `tableHeight`
41
41
  *
42
42
  * It also registers commands used to manipulate the above attributes:
43
43
  *
@@ -91,14 +91,14 @@ export default class TablePropertiesEditing extends Plugin {
91
91
  editor.commands.add( 'tableAlignment', new TableAlignmentCommand( editor, defaultTableProperties.alignment ) );
92
92
 
93
93
  enableTableToFigureProperty( schema, conversion, {
94
- modelAttribute: 'width',
94
+ modelAttribute: 'tableWidth',
95
95
  styleName: 'width',
96
96
  defaultValue: defaultTableProperties.width
97
97
  } );
98
98
  editor.commands.add( 'tableWidth', new TableWidthCommand( editor, defaultTableProperties.width ) );
99
99
 
100
100
  enableTableToFigureProperty( schema, conversion, {
101
- modelAttribute: 'height',
101
+ modelAttribute: 'tableHeight',
102
102
  styleName: 'height',
103
103
  defaultValue: defaultTableProperties.height
104
104
  } );
@@ -106,7 +106,7 @@ export default class TablePropertiesEditing extends Plugin {
106
106
 
107
107
  editor.data.addStyleProcessorRules( addBackgroundRules );
108
108
  enableProperty( schema, conversion, {
109
- modelAttribute: 'backgroundColor',
109
+ modelAttribute: 'tableBackgroundColor',
110
110
  styleName: 'background-color',
111
111
  defaultValue: defaultTableProperties.backgroundColor
112
112
  } );
@@ -117,22 +117,30 @@ export default class TablePropertiesEditing extends Plugin {
117
117
  }
118
118
  }
119
119
 
120
- // Enables `'borderStyle'`, `'borderColor'` and `'borderWidth'` attributes for table.
120
+ // Enables `tableBorderStyle'`, `tableBorderColor'` and `tableBorderWidth'` attributes for table.
121
121
  //
122
122
  // @param {module:engine/model/schema~Schema} schema
123
123
  // @param {module:engine/conversion/conversion~Conversion} conversion
124
124
  // @param {Object} defaultBorder The default border values.
125
- // @param {String} defaultBorder.color The default `borderColor` value.
126
- // @param {String} defaultBorder.style The default `borderStyle` value.
127
- // @param {String} defaultBorder.width The default `borderWidth` value.
125
+ // @param {String} defaultBorder.color The default `tableBorderColor` value.
126
+ // @param {String} defaultBorder.style The default `tableBorderStyle` value.
127
+ // @param {String} defaultBorder.width The default `tableBorderWidth` value.
128
128
  function enableBorderProperties( schema, conversion, defaultBorder ) {
129
+ const modelAttributes = {
130
+ width: 'tableBorderWidth',
131
+ color: 'tableBorderColor',
132
+ style: 'tableBorderStyle'
133
+ };
134
+
129
135
  schema.extend( 'table', {
130
- allowAttributes: [ 'borderWidth', 'borderColor', 'borderStyle' ]
136
+ allowAttributes: Object.values( modelAttributes )
131
137
  } );
132
- upcastBorderStyles( conversion, 'table', defaultBorder );
133
- downcastTableAttribute( conversion, { modelAttribute: 'borderColor', styleName: 'border-color' } );
134
- downcastTableAttribute( conversion, { modelAttribute: 'borderStyle', styleName: 'border-style' } );
135
- downcastTableAttribute( conversion, { modelAttribute: 'borderWidth', styleName: 'border-width' } );
138
+
139
+ upcastBorderStyles( conversion, 'table', modelAttributes, defaultBorder );
140
+
141
+ downcastTableAttribute( conversion, { modelAttribute: modelAttributes.color, styleName: 'border-color' } );
142
+ downcastTableAttribute( conversion, { modelAttribute: modelAttributes.style, styleName: 'border-style' } );
143
+ downcastTableAttribute( conversion, { modelAttribute: modelAttributes.width, styleName: 'border-width' } );
136
144
  }
137
145
 
138
146
  // Enables the `'alignment'` attribute for table.
@@ -142,14 +150,14 @@ function enableBorderProperties( schema, conversion, defaultBorder ) {
142
150
  // @param {String} defaultValue The default alignment value.
143
151
  function enableAlignmentProperty( schema, conversion, defaultValue ) {
144
152
  schema.extend( 'table', {
145
- allowAttributes: [ 'alignment' ]
153
+ allowAttributes: [ 'tableAlignment' ]
146
154
  } );
147
155
 
148
156
  conversion.for( 'downcast' )
149
157
  .attributeToAttribute( {
150
158
  model: {
151
159
  name: 'table',
152
- key: 'alignment'
160
+ key: 'tableAlignment'
153
161
  },
154
162
  view: alignment => ( {
155
163
  key: 'style',
@@ -171,7 +179,7 @@ function enableAlignmentProperty( schema, conversion, defaultValue ) {
171
179
  }
172
180
  },
173
181
  model: {
174
- key: 'alignment',
182
+ key: 'tableAlignment',
175
183
  value: viewElement => {
176
184
  let align = viewElement.getStyle( 'float' );
177
185
 
@@ -193,7 +201,7 @@ function enableAlignmentProperty( schema, conversion, defaultValue ) {
193
201
  },
194
202
  model: {
195
203
  name: 'table',
196
- key: 'alignment',
204
+ key: 'tableAlignment',
197
205
  value: viewElement => {
198
206
  const align = viewElement.getAttribute( 'align' );
199
207
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -379,6 +379,16 @@ export default class TablePropertiesView extends View {
379
379
  this.keystrokes.listenTo( this.element );
380
380
  }
381
381
 
382
+ /**
383
+ * @inheritDoc
384
+ */
385
+ destroy() {
386
+ super.destroy();
387
+
388
+ this.focusTracker.destroy();
389
+ this.keystrokes.destroy();
390
+ }
391
+
382
392
  /**
383
393
  * Focuses the fist focusable field in the form.
384
394
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -14,7 +14,6 @@ import TableWalker from './tablewalker';
14
14
  import TableUtils from './tableutils';
15
15
 
16
16
  import { cropTableToDimensions, adjustLastRowIndex, adjustLastColumnIndex } from './utils/structure';
17
- import { getColumnIndexes, getRowIndexes, getSelectedTableCells, isSelectionRectangular } from './utils/selection';
18
17
 
19
18
  import '../theme/tableselection.css';
20
19
 
@@ -36,7 +35,7 @@ export default class TableSelection extends Plugin {
36
35
  * @inheritDoc
37
36
  */
38
37
  static get requires() {
39
- return [ TableUtils ];
38
+ return [ TableUtils, TableUtils ];
40
39
  }
41
40
 
42
41
  /**
@@ -58,9 +57,10 @@ export default class TableSelection extends Plugin {
58
57
  * @returns {Array.<module:engine/model/element~Element>|null}
59
58
  */
60
59
  getSelectedTableCells() {
60
+ const tableUtils = this.editor.plugins.get( TableUtils );
61
61
  const selection = this.editor.model.document.selection;
62
62
 
63
- const selectedCells = getSelectedTableCells( selection );
63
+ const selectedCells = tableUtils.getSelectedTableCells( selection );
64
64
 
65
65
  if ( selectedCells.length == 0 ) {
66
66
  return null;
@@ -81,6 +81,7 @@ export default class TableSelection extends Plugin {
81
81
  * @returns {module:engine/model/documentfragment~DocumentFragment|null}
82
82
  */
83
83
  getSelectionAsFragment() {
84
+ const tableUtils = this.editor.plugins.get( TableUtils );
84
85
  const selectedCells = this.getSelectedTableCells();
85
86
 
86
87
  if ( !selectedCells ) {
@@ -89,10 +90,9 @@ export default class TableSelection extends Plugin {
89
90
 
90
91
  return this.editor.model.change( writer => {
91
92
  const documentFragment = writer.createDocumentFragment();
92
- const tableUtils = this.editor.plugins.get( 'TableUtils' );
93
93
 
94
- const { first: firstColumn, last: lastColumn } = getColumnIndexes( selectedCells );
95
- const { first: firstRow, last: lastRow } = getRowIndexes( selectedCells );
94
+ const { first: firstColumn, last: lastColumn } = tableUtils.getColumnIndexes( selectedCells );
95
+ const { first: firstRow, last: lastRow } = tableUtils.getRowIndexes( selectedCells );
96
96
 
97
97
  const sourceTable = selectedCells[ 0 ].findAncestor( 'table' );
98
98
 
@@ -101,7 +101,7 @@ export default class TableSelection extends Plugin {
101
101
 
102
102
  // If the selection is rectangular there could be a case of all cells in the last row/column spanned over
103
103
  // next row/column so the real lastRow/lastColumn should be updated.
104
- if ( isSelectionRectangular( selectedCells, tableUtils ) ) {
104
+ if ( tableUtils.isSelectionRectangular( selectedCells ) ) {
105
105
  const dimensions = {
106
106
  firstColumn,
107
107
  lastColumn,
@@ -269,10 +269,11 @@ export default class TableSelection extends Plugin {
269
269
  * @param {Array.<*>} args Delete content method arguments.
270
270
  */
271
271
  _handleDeleteContent( event, args ) {
272
+ const tableUtils = this.editor.plugins.get( TableUtils );
272
273
  const [ selection, options ] = args;
273
274
  const model = this.editor.model;
274
275
  const isBackward = !options || options.direction == 'backward';
275
- const selectedTableCells = getSelectedTableCells( selection );
276
+ const selectedTableCells = tableUtils.getSelectedTableCells( selection );
276
277
 
277
278
  if ( !selectedTableCells.length ) {
278
279
  return;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
package/src/tableui.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
package/src/tableutils.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -775,6 +775,288 @@ export default class TableUtils extends Plugin {
775
775
  return Array.from( table.getChildren() )
776
776
  .reduce( ( rowCount, child ) => child.is( 'element', 'tableRow' ) ? rowCount + 1 : rowCount, 0 );
777
777
  }
778
+
779
+ /**
780
+ * Creates an instance of the table walker.
781
+ *
782
+ * The table walker iterates internally by traversing the table from row index = 0 and column index = 0.
783
+ * It walks row by row and column by column in order to output values defined in the options.
784
+ * By default it will output only the locations that are occupied by a cell. To include also spanned rows and columns,
785
+ * pass the `includeAllSlots` option.
786
+ *
787
+ * @protected
788
+ * @param {module:engine/model/element~Element} table A table over which the walker iterates.
789
+ * @param {Object} [options={}] An object with configuration.
790
+ * @param {Number} [options.row] A row index for which this iterator will output cells.
791
+ * Can't be used together with `startRow` and `endRow`.
792
+ * @param {Number} [options.startRow=0] A row index from which this iterator should start. Can't be used together with `row`.
793
+ * @param {Number} [options.endRow] A row index at which this iterator should end. Can't be used together with `row`.
794
+ * @param {Number} [options.column] A column index for which this iterator will output cells.
795
+ * Can't be used together with `startColumn` and `endColumn`.
796
+ * @param {Number} [options.startColumn=0] A column index from which this iterator should start. Can't be used together with `column`.
797
+ * @param {Number} [options.endColumn] A column index at which this iterator should end. Can't be used together with `column`.
798
+ * @param {Boolean} [options.includeAllSlots=false] Also return values for spanned cells.
799
+ */
800
+ createTableWalker( table, options = {} ) {
801
+ return new TableWalker( table, options );
802
+ }
803
+
804
+ /**
805
+ * Returns all model table cells that are fully selected (from the outside)
806
+ * within the provided model selection's ranges.
807
+ *
808
+ * To obtain the cells selected from the inside, use
809
+ * {@link #getTableCellsContainingSelection}.
810
+ *
811
+ * @param {module:engine/model/selection~Selection} selection
812
+ * @returns {Array.<module:engine/model/element~Element>}
813
+ */
814
+ getSelectedTableCells( selection ) {
815
+ const cells = [];
816
+
817
+ for ( const range of this.sortRanges( selection.getRanges() ) ) {
818
+ const element = range.getContainedElement();
819
+
820
+ if ( element && element.is( 'element', 'tableCell' ) ) {
821
+ cells.push( element );
822
+ }
823
+ }
824
+
825
+ return cells;
826
+ }
827
+
828
+ /**
829
+ * Returns all model table cells that the provided model selection's ranges
830
+ * {@link module:engine/model/range~Range#start} inside.
831
+ *
832
+ * To obtain the cells selected from the outside, use
833
+ * {@link #getSelectedTableCells}.
834
+ *
835
+ * @param {module:engine/model/selection~Selection} selection
836
+ * @returns {Array.<module:engine/model/element~Element>}
837
+ */
838
+ getTableCellsContainingSelection( selection ) {
839
+ const cells = [];
840
+
841
+ for ( const range of selection.getRanges() ) {
842
+ const cellWithSelection = range.start.findAncestor( 'tableCell' );
843
+
844
+ if ( cellWithSelection ) {
845
+ cells.push( cellWithSelection );
846
+ }
847
+ }
848
+
849
+ return cells;
850
+ }
851
+
852
+ /**
853
+ * Returns all model table cells that are either completely selected
854
+ * by selection ranges or host selection range
855
+ * {@link module:engine/model/range~Range#start start positions} inside them.
856
+ *
857
+ * Combines {@link #getTableCellsContainingSelection} and
858
+ * {@link #getSelectedTableCells}.
859
+ *
860
+ * @param {module:engine/model/selection~Selection} selection
861
+ * @returns {Array.<module:engine/model/element~Element>}
862
+ */
863
+ getSelectionAffectedTableCells( selection ) {
864
+ const selectedCells = this.getSelectedTableCells( selection );
865
+
866
+ if ( selectedCells.length ) {
867
+ return selectedCells;
868
+ }
869
+
870
+ return this.getTableCellsContainingSelection( selection );
871
+ }
872
+
873
+ /**
874
+ * Returns an object with the `first` and `last` row index contained in the given `tableCells`.
875
+ *
876
+ * const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
877
+ *
878
+ * const { first, last } = getRowIndexes( selectedTableCells );
879
+ *
880
+ * console.log( `Selected rows: ${ first } to ${ last }` );
881
+ *
882
+ * @param {Array.<module:engine/model/element~Element>} tableCells
883
+ * @returns {Object} Returns an object with the `first` and `last` table row indexes.
884
+ */
885
+ getRowIndexes( tableCells ) {
886
+ const indexes = tableCells.map( cell => cell.parent.index );
887
+
888
+ return this._getFirstLastIndexesObject( indexes );
889
+ }
890
+
891
+ /**
892
+ * Returns an object with the `first` and `last` column index contained in the given `tableCells`.
893
+ *
894
+ * const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
895
+ *
896
+ * const { first, last } = getColumnIndexes( selectedTableCells );
897
+ *
898
+ * console.log( `Selected columns: ${ first } to ${ last }` );
899
+ *
900
+ * @param {Array.<module:engine/model/element~Element>} tableCells
901
+ * @returns {Object} Returns an object with the `first` and `last` table column indexes.
902
+ */
903
+ getColumnIndexes( tableCells ) {
904
+ const table = tableCells[ 0 ].findAncestor( 'table' );
905
+ const tableMap = [ ...new TableWalker( table ) ];
906
+
907
+ const indexes = tableMap
908
+ .filter( entry => tableCells.includes( entry.cell ) )
909
+ .map( entry => entry.column );
910
+
911
+ return this._getFirstLastIndexesObject( indexes );
912
+ }
913
+
914
+ /**
915
+ * Checks if the selection contains cells that do not exceed rectangular selection.
916
+ *
917
+ * In a table below:
918
+ *
919
+ * ┌───┬───┬───┬───┐
920
+ * │ a │ b │ c │ d │
921
+ * ├───┴───┼───┤ │
922
+ * │ e │ f │ │
923
+ * │ ├───┼───┤
924
+ * │ │ g │ h │
925
+ * └───────┴───┴───┘
926
+ *
927
+ * Valid selections are these which create a solid rectangle (without gaps), such as:
928
+ * - a, b (two horizontal cells)
929
+ * - c, f (two vertical cells)
930
+ * - a, b, e (cell "e" spans over four cells)
931
+ * - c, d, f (cell d spans over a cell in the row below)
932
+ *
933
+ * While an invalid selection would be:
934
+ * - a, c (the unselected cell "b" creates a gap)
935
+ * - f, g, h (cell "d" spans over a cell from the row of "f" cell - thus creates a gap)
936
+ *
937
+ * @param {Array.<module:engine/model/element~Element>} selectedTableCells
938
+ * @returns {Boolean}
939
+ */
940
+ isSelectionRectangular( selectedTableCells ) {
941
+ if ( selectedTableCells.length < 2 || !this._areCellInTheSameTableSection( selectedTableCells ) ) {
942
+ return false;
943
+ }
944
+
945
+ // A valid selection is a fully occupied rectangle composed of table cells.
946
+ // Below we will calculate the area of a selected table cells and the area of valid selection.
947
+ // The area of a valid selection is defined by top-left and bottom-right cells.
948
+ const rows = new Set();
949
+ const columns = new Set();
950
+
951
+ let areaOfSelectedCells = 0;
952
+
953
+ for ( const tableCell of selectedTableCells ) {
954
+ const { row, column } = this.getCellLocation( tableCell );
955
+ const rowspan = parseInt( tableCell.getAttribute( 'rowspan' ) || 1 );
956
+ const colspan = parseInt( tableCell.getAttribute( 'colspan' ) || 1 );
957
+
958
+ // Record row & column indexes of current cell.
959
+ rows.add( row );
960
+ columns.add( column );
961
+
962
+ // For cells that spans over multiple rows add also the last row that this cell spans over.
963
+ if ( rowspan > 1 ) {
964
+ rows.add( row + rowspan - 1 );
965
+ }
966
+
967
+ // For cells that spans over multiple columns add also the last column that this cell spans over.
968
+ if ( colspan > 1 ) {
969
+ columns.add( column + colspan - 1 );
970
+ }
971
+
972
+ areaOfSelectedCells += ( rowspan * colspan );
973
+ }
974
+
975
+ // We can only merge table cells that are in adjacent rows...
976
+ const areaOfValidSelection = getBiggestRectangleArea( rows, columns );
977
+
978
+ return areaOfValidSelection == areaOfSelectedCells;
979
+ }
980
+
981
+ /**
982
+ * Returns array of sorted ranges.
983
+ *
984
+ * @param {Iterable.<module:engine/model/range~Range>} ranges
985
+ * @return {Array.<module:engine/model/range~Range>}
986
+ */
987
+ sortRanges( ranges ) {
988
+ return Array.from( ranges ).sort( compareRangeOrder );
989
+ }
990
+
991
+ /**
992
+ * Helper method to get an object with `first` and `last` indexes from an unsorted array of indexes.
993
+ *
994
+ * @private
995
+ * @param {Number[]} indexes
996
+ * @returns {Object}
997
+ */
998
+ _getFirstLastIndexesObject( indexes ) {
999
+ const allIndexesSorted = indexes.sort( ( indexA, indexB ) => indexA - indexB );
1000
+
1001
+ const first = allIndexesSorted[ 0 ];
1002
+ const last = allIndexesSorted[ allIndexesSorted.length - 1 ];
1003
+
1004
+ return { first, last };
1005
+ }
1006
+
1007
+ /**
1008
+ * Checks if the selection does not mix a header (column or row) with other cells.
1009
+ *
1010
+ * For instance, in the table below valid selections consist of cells with the same letter only.
1011
+ * So, a-a (same heading row and column) or d-d (body cells) are valid while c-d or a-b are not.
1012
+ *
1013
+ * header columns
1014
+ * ↓ ↓
1015
+ * ┌───┬───┬───┬───┐
1016
+ * │ a │ a │ b │ b │ ← header row
1017
+ * ├───┼───┼───┼───┤
1018
+ * │ c │ c │ d │ d │
1019
+ * ├───┼───┼───┼───┤
1020
+ * │ c │ c │ d │ d │
1021
+ * └───┴───┴───┴───┘
1022
+ *
1023
+ * @private
1024
+ * @param {Array.<module:engine/model/element~Element>} tableCells
1025
+ * @returns {Boolean}
1026
+ */
1027
+ _areCellInTheSameTableSection( tableCells ) {
1028
+ const table = tableCells[ 0 ].findAncestor( 'table' );
1029
+
1030
+ const rowIndexes = this.getRowIndexes( tableCells );
1031
+ const headingRows = parseInt( table.getAttribute( 'headingRows' ) || 0 );
1032
+
1033
+ // Calculating row indexes is a bit cheaper so if this check fails we can't merge.
1034
+ if ( !this._areIndexesInSameSection( rowIndexes, headingRows ) ) {
1035
+ return false;
1036
+ }
1037
+
1038
+ const headingColumns = parseInt( table.getAttribute( 'headingColumns' ) || 0 );
1039
+ const columnIndexes = this.getColumnIndexes( tableCells );
1040
+
1041
+ // Similarly cells must be in same column section.
1042
+ return this._areIndexesInSameSection( columnIndexes, headingColumns );
1043
+ }
1044
+
1045
+ /**
1046
+ * Unified check if table rows/columns indexes are in the same heading/body section.
1047
+ *
1048
+ * @private
1049
+ * @param {Object} params
1050
+ * @param {Number} params.first
1051
+ * @param {Number} params.last
1052
+ * @param {Number} headingSectionSize
1053
+ */
1054
+ _areIndexesInSameSection( { first, last }, headingSectionSize ) {
1055
+ const firstCellIsInHeading = first < headingSectionSize;
1056
+ const lastCellIsInHeading = last < headingSectionSize;
1057
+
1058
+ return firstCellIsInHeading === lastCellIsInHeading;
1059
+ }
778
1060
  }
779
1061
 
780
1062
  // Creates empty rows at the given index in an existing table.
@@ -943,3 +1225,31 @@ function moveCellsToRow( table, targetRowIndex, cellsToMove, writer ) {
943
1225
  }
944
1226
  }
945
1227
  }
1228
+
1229
+ function compareRangeOrder( rangeA, rangeB ) {
1230
+ // Since table cell ranges are disjoint, it's enough to check their start positions.
1231
+ const posA = rangeA.start;
1232
+ const posB = rangeB.start;
1233
+
1234
+ // Checking for equal position (returning 0) is not needed because this would be either:
1235
+ // a. Intersecting range (not allowed by model)
1236
+ // b. Collapsed range on the same position (allowed by model but should not happen).
1237
+ return posA.isBefore( posB ) ? -1 : 1;
1238
+ }
1239
+
1240
+ // Calculates the area of a maximum rectangle that can span over the provided row & column indexes.
1241
+ //
1242
+ // @param {Array.<Number>} rows
1243
+ // @param {Array.<Number>} columns
1244
+ // @returns {Number}
1245
+ function getBiggestRectangleArea( rows, columns ) {
1246
+ const rowsIndexes = Array.from( rows.values() );
1247
+ const columnIndexes = Array.from( columns.values() );
1248
+
1249
+ const lastRow = Math.max( ...rowsIndexes );
1250
+ const firstRow = Math.min( ...rowsIndexes );
1251
+ const lastColumn = Math.max( ...columnIndexes );
1252
+ const firstColumn = Math.min( ...columnIndexes );
1253
+
1254
+ return ( lastRow - firstRow + 1 ) * ( lastColumn - firstColumn + 1 );
1255
+ }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * @license Copyright (c) 2003-2021, CKSource - Frederico Knabben. All rights reserved.
2
+ * @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
3
3
  * For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
4
4
  */
5
5