handsontable 0.0.0-next-abfe462-20231207 → 0.0.0-next-3d099da-20231208
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of handsontable might be problematic. Click here for more details.
- package/3rdparty/walkontable/src/calculator/index.js +6 -11
- package/3rdparty/walkontable/src/calculator/index.mjs +3 -5
- package/3rdparty/walkontable/src/calculator/viewportColumns.js +122 -1
- package/3rdparty/walkontable/src/calculator/viewportColumns.mjs +124 -2
- package/3rdparty/walkontable/src/calculator/viewportRows.js +1 -1
- package/3rdparty/walkontable/src/calculator/viewportRows.mjs +3 -2
- package/3rdparty/walkontable/src/core/_base.js +12 -0
- package/3rdparty/walkontable/src/core/_base.mjs +12 -0
- package/3rdparty/walkontable/src/core/core.js +2 -0
- package/3rdparty/walkontable/src/core/core.mjs +2 -0
- package/3rdparty/walkontable/src/facade/core.js +9 -0
- package/3rdparty/walkontable/src/facade/core.mjs +9 -0
- package/3rdparty/walkontable/src/index.js +4 -3
- package/3rdparty/walkontable/src/index.mjs +2 -1
- package/3rdparty/walkontable/src/overlays.js +3 -0
- package/3rdparty/walkontable/src/overlays.mjs +4 -0
- package/3rdparty/walkontable/src/renderer/colGroup.js +0 -10
- package/3rdparty/walkontable/src/renderer/colGroup.mjs +0 -10
- package/3rdparty/walkontable/src/renderer/rows.js +3 -4
- package/3rdparty/walkontable/src/renderer/rows.mjs +3 -4
- package/3rdparty/walkontable/src/selection/manager.js +1 -0
- package/3rdparty/walkontable/src/selection/manager.mjs +1 -0
- package/3rdparty/walkontable/src/settings.js +0 -3
- package/3rdparty/walkontable/src/settings.mjs +0 -2
- package/3rdparty/walkontable/src/table.js +1 -0
- package/3rdparty/walkontable/src/table.mjs +1 -0
- package/3rdparty/walkontable/src/utils/column.js +12 -27
- package/3rdparty/walkontable/src/utils/column.mjs +12 -27
- package/3rdparty/walkontable/src/viewport.js +17 -22
- package/3rdparty/walkontable/src/viewport.mjs +18 -23
- package/base.js +2 -4
- package/base.mjs +2 -2
- package/core/focusCatcher/index.js +6 -44
- package/core/focusCatcher/index.mjs +6 -44
- package/core.js +11 -0
- package/core.mjs +11 -0
- package/dataMap/dataMap.js +0 -1
- package/dataMap/metaManager/metaSchema.js +2 -28
- package/dataMap/metaManager/metaSchema.mjs +2 -28
- package/dataMap/metaManager/mods/extendMetaProperties.js +0 -12
- package/dataMap/metaManager/mods/extendMetaProperties.mjs +0 -12
- package/dist/handsontable.css +2 -2
- package/dist/handsontable.full.css +2 -2
- package/dist/handsontable.full.js +3902 -2639
- package/dist/handsontable.full.min.css +2 -2
- package/dist/handsontable.full.min.js +70 -66
- package/dist/handsontable.js +3905 -2642
- package/dist/handsontable.min.css +2 -2
- package/dist/handsontable.min.js +39 -35
- package/editorManager.js +4 -3
- package/editorManager.mjs +4 -3
- package/editors/autocompleteEditor/autocompleteEditor.js +2 -0
- package/editors/autocompleteEditor/autocompleteEditor.mjs +2 -0
- package/editors/handsontableEditor/handsontableEditor.js +1 -0
- package/editors/handsontableEditor/handsontableEditor.mjs +1 -0
- package/editors/textEditor/textEditor.js +4 -0
- package/editors/textEditor/textEditor.mjs +4 -0
- package/helpers/mixed.js +1 -1
- package/helpers/mixed.mjs +1 -1
- package/package.json +1 -1
- package/pluginHooks.d.ts +29 -6
- package/pluginHooks.js +123 -65
- package/pluginHooks.mjs +122 -62
- package/plugins/copyPaste/clipboardData/clipboardData.js +588 -0
- package/plugins/copyPaste/clipboardData/clipboardData.mjs +584 -0
- package/plugins/copyPaste/clipboardData/copyClipboardData.js +69 -0
- package/plugins/copyPaste/clipboardData/copyClipboardData.mjs +65 -0
- package/plugins/copyPaste/clipboardData/index.js +9 -0
- package/plugins/copyPaste/clipboardData/index.mjs +4 -0
- package/plugins/copyPaste/clipboardData/pasteClipboardData.js +81 -0
- package/plugins/copyPaste/clipboardData/pasteClipboardData.mjs +77 -0
- package/plugins/copyPaste/copyPaste.js +51 -129
- package/plugins/copyPaste/copyPaste.mjs +54 -132
- package/plugins/copyPaste/copyableRanges.js +7 -43
- package/plugins/copyPaste/copyableRanges.mjs +7 -42
- package/plugins/copyPaste/pasteEvent.mjs +1 -1
- package/plugins/customBorders/customBorders.js +5 -0
- package/plugins/customBorders/customBorders.mjs +5 -0
- package/plugins/customBorders/utils.js +1 -0
- package/plugins/customBorders/utils.mjs +1 -0
- package/plugins/formulas/formulas.js +2 -0
- package/plugins/formulas/formulas.mjs +2 -0
- package/plugins/formulas/indexSyncer/axisSyncer.js +1 -0
- package/plugins/formulas/indexSyncer/axisSyncer.mjs +1 -0
- package/plugins/manualColumnResize/manualColumnResize.js +1 -0
- package/plugins/manualColumnResize/manualColumnResize.mjs +1 -0
- package/plugins/mergeCells/mergeCells.js +127 -1
- package/plugins/mergeCells/mergeCells.mjs +127 -1
- package/plugins/nestedHeaders/nestedHeaders.js +87 -41
- package/plugins/nestedHeaders/nestedHeaders.mjs +88 -42
- package/plugins/nestedHeaders/stateManager/headersTree.js +1 -0
- package/plugins/nestedHeaders/stateManager/headersTree.mjs +1 -0
- package/plugins/undoRedo/undoRedo.js +2 -0
- package/plugins/undoRedo/undoRedo.mjs +2 -0
- package/renderers/autocompleteRenderer/autocompleteRenderer.js +1 -0
- package/renderers/autocompleteRenderer/autocompleteRenderer.mjs +1 -0
- package/renderers/checkboxRenderer/checkboxRenderer.js +2 -0
- package/renderers/checkboxRenderer/checkboxRenderer.mjs +2 -0
- package/selection/highlight/highlight.js +1 -0
- package/selection/highlight/highlight.mjs +1 -0
- package/selection/index.js +3 -1
- package/selection/index.mjs +2 -2
- package/selection/utils.js +34 -0
- package/selection/utils.mjs +33 -0
- package/settings.d.ts +0 -1
- package/tableView.js +2 -1
- package/tableView.mjs +2 -1
- package/translations/indexMapper.js +1 -2
- package/utils/parseTable.js +538 -84
- package/utils/parseTable.mjs +534 -83
- package/validators/timeValidator/timeValidator.js +1 -0
- package/validators/timeValidator/timeValidator.mjs +1 -0
- package/3rdparty/walkontable/src/calculator/renderAllColumns.js +0 -50
- package/3rdparty/walkontable/src/calculator/renderAllColumns.mjs +0 -46
- package/3rdparty/walkontable/src/calculator/renderAllRows.js +0 -50
- package/3rdparty/walkontable/src/calculator/renderAllRows.mjs +0 -46
- package/3rdparty/walkontable/src/utils/columnStretching.js +0 -219
- package/3rdparty/walkontable/src/utils/columnStretching.mjs +0 -215
- package/plugins/copyPaste/clipboardData.js +0 -18
- package/plugins/copyPaste/clipboardData.mjs +0 -14
package/utils/parseTable.js
CHANGED
@@ -1,11 +1,17 @@
|
|
1
1
|
"use strict";
|
2
2
|
|
3
3
|
exports.__esModule = true;
|
4
|
-
exports.
|
4
|
+
exports.getDataByCoords = getDataByCoords;
|
5
|
+
exports.getDataWithHeadersByConfig = getDataWithHeadersByConfig;
|
6
|
+
exports.getHTMLByCoords = getHTMLByCoords;
|
7
|
+
exports.getHTMLFromConfig = getHTMLFromConfig;
|
5
8
|
exports.htmlToGridSettings = htmlToGridSettings;
|
6
9
|
exports.instanceToHTML = instanceToHTML;
|
7
10
|
require("core-js/modules/es.array.push.js");
|
11
|
+
require("core-js/modules/es.string.replace-all.js");
|
8
12
|
var _mixed = require("./../helpers/mixed");
|
13
|
+
var _object = require("./../helpers/object");
|
14
|
+
var _number = require("../helpers/number");
|
9
15
|
const ESCAPED_HTML_CHARS = {
|
10
16
|
' ': '\x20',
|
11
17
|
'&': '&',
|
@@ -24,102 +30,549 @@ function isHTMLTable(element) {
|
|
24
30
|
return (element && element.nodeName || '') === 'TABLE';
|
25
31
|
}
|
26
32
|
|
33
|
+
/**
|
34
|
+
* Parses empty values to an empty string or leave them untouched otherwise.
|
35
|
+
*
|
36
|
+
* @private
|
37
|
+
* @param {string} cellValue Parsed cell value.
|
38
|
+
* @returns {string}
|
39
|
+
*/
|
40
|
+
function parseEmptyValues(cellValue) {
|
41
|
+
if ((0, _mixed.isEmpty)(cellValue)) {
|
42
|
+
return '';
|
43
|
+
}
|
44
|
+
return cellValue;
|
45
|
+
}
|
46
|
+
|
27
47
|
/**
|
28
48
|
* Converts Handsontable into HTMLTableElement.
|
29
49
|
*
|
30
|
-
* @param {Core}
|
50
|
+
* @param {Core} hotInstance The Handsontable instance.
|
31
51
|
* @returns {string} OuterHTML of the HTMLTableElement.
|
32
52
|
*/
|
33
|
-
function instanceToHTML(
|
34
|
-
const
|
35
|
-
const
|
36
|
-
const
|
37
|
-
|
38
|
-
|
39
|
-
const
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
53
|
+
function instanceToHTML(hotInstance) {
|
54
|
+
const startColumn = hotInstance.hasRowHeaders() ? -1 : 0;
|
55
|
+
const startRow = hotInstance.hasColHeaders() ? -1 : 0;
|
56
|
+
const rows = Array.from({
|
57
|
+
length: hotInstance.countRows() + Math.abs(startRow)
|
58
|
+
}, (_, i) => i + startRow);
|
59
|
+
const columns = Array.from({
|
60
|
+
length: hotInstance.countCols() + Math.abs(startColumn)
|
61
|
+
}, (_, i) => i + startColumn);
|
62
|
+
return getHTMLByCoords(hotInstance, {
|
63
|
+
rows,
|
64
|
+
columns
|
65
|
+
});
|
66
|
+
}
|
67
|
+
|
68
|
+
/**
|
69
|
+
* Converts Handsontable's coordinates into HTMLTableElement.
|
70
|
+
*
|
71
|
+
* @param {Core} hotInstance The Handsontable instance.
|
72
|
+
* @param {object} config Configuration for building HTMLTableElement.
|
73
|
+
* @param {Array<number>} config.rows List of row indexes which should be taken into account when creating the table.
|
74
|
+
* @param {Array<number>} config.columns List of column indexes which should be taken into account when creating the table.
|
75
|
+
* @returns {string} OuterHTML of the HTMLTableElement.
|
76
|
+
*/
|
77
|
+
function getHTMLByCoords(hotInstance, config) {
|
78
|
+
return ['<table>',
|
79
|
+
// Needed for desktop Excel on MacOS while pasting any elements with rowspan/colspan.
|
80
|
+
'<!--StartFragment-->', ...getHeadersHTMLByCoords(hotInstance, config), ...getBodyHTMLByCoords(hotInstance, config),
|
81
|
+
// Needed for desktop Excel on MacOS while pasting any elements with rowspan/colspan.
|
82
|
+
'<!--EndFragment-->', '</table>'].join('');
|
83
|
+
}
|
84
|
+
|
85
|
+
/**
|
86
|
+
* Converts Handsontable's coordinates into list of cell values.
|
87
|
+
*
|
88
|
+
* @param {Core} hotInstance The Handsontable instance.
|
89
|
+
* @param {object} config Configuration for building the cell value list.
|
90
|
+
* @param {Array<number>} config.rows List of row indexes which should be taken into account when creating the
|
91
|
+
* cell value list.
|
92
|
+
* @param {Array<number>} config.columns List of column indexes which should be taken into account when creating the
|
93
|
+
* cell value list.
|
94
|
+
* @returns {Array<Array<string>>} List of displayed cell values.
|
95
|
+
*/
|
96
|
+
function getDataByCoords(hotInstance, config) {
|
97
|
+
return [...getHeadersDataByCoords(hotInstance, config), ...getBodyDataByCoords(hotInstance, config)];
|
98
|
+
}
|
99
|
+
|
100
|
+
/**
|
101
|
+
* Converts config into HTMLTableElement.
|
102
|
+
*
|
103
|
+
* @param {object} config Configuration for building HTMLTableElement.
|
104
|
+
* @param {Array<number>} [config.excludedRows] List of row indexes which should be excluded when creating the table.
|
105
|
+
* @param {Array<number>} [config.excludedColumns] List of column indexes which should be excluded when creating the table.
|
106
|
+
* @param {Array<Array<string>>} [config.data] List of cell data.
|
107
|
+
* @param {Array<object>} [config.mergeCells] List of merged cells.
|
108
|
+
* @param {Array<Array<string|object>>} [config.nestedHeaders] List of headers and corresponding information about some
|
109
|
+
* nested elements.
|
110
|
+
* @param {Array<string>} [config.colHeaders] List of first level header values.
|
111
|
+
* @returns {string} OuterHTML of the HTMLTableElement.
|
112
|
+
*/
|
113
|
+
function getHTMLFromConfig(config) {
|
114
|
+
return ['<table>',
|
115
|
+
// Needed for desktop Excel on MacOS while pasting any elements with rowspan/colspan.
|
116
|
+
'<!--StartFragment-->', ...getHeadersHTMLByConfig(config), ...getBodyHTMLByConfig(config),
|
117
|
+
// Needed for desktop Excel on MacOS while pasting any elements with rowspan/colspan.
|
118
|
+
'<!--EndFragment-->', '</table>'].join('');
|
119
|
+
}
|
120
|
+
|
121
|
+
/**
|
122
|
+
* Get list of filtered nested headers.
|
123
|
+
*
|
124
|
+
* @param {Array<Array<string|object>>} nestedHeaders List of nested headers which will be filtered.
|
125
|
+
* @param {Array<number>} excludedHeaders List of headers which should be excluded when creating the HTMLTableElement.tHead.
|
126
|
+
* @param {Array<number>} excludedColumns List of column indexes which should be excluded when creating the HTMLTableElement.tHead.
|
127
|
+
* @returns {*}
|
128
|
+
*/
|
129
|
+
function getFilteredNestedHeaders(nestedHeaders, excludedHeaders, excludedColumns) {
|
130
|
+
return nestedHeaders.reduce((listOfHeaders, headerValues, rowIndex) => {
|
131
|
+
if (excludedHeaders.includes(rowIndex - nestedHeaders.length)) {
|
132
|
+
return listOfHeaders;
|
133
|
+
}
|
134
|
+
const filteredNestedHeader = headerValues.filter((columnData, columnIndex) => excludedColumns.includes(columnIndex) === false);
|
135
|
+
if (filteredNestedHeader.length > 0) {
|
136
|
+
return listOfHeaders.concat([filteredNestedHeader]);
|
137
|
+
}
|
138
|
+
return listOfHeaders;
|
139
|
+
}, []);
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Get HTML for nested headers.
|
144
|
+
*
|
145
|
+
* @param {Array<Array<string|object>>} nestedHeaders List of nested headers which will be filtered.
|
146
|
+
* @param {Array<number>} excludedHeaders List of headers which should be excluded when creating the HTMLTableElement.tHead.
|
147
|
+
* @param {Array<number>} excludedColumns List of column indexes which should be excluded when creating the HTMLTableElement.tHead.
|
148
|
+
* @returns {Array<string>}
|
149
|
+
*/
|
150
|
+
function getNestedHeadersHTML(nestedHeaders, excludedHeaders, excludedColumns) {
|
151
|
+
const headersHTML = [];
|
152
|
+
getFilteredNestedHeaders(nestedHeaders, excludedHeaders, excludedColumns).forEach(listOfHeaders => {
|
153
|
+
const rowHTML = ['<tr>'];
|
154
|
+
for (let i = 0; i < listOfHeaders.length; i += 1) {
|
155
|
+
const header = listOfHeaders[i];
|
156
|
+
let headerValue = header;
|
157
|
+
let colspanAttribute = '';
|
158
|
+
if ((0, _object.isObject)(header)) {
|
159
|
+
const {
|
160
|
+
colspan,
|
161
|
+
label
|
162
|
+
} = header;
|
163
|
+
headerValue = label;
|
164
|
+
colspanAttribute = ` colspan=${colspan}`;
|
165
|
+
}
|
166
|
+
rowHTML.push(`<th${colspanAttribute}>${encodeHTMLEntities(parseEmptyValues(headerValue))}</th>`);
|
167
|
+
}
|
168
|
+
rowHTML.push('</tr>');
|
169
|
+
headersHTML.push(...rowHTML);
|
170
|
+
});
|
171
|
+
return headersHTML;
|
172
|
+
}
|
173
|
+
|
174
|
+
/**
|
175
|
+
* Get HTML for first level header.
|
176
|
+
*
|
177
|
+
* @param {Array<string>} columnHeaders List of header values which will be filtered.
|
178
|
+
* @param {Array<number>} excludedHeaders List of headers which should be excluded when creating the HTMLTableElement.tHead.
|
179
|
+
* @param {Array<number>} excludedColumns List of column indexes which should be excluded when creating the HTMLTableElement.tHead.
|
180
|
+
* @returns {*[]}
|
181
|
+
*/
|
182
|
+
function getSimpleHeadersHTML(columnHeaders, excludedHeaders, excludedColumns) {
|
183
|
+
if (excludedHeaders.includes(-1)) {
|
184
|
+
return [];
|
185
|
+
}
|
186
|
+
const filteredColumnHeaders = columnHeaders.filter((columnHeaderValue, columnIndex) => excludedColumns.includes(columnIndex) === false);
|
187
|
+
if (filteredColumnHeaders.length === 0) {
|
188
|
+
return [];
|
189
|
+
}
|
190
|
+
return ['<tr>', ...filteredColumnHeaders.map(columnHeader => `<th>${encodeHTMLEntities(parseEmptyValues(columnHeader))}</th>`), '</tr>'];
|
191
|
+
}
|
192
|
+
|
193
|
+
/**
|
194
|
+
* Get list of cells filtered by list of excluded rows and columns.
|
195
|
+
*
|
196
|
+
* @private
|
197
|
+
* @param {Array<Array<string>>} data List of cells values which will be filtered.
|
198
|
+
* @param {Array<number>} excludedRows List of row indexes which should be excluded when creating the HTMLTableElement.tHead.
|
199
|
+
* @param {Array<number>} excludedColumns List of column indexes which should be excluded when creating the HTMLTableElement.tHead.
|
200
|
+
* @returns {Array<string>} List of cell values.
|
201
|
+
*/
|
202
|
+
function getFilteredCells(data, excludedRows, excludedColumns) {
|
203
|
+
if (Array.isArray(data) === false) {
|
204
|
+
return [];
|
205
|
+
}
|
206
|
+
return data.reduce((listOfCells, rowData, rowIndex) => {
|
207
|
+
if (excludedRows.includes(rowIndex)) {
|
208
|
+
return listOfCells;
|
209
|
+
}
|
210
|
+
const filteredRowData = rowData.filter((cellData, columnIndex) => excludedColumns.includes(columnIndex) === false);
|
211
|
+
if (filteredRowData.length > 0) {
|
212
|
+
return listOfCells.concat([filteredRowData]);
|
213
|
+
}
|
214
|
+
return listOfCells;
|
215
|
+
}, []);
|
216
|
+
}
|
217
|
+
|
218
|
+
/**
|
219
|
+
* Prepare information about merged areas to reduce complexity of calculations.
|
220
|
+
*
|
221
|
+
* @private
|
222
|
+
* @param {Array<object>} mergedCellsConfig List of merged cells.
|
223
|
+
* @returns {{mergedCellsMap: Map<any, any>, mergedArea: Set<any>}}
|
224
|
+
*/
|
225
|
+
function getMergedCellsInformation(mergedCellsConfig) {
|
226
|
+
const mergedCellsMap = new Map();
|
227
|
+
const mergedArea = new Set();
|
228
|
+
let mergedRows = 1;
|
229
|
+
let mergedColumns = 1;
|
230
|
+
mergedCellsConfig === null || mergedCellsConfig === void 0 || mergedCellsConfig.forEach(mergeArea => {
|
231
|
+
const {
|
232
|
+
row,
|
233
|
+
col,
|
234
|
+
rowspan,
|
235
|
+
colspan
|
236
|
+
} = mergeArea;
|
237
|
+
mergedCellsMap.set(`${row}x${col}`, {
|
238
|
+
rowspan,
|
239
|
+
colspan
|
240
|
+
});
|
241
|
+
if (Number.isInteger(rowspan)) {
|
242
|
+
mergedRows = rowspan;
|
243
|
+
}
|
244
|
+
if (Number.isInteger(colspan)) {
|
245
|
+
mergedColumns = colspan;
|
246
|
+
}
|
247
|
+
(0, _number.rangeEach)(row, row + mergedRows - 1, rowIndex => {
|
248
|
+
(0, _number.rangeEach)(col, col + mergedColumns - 1, columnIndex => {
|
249
|
+
// Other than start point.
|
250
|
+
if (rowIndex !== row || columnIndex !== col) {
|
251
|
+
mergedArea.add(`${rowIndex}x${columnIndex}`);
|
252
|
+
}
|
253
|
+
});
|
254
|
+
});
|
255
|
+
});
|
256
|
+
return {
|
257
|
+
mergedCellsMap,
|
258
|
+
mergedArea
|
259
|
+
};
|
260
|
+
}
|
261
|
+
|
262
|
+
/**
|
263
|
+
* Converts config with information about cells into HTMLTableElement.tBodies.
|
264
|
+
*
|
265
|
+
* @private
|
266
|
+
* @param {object} config Configuration for building HTMLTableElement.tBodies.
|
267
|
+
* @param {Array<Array<string>>} config.data List of cell data.
|
268
|
+
* @param {Array<number>} [config.excludedRows] List of row indexes which should be excluded when creating the HTMLTableElement.tBodies.
|
269
|
+
* @param {Array<number>} [config.excludedColumns] List of column indexes which should be excluded when creating the HTMLTableElement.tBodies.
|
270
|
+
* @param {Array<object>} [config.mergeCells] List of merged cells.
|
271
|
+
* @returns {Array<string>} List of HTMLElements stored as strings.
|
272
|
+
*/
|
273
|
+
function getBodyHTMLByConfig(config) {
|
274
|
+
const excludedColumns = config.excludedColumns || [];
|
275
|
+
const excludedRows = config.excludedRows || [];
|
276
|
+
const {
|
277
|
+
data,
|
278
|
+
mergeCells
|
279
|
+
} = config;
|
280
|
+
const ignoredCellRows = excludedRows.filter(rowIndex => rowIndex >= 0);
|
281
|
+
const filteredData = getFilteredCells(data, ignoredCellRows, excludedColumns);
|
282
|
+
const cells = [];
|
283
|
+
if (filteredData.length === 0) {
|
284
|
+
return [];
|
285
|
+
}
|
286
|
+
const {
|
287
|
+
mergedCellsMap,
|
288
|
+
mergedArea
|
289
|
+
} = getMergedCellsInformation(mergeCells);
|
290
|
+
filteredData.forEach((rowData, rowIndex) => {
|
291
|
+
const rowHTML = ['<tr>'];
|
292
|
+
rowData.forEach((cellData, columnIndex) => {
|
293
|
+
const attrs = [];
|
294
|
+
const checkedMergeCoordinate = `${rowIndex}x${columnIndex}`;
|
295
|
+
const mergeParent = mergedCellsMap.get(checkedMergeCoordinate);
|
296
|
+
if (mergeParent !== undefined) {
|
57
297
|
const {
|
58
|
-
hidden,
|
59
298
|
rowspan,
|
60
299
|
colspan
|
61
|
-
} =
|
62
|
-
if (
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
}
|
67
|
-
if (colspan) {
|
68
|
-
attrs.push(`colspan="${colspan}"`);
|
69
|
-
}
|
70
|
-
if ((0, _mixed.isEmpty)(cellData)) {
|
71
|
-
cell = `<td ${attrs.join(' ')}></td>`;
|
72
|
-
} else {
|
73
|
-
const value = cellData.toString().replace('<', '<').replace('>', '>').replace(/(<br(\s*|\/)>(\r\n|\n)?|\r\n|\n)/g, '<br>\r\n').replace(/\x20/gi, ' ').replace(/\t/gi, '	');
|
74
|
-
cell = `<td ${attrs.join(' ')}>${value}</td>`;
|
75
|
-
}
|
300
|
+
} = mergeParent;
|
301
|
+
if (Number.isInteger(rowspan) && rowspan > 1) {
|
302
|
+
attrs.push(` rowspan="${rowspan}"`);
|
303
|
+
}
|
304
|
+
if (Number.isInteger(colspan) && colspan > 1) {
|
305
|
+
attrs.push(` colspan="${colspan}"`);
|
76
306
|
}
|
307
|
+
} else if (mergedArea.has(checkedMergeCoordinate)) {
|
308
|
+
return;
|
77
309
|
}
|
78
|
-
|
79
|
-
}
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
310
|
+
rowHTML.push(`<td${attrs.join('')}>${encodeHTMLEntities(parseEmptyValues(cellData))}</td>`);
|
311
|
+
});
|
312
|
+
rowHTML.push('</tr>');
|
313
|
+
cells.push(...rowHTML);
|
314
|
+
});
|
315
|
+
return ['<tbody>', ...cells, '</tbody>'];
|
316
|
+
}
|
317
|
+
|
318
|
+
/**
|
319
|
+
* Converts config with information about headers into HTMLTableElement.tHead.
|
320
|
+
*
|
321
|
+
* @private
|
322
|
+
* @param {object} config Configuration for building HTMLTableElement.tHead.
|
323
|
+
* @param {Array<Array<string|object>>} [config.nestedHeaders] List of headers and corresponding information about some
|
324
|
+
* nested elements.
|
325
|
+
* @param {Array<string>} [config.colHeaders] List of first level header values.
|
326
|
+
* @param {Array<number>} [config.excludedRows] List of row indexes which should be excluded when creating the HTMLTableElement.tHead.
|
327
|
+
* @param {Array<number>} [config.excludedColumns] List of column indexes which should be excluded when creating the HTMLTableElement.tHead.
|
328
|
+
* @returns {Array<string>} List of HTMLElements stored as strings.
|
329
|
+
*/
|
330
|
+
function getHeadersHTMLByConfig(config) {
|
331
|
+
const headersHTML = [];
|
332
|
+
const excludedColumns = Array.isArray(config === null || config === void 0 ? void 0 : config.excludedColumns) ? config.excludedColumns : [];
|
333
|
+
const excludedRows = Array.isArray(config === null || config === void 0 ? void 0 : config.excludedRows) ? config.excludedRows : [];
|
334
|
+
const {
|
335
|
+
nestedHeaders,
|
336
|
+
colHeaders
|
337
|
+
} = config;
|
338
|
+
const excludedHeaders = excludedRows.filter(rowIndex => rowIndex < 0);
|
339
|
+
if (Array.isArray(nestedHeaders)) {
|
340
|
+
headersHTML.push(...getNestedHeadersHTML(nestedHeaders, excludedHeaders, excludedColumns));
|
341
|
+
} else if (Array.isArray(colHeaders)) {
|
342
|
+
headersHTML.push(...getSimpleHeadersHTML(colHeaders, excludedHeaders, excludedColumns));
|
86
343
|
}
|
87
|
-
|
88
|
-
|
344
|
+
if (headersHTML.length > 0) {
|
345
|
+
return ['<thead>', ...headersHTML, '</thead>'];
|
346
|
+
}
|
347
|
+
return [];
|
89
348
|
}
|
90
349
|
|
91
350
|
/**
|
92
|
-
* Converts
|
351
|
+
* Converts config with information about cells and headers into list of values.
|
93
352
|
*
|
94
|
-
* @param {
|
95
|
-
* @
|
353
|
+
* @param {object} config Configuration for building list of values.
|
354
|
+
* @param {Array<number>} [config.excludedRows] List of row indexes which should be excluded when creating the value list.
|
355
|
+
* @param {Array<number>} [config.excludedColumns] List of column indexes which should be excluded when creating the value list.
|
356
|
+
* @param {Array<Array<string|object>>} [config.nestedHeaders] List of headers and information about some nested elements.
|
357
|
+
* @param {Array<string>} [config.colHeaders] List of first level header values.
|
358
|
+
* @returns {string[][]} List of values.
|
96
359
|
*/
|
97
|
-
|
98
|
-
|
99
|
-
const
|
100
|
-
const
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
360
|
+
function getDataWithHeadersByConfig(config) {
|
361
|
+
const dataWithHeaders = [];
|
362
|
+
const excludedColumns = Array.isArray(config === null || config === void 0 ? void 0 : config.excludedColumns) ? config.excludedColumns : [];
|
363
|
+
const excludedRows = Array.isArray(config === null || config === void 0 ? void 0 : config.excludedRows) ? config.excludedRows : [];
|
364
|
+
const {
|
365
|
+
data,
|
366
|
+
nestedHeaders,
|
367
|
+
colHeaders
|
368
|
+
} = config;
|
369
|
+
const excludedHeaders = excludedRows.filter(rowIndex => rowIndex < 0);
|
370
|
+
if (Array.isArray(nestedHeaders)) {
|
371
|
+
dataWithHeaders.push(...getFilteredNestedHeaders(nestedHeaders, excludedHeaders, excludedColumns).map(listOfHeaders => {
|
372
|
+
return listOfHeaders.reduce((headers, header) => {
|
373
|
+
if ((0, _object.isObject)(header)) {
|
374
|
+
headers.push(header.label, ...new Array(header.colspan - 1).fill(''));
|
375
|
+
} else {
|
376
|
+
headers.push(header);
|
377
|
+
}
|
378
|
+
return headers;
|
379
|
+
}, []);
|
380
|
+
}));
|
381
|
+
} else if (Array.isArray(colHeaders)) {
|
382
|
+
dataWithHeaders.push([...colHeaders.filter((columnHeaderData, columnIndex) => excludedColumns.includes(columnIndex) === false)]);
|
383
|
+
}
|
384
|
+
dataWithHeaders.push(...getFilteredCells(data, excludedRows.filter(rowIndex => rowIndex >= 0), excludedColumns.filter(columnIndex => columnIndex >= 0)));
|
385
|
+
return dataWithHeaders;
|
386
|
+
}
|
387
|
+
|
388
|
+
/**
|
389
|
+
* Encode text to HTML.
|
390
|
+
*
|
391
|
+
* @param {string} text Text to prepare.
|
392
|
+
* @returns {string}
|
393
|
+
*/
|
394
|
+
function encodeHTMLEntities(text) {
|
395
|
+
return `${text}`.replace(/&/g, '&').replace('<', '<').replace('>', '>').replace(/(<br(\s*|\/)>(\r\n|\n)?|\r\n|\n)/g, '<br>\r\n').replace(/\x20{2,}/gi, substring => {
|
396
|
+
// The way how Excel serializes data with at least two spaces.
|
397
|
+
return `<span style="mso-spacerun: yes">${' '.repeat(substring.length - 1)} </span>`;
|
398
|
+
}).replace(/\t/gi, '	');
|
399
|
+
}
|
400
|
+
|
401
|
+
/**
|
402
|
+
* Decode HTML to simple text.
|
403
|
+
*
|
404
|
+
* @param {string} html HTML for handling.
|
405
|
+
* @returns {string}
|
406
|
+
*/
|
407
|
+
function decodeHTMLEntities(html) {
|
408
|
+
return html.replace(regEscapedChars, match => ESCAPED_HTML_CHARS[match])
|
409
|
+
// The way how Excel serializes data with at least two spaces.
|
410
|
+
.replace(/<span style="mso-spacerun: yes">(.+?)<\/span>/, '$1').replaceAll(' ', ' ');
|
411
|
+
}
|
412
|
+
|
413
|
+
/**
|
414
|
+
* Converts Handsontable's header coordinates into HTMLTableElement.tHead.
|
415
|
+
*
|
416
|
+
* @param {Core} hotInstance The Handsontable instance.
|
417
|
+
* @param {object} config Configuration for building HTMLTableElement.tHead.
|
418
|
+
* @param {Array<number>} config.rows List of row indexes which should be taken into account when creating
|
419
|
+
* the HTMLTableElement.tHead.
|
420
|
+
* @param {Array<number>} config.columns List of column indexes which should be taken into account when creating
|
421
|
+
* the HTMLTableElement.tHead.
|
422
|
+
* @returns {Array<string>} List of HTMLElements stored as strings.
|
423
|
+
*/
|
424
|
+
function getHeadersHTMLByCoords(hotInstance, config) {
|
425
|
+
const {
|
426
|
+
rows,
|
427
|
+
columns
|
428
|
+
} = config;
|
429
|
+
const headers = rows.filter(rowIndex => rowIndex < 0);
|
430
|
+
const headersHTML = [];
|
431
|
+
if (headers.length === 0 || columns.length === 0) {
|
432
|
+
return [];
|
433
|
+
}
|
434
|
+
headers.forEach(rowIndex => {
|
435
|
+
const rowHTML = ['<tr>'];
|
436
|
+
for (let i = 0; i < columns.length; i += 1) {
|
437
|
+
const columnIndex = columns[i];
|
438
|
+
const headerCell = hotInstance.getCell(rowIndex, columnIndex);
|
439
|
+
const colspan = headerCell === null || headerCell === void 0 ? void 0 : headerCell.getAttribute('colspan');
|
440
|
+
let colspanAttribute = '';
|
441
|
+
if (colspan) {
|
442
|
+
const parsedColspan = parseInt(colspan, 10);
|
443
|
+
const colspanReduced = Math.min(parsedColspan, columns.length - i);
|
444
|
+
colspanAttribute = ` colspan=${colspanReduced}`;
|
445
|
+
i += colspanReduced - 1;
|
446
|
+
}
|
447
|
+
rowHTML.push(`<th${colspanAttribute}>${encodeHTMLEntities(parseEmptyValues(hotInstance.getColHeader(columnIndex, rowIndex)))}</th>`);
|
115
448
|
}
|
116
|
-
|
117
|
-
|
118
|
-
|
449
|
+
rowHTML.push('</tr>');
|
450
|
+
headersHTML.push(...rowHTML);
|
451
|
+
});
|
452
|
+
return ['<thead>', ...headersHTML, '</thead>'];
|
453
|
+
}
|
454
|
+
|
455
|
+
/**
|
456
|
+
* Converts Handsontable's coordinates into list of values for cells being headers.
|
457
|
+
*
|
458
|
+
* @param {Core} hotInstance The Handsontable instance.
|
459
|
+
* @param {object} config Configuration for building the cell value list.
|
460
|
+
* @param {Array<number>} config.rows List of row indexes which should be taken into account when creating the
|
461
|
+
* cell value list.
|
462
|
+
* @param {Array<number>} config.columns List of column indexes which should be taken into account when creating the
|
463
|
+
* cell value list.
|
464
|
+
* @returns {Array[]} List of displayed cell values.
|
465
|
+
*/
|
466
|
+
function getHeadersDataByCoords(hotInstance, config) {
|
467
|
+
const headersData = [];
|
468
|
+
const {
|
469
|
+
columns,
|
470
|
+
rows
|
471
|
+
} = config;
|
472
|
+
const headers = rows.filter(rowIndex => rowIndex < 0);
|
473
|
+
headers.forEach(rowIndex => {
|
474
|
+
const rowData = [];
|
475
|
+
for (let i = 0; i < columns.length; i += 1) {
|
476
|
+
const columnIndex = columns[i];
|
477
|
+
const headerCell = hotInstance.getCell(rowIndex, columnIndex);
|
478
|
+
const colspan = headerCell === null || headerCell === void 0 ? void 0 : headerCell.getAttribute('colspan');
|
479
|
+
rowData.push(hotInstance.getColHeader(columnIndex, rowIndex));
|
480
|
+
if (colspan) {
|
481
|
+
const parsedColspan = parseInt(colspan, 10);
|
482
|
+
const colspanReduced = Math.min(parsedColspan, columns.length - i);
|
483
|
+
rowData.push(...new Array(colspanReduced - 1).fill(''));
|
484
|
+
i += colspanReduced - 1;
|
485
|
+
}
|
119
486
|
}
|
487
|
+
headersData.push(rowData);
|
488
|
+
});
|
489
|
+
return headersData;
|
490
|
+
}
|
491
|
+
|
492
|
+
/**
|
493
|
+
* Converts Handsontable's header coordinates into HTMLTableElement.tBodies.
|
494
|
+
*
|
495
|
+
* @param {Core} hotInstance The Handsontable instance.
|
496
|
+
* @param {object} config Configuration for building HTMLTableElement.
|
497
|
+
* @param {Array<number>} config.rows List of row indexes which should be taken into account when creating the table.
|
498
|
+
* @param {Array<number>} config.columns List of column indexes which should be taken into account when creating the table.
|
499
|
+
* @returns {Array<string>} List of HTMLElements stored as strings.
|
500
|
+
*/
|
501
|
+
function getBodyHTMLByCoords(hotInstance, config) {
|
502
|
+
const {
|
503
|
+
columns,
|
504
|
+
rows
|
505
|
+
} = config;
|
506
|
+
const bodyRows = rows.filter(rowIndex => rowIndex >= 0);
|
507
|
+
const cells = [];
|
508
|
+
if (bodyRows.length === 0 || columns.length === 0) {
|
509
|
+
return [];
|
120
510
|
}
|
121
|
-
|
122
|
-
|
511
|
+
bodyRows.forEach((rowIndex, nthRow) => {
|
512
|
+
const rowHTML = ['<tr>'];
|
513
|
+
columns.forEach((columnIndex, nthColumn) => {
|
514
|
+
if (columnIndex < 0) {
|
515
|
+
rowHTML.push(`<th>${encodeHTMLEntities(parseEmptyValues(hotInstance.getRowHeader(rowIndex)))}</th>`);
|
516
|
+
return;
|
517
|
+
}
|
518
|
+
const cellValue = hotInstance.getCopyableData(rowIndex, columnIndex);
|
519
|
+
const cellValueParsed = encodeHTMLEntities(parseEmptyValues(cellValue));
|
520
|
+
const {
|
521
|
+
hidden,
|
522
|
+
rowspan,
|
523
|
+
colspan
|
524
|
+
} = hotInstance.getCellMeta(rowIndex, columnIndex);
|
525
|
+
if (!hidden) {
|
526
|
+
const attrs = [];
|
527
|
+
if (rowspan) {
|
528
|
+
const recalculatedRowSpan = Math.min(rowspan, bodyRows.slice(nthRow).length);
|
529
|
+
if (recalculatedRowSpan > 1) {
|
530
|
+
attrs.push(` rowspan="${recalculatedRowSpan}"`);
|
531
|
+
}
|
532
|
+
}
|
533
|
+
if (colspan) {
|
534
|
+
const recalculatedColumnSpan = Math.min(colspan, columns.slice(nthColumn).length);
|
535
|
+
if (recalculatedColumnSpan > 1) {
|
536
|
+
attrs.push(` colspan="${recalculatedColumnSpan}"`);
|
537
|
+
}
|
538
|
+
}
|
539
|
+
rowHTML.push(`<td${attrs.join('')}>${cellValueParsed}</td>`);
|
540
|
+
}
|
541
|
+
});
|
542
|
+
rowHTML.push('</tr>');
|
543
|
+
cells.push(...rowHTML);
|
544
|
+
});
|
545
|
+
return ['<tbody>', ...cells, '</tbody>'];
|
546
|
+
}
|
547
|
+
|
548
|
+
/**
|
549
|
+
* Converts Handsontable's coordinates into list of values for cells not being headers.
|
550
|
+
*
|
551
|
+
* @param {Core} hotInstance The Handsontable instance.
|
552
|
+
* @param {object} config Configuration for building the cell value list.
|
553
|
+
* @param {Array<number>} config.rows List of row indexes which should be taken into account when creating the
|
554
|
+
* cell value list.
|
555
|
+
* @param {Array<number>} config.columns List of column indexes which should be taken into account when creating the
|
556
|
+
* cell value list.
|
557
|
+
* @returns {Array[]} List of displayed cell values.
|
558
|
+
*/
|
559
|
+
function getBodyDataByCoords(hotInstance, config) {
|
560
|
+
const cells = [];
|
561
|
+
const {
|
562
|
+
columns,
|
563
|
+
rows
|
564
|
+
} = config;
|
565
|
+
const bodyRows = rows.filter(rowIndex => rowIndex >= 0);
|
566
|
+
bodyRows.forEach(rowIndex => {
|
567
|
+
const rowData = [];
|
568
|
+
columns.forEach(columnIndex => {
|
569
|
+
const cellValue = hotInstance.getCopyableData(rowIndex, columnIndex);
|
570
|
+
const cellValueParsed = (0, _mixed.isEmpty)(cellValue) ? '' : cellValue;
|
571
|
+
rowData.push(cellValueParsed);
|
572
|
+
});
|
573
|
+
cells.push(rowData);
|
574
|
+
});
|
575
|
+
return cells;
|
123
576
|
}
|
124
577
|
|
125
578
|
/**
|
@@ -167,18 +620,19 @@ function htmlToGridSettings(element) {
|
|
167
620
|
}
|
168
621
|
return !isDataRow;
|
169
622
|
});
|
623
|
+
const isAnyNested = thRows.find(tr => tr.querySelector('th[colspan]') !== null) !== undefined;
|
170
624
|
thRowsLen = thRows.length;
|
171
625
|
hasColHeaders = thRowsLen > 0;
|
172
|
-
if (thRowsLen > 1) {
|
626
|
+
if (thRowsLen > 1 || isAnyNested) {
|
173
627
|
settingsObj.nestedHeaders = Array.from(thRows).reduce((rows, row) => {
|
174
628
|
const headersRow = Array.from(row.cells).reduce((headers, header, currentIndex) => {
|
175
629
|
if (hasRowHeaders && currentIndex === 0) {
|
176
630
|
return headers;
|
177
631
|
}
|
178
632
|
const {
|
179
|
-
colSpan: colspan
|
180
|
-
innerHTML
|
633
|
+
colSpan: colspan
|
181
634
|
} = header;
|
635
|
+
const innerHTML = decodeHTMLEntities(header.innerHTML);
|
182
636
|
const nextHeader = colspan > 1 ? {
|
183
637
|
label: innerHTML,
|
184
638
|
colspan
|
@@ -194,7 +648,7 @@ function htmlToGridSettings(element) {
|
|
194
648
|
if (hasRowHeaders && index === 0) {
|
195
649
|
return headers;
|
196
650
|
}
|
197
|
-
headers.push(header.innerHTML);
|
651
|
+
headers.push(decodeHTMLEntities(header.innerHTML));
|
198
652
|
return headers;
|
199
653
|
}, []);
|
200
654
|
}
|
@@ -228,7 +682,7 @@ function htmlToGridSettings(element) {
|
|
228
682
|
rowSpan: rowspan,
|
229
683
|
colSpan: colspan
|
230
684
|
} = cell;
|
231
|
-
const col = dataArr[row].findIndex(value => value ===
|
685
|
+
const col = dataArr[row].findIndex(value => value === void 0);
|
232
686
|
if (nodeName === 'TD') {
|
233
687
|
if (rowspan > 1 || colspan > 1) {
|
234
688
|
for (let rstart = row; rstart < row + rowspan; rstart++) {
|
@@ -255,7 +709,7 @@ function htmlToGridSettings(element) {
|
|
255
709
|
} else {
|
256
710
|
cellValue = innerHTML.replace(/<br(\s*|\/)>[\r\n]?/gim, '\r\n');
|
257
711
|
}
|
258
|
-
dataArr[row][col] = cellValue
|
712
|
+
dataArr[row][col] = decodeHTMLEntities(cellValue);
|
259
713
|
} else {
|
260
714
|
rowHeaders.push(innerHTML);
|
261
715
|
}
|