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