This repository has been archived by the owner on Jun 26, 2020. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 19
/
utils.js
175 lines (147 loc) · 5.4 KB
/
utils.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
/**
* @license Copyright (c) 2003-2020, CKSource - Frederico Knabben. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
/**
* @module table/utils
*/
import { isWidget, toWidget } from '@ckeditor/ckeditor5-widget/src/utils';
import { findAncestor } from './commands/utils';
/**
* Converts a given {@link module:engine/view/element~Element} to a table widget:
* * Adds a {@link module:engine/view/element~Element#_setCustomProperty custom property} allowing to recognize the table widget element.
* * Calls the {@link module:widget/utils~toWidget} function with the proper element's label creator.
*
* @param {module:engine/view/element~Element} viewElement
* @param {module:engine/view/downcastwriter~DowncastWriter} writer An instance of the view writer.
* @param {String} label The element's label. It will be concatenated with the table `alt` attribute if one is present.
* @returns {module:engine/view/element~Element}
*/
export function toTableWidget( viewElement, writer ) {
writer.setCustomProperty( 'table', true, viewElement );
return toWidget( viewElement, writer, { hasSelectionHandle: true } );
}
/**
* Checks if a given view element is a table widget.
*
* @param {module:engine/view/element~Element} viewElement
* @returns {Boolean}
*/
export function isTableWidget( viewElement ) {
return !!viewElement.getCustomProperty( 'table' ) && isWidget( viewElement );
}
/**
* Returns a table widget editing view element if one is selected.
*
* @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
* @returns {module:engine/view/element~Element|null}
*/
export function getSelectedTableWidget( selection ) {
const viewElement = selection.getSelectedElement();
if ( viewElement && isTableWidget( viewElement ) ) {
return viewElement;
}
return null;
}
/**
* Returns a table widget editing view element if one is among the selection's ancestors.
*
* @param {module:engine/view/selection~Selection|module:engine/view/documentselection~DocumentSelection} selection
* @returns {module:engine/view/element~Element|null}
*/
export function getTableWidgetAncestor( selection ) {
const parentTable = findAncestor( 'table', selection.getFirstPosition() );
if ( parentTable && isTableWidget( parentTable.parent ) ) {
return parentTable.parent;
}
return null;
}
/**
* Returns all model table cells that are fully selected (from the outside)
* within the provided model selection's ranges.
*
* To obtain the cells selected from the inside, use
* {@link module:table/utils~getTableCellsContainingSelection}.
*
* @param {module:engine/model/selection~Selection} selection
* @returns {Array.<module:engine/model/element~Element>}
*/
export function getSelectedTableCells( selection ) {
const cells = [];
for ( const range of sortRanges( selection.getRanges() ) ) {
const element = range.getContainedElement();
if ( element && element.is( 'tableCell' ) ) {
cells.push( element );
}
}
return cells;
}
/**
* Returns all model table cells that the provided model selection's ranges
* {@link module:engine/model/range~Range#start} inside.
*
* To obtain the cells selected from the outside, use
* {@link module:table/utils~getSelectedTableCells}.
*
* @param {module:engine/model/selection~Selection} selection
* @returns {Array.<module:engine/model/element~Element>}
*/
export function getTableCellsContainingSelection( selection ) {
const cells = [];
for ( const range of selection.getRanges() ) {
const cellWithSelection = findAncestor( 'tableCell', range.start );
if ( cellWithSelection ) {
cells.push( cellWithSelection );
}
}
return cells;
}
/**
* Returns all model table cells that are either completely selected
* by selection ranges or host selection range
* {@link module:engine/model/range~Range#start start positions} inside them.
*
* Combines {@link module:table/utils~getTableCellsContainingSelection} and
* {@link module:table/utils~getSelectedTableCells}.
*
* @param {module:engine/model/selection~Selection} selection
* @returns {Array.<module:engine/model/element~Element>}
*/
export function getSelectionAffectedTableCells( selection ) {
const selectedCells = getSelectedTableCells( selection );
if ( selectedCells.length ) {
return selectedCells;
}
return getTableCellsContainingSelection( selection );
}
/**
* Returns a helper object with `first` and `last` row index contained in given `tableCells`.
*
* const selectedTableCells = getSelectedTableCells( editor.model.document.selection );
*
* const { first, last } = getRowIndexes( selectedTableCells );
*
* console.log( `Selected rows ${ first } to ${ last }` );
*
* @package {Array.<module:engine/model/element~Element>}
* @returns {Object} Returns an object with `first` and `last` table row indexes.
*/
export function getRowIndexes( tableCells ) {
const allIndexesSorted = tableCells.map( cell => cell.parent.index ).sort();
return {
first: allIndexesSorted[ 0 ],
last: allIndexesSorted[ allIndexesSorted.length - 1 ]
};
}
function sortRanges( rangesIterator ) {
return Array.from( rangesIterator ).sort( compareRangeOrder );
}
function compareRangeOrder( rangeA, rangeB ) {
// Since table cell ranges are disjoint, it's enough to check their start positions.
const posA = rangeA.start;
const posB = rangeB.start;
if ( posA.isEqual( posB ) ) {
return 0;
}
return posA.isBefore( posB ) ? -1 : 1;
}