@sapui5/sap.ui.export 1.141.1 → 1.142.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.
- package/README.md +2 -2
- package/package.json +2 -2
- package/src/sap/ui/export/.library +1 -1
- package/src/sap/ui/export/CommaSeparatedValues.js +188 -0
- package/src/sap/ui/export/ExportBase.js +1 -1
- package/src/sap/ui/export/ExportHandler.js +1 -1
- package/src/sap/ui/export/ExportUtils.js +2 -2
- package/src/sap/ui/export/PortableDocument.js +1 -1
- package/src/sap/ui/export/Spreadsheet.js +2 -2
- package/src/sap/ui/export/SpreadsheetExport.js +59 -48
- package/src/sap/ui/export/js/CSVBuilder.js +201 -0
- package/src/sap/ui/export/js/SpreadsheetWorker.js +4 -0
- package/src/sap/ui/export/js/XLSXBuilder.js +1 -1
- package/src/sap/ui/export/js/XLSXBuilder.js.map +1 -1
- package/src/sap/ui/export/library.js +5 -5
- package/src/sap/ui/export/provider/DataProviderBase.js +1 -1
- package/src/sap/ui/export/util/Filter.js +1 -1
package/README.md
CHANGED
|
@@ -5,8 +5,8 @@ Runtime resources of the [SAPUI5](https://ui5.sap.com) Library **sap.ui.export**
|
|
|
5
5
|
|
|
6
6
|
## Usage
|
|
7
7
|
Refrain from installing this package using npm, Yarn or similar package managers.
|
|
8
|
-
It is meant to be consumed using the [UI5
|
|
9
|
-
For details please refer to our documentation on [Consuming SAPUI5 Libraries](https://
|
|
8
|
+
It is meant to be consumed using the [UI5 CLI](https://ui5.github.io/cli/).
|
|
9
|
+
For details please refer to our documentation on [Consuming SAPUI5 Libraries](https://ui5.github.io/cli/pages/SAPUI5/).
|
|
10
10
|
|
|
11
11
|
## License
|
|
12
12
|
This package is provided under the terms of the [SAP Developer License Agreement](https://tools.hana.ondemand.com/developer-license-3_2.txt).
|
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sapui5/sap.ui.export",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.142.0",
|
|
4
4
|
"description": "SAPUI5 Library sap.ui.export",
|
|
5
|
-
"homepage": "https://
|
|
5
|
+
"homepage": "https://ui5.github.io/cli/pages/SAPUI5/",
|
|
6
6
|
"author": "SAP SE (https://www.sap.com)",
|
|
7
7
|
"license": "SEE LICENSE IN LICENSE.txt",
|
|
8
8
|
"keywords": [
|
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SAPUI5
|
|
3
|
+
* (c) Copyright 2025 SAP SE. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
sap.ui.define([
|
|
7
|
+
"./library",
|
|
8
|
+
"sap/base/Log",
|
|
9
|
+
"sap/ui/export/js/CSVBuilder",
|
|
10
|
+
"sap/ui/export/ExportBase",
|
|
11
|
+
"sap/ui/export/ExportUtils"
|
|
12
|
+
], function(library, Log, CSVBuilder, ExportBase, ExportUtils) {
|
|
13
|
+
"use strict";
|
|
14
|
+
|
|
15
|
+
const FileType = library.FileType;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Provides functionality to export data in CSV format.
|
|
19
|
+
*
|
|
20
|
+
* The <code>CommaSeparatedValues</code> class extends the <code>ExportBase</code> class and provides the functionality to create CSV files.
|
|
21
|
+
* It supports appending data, validating data, escaping content, and building the final CSV file. Additionally, it provides methods to
|
|
22
|
+
* process data sources, apply default export settings, and manage the export process.
|
|
23
|
+
*
|
|
24
|
+
* There are the following key features:
|
|
25
|
+
* <ul>
|
|
26
|
+
* <li>Supports JSON arrays and ClientListBindings as data sources.</li>
|
|
27
|
+
* <li>Escapes special characters and prevents CSV injection.</li>
|
|
28
|
+
* <li>Adds a UTF-8 Byte Order Mark (BOM) for compatibility with spreadsheet software.</li>
|
|
29
|
+
* </ul>
|
|
30
|
+
*
|
|
31
|
+
* Example Usage:
|
|
32
|
+
* ```javascript
|
|
33
|
+
* const oCSV = new sap.ui.export.CommaSeparatedValues(mSettings);
|
|
34
|
+
* oCSV.build();
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* @class sap.ui.export.CommaSeparatedValues
|
|
38
|
+
* @extends sap.ui.export.ExportBase
|
|
39
|
+
* @alias sap.ui.export.CommaSeparatedValues
|
|
40
|
+
* @public
|
|
41
|
+
* @since 1.142
|
|
42
|
+
*/
|
|
43
|
+
const CSV = ExportBase.extend("sap.ui.export.CommaSeparatedValues", {});
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Marks the current export process as cancelled which prevents the file from being saved.
|
|
47
|
+
*
|
|
48
|
+
* @returns {this} Reference to <code>this</code> in order to allow method chaining
|
|
49
|
+
*
|
|
50
|
+
* @public
|
|
51
|
+
*/
|
|
52
|
+
CSV.prototype.cancel = function() {
|
|
53
|
+
if (!this._bIsCancelled) {
|
|
54
|
+
this._bIsCancelled = true;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return this;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Sets the data source configuration that is used for exporting the data.
|
|
62
|
+
*
|
|
63
|
+
* <ul>
|
|
64
|
+
* <li>If the passed parameter is null, the call is ignored.</li>
|
|
65
|
+
* <li>Supports JSON arrays as data sources.</li>
|
|
66
|
+
* <li>Logs an error for unsupported data source types.</li>
|
|
67
|
+
* </ul>
|
|
68
|
+
*
|
|
69
|
+
* @param {object|sap.ui.model.ListBinding|sap.ui.model.TreeBinding} oDataSource Possible types are a data
|
|
70
|
+
* source configuration, a <code>sap.ui.model.ListBinding</code> or <code>sap.ui.model.TreeBinding</code>
|
|
71
|
+
* @returns {object|null} Valid <code>dataSource</code> object or null in case the <code>dataSource</code> configuration is not supported
|
|
72
|
+
*
|
|
73
|
+
* @private
|
|
74
|
+
*/
|
|
75
|
+
CSV.prototype.processDataSource = function(oDataSource) {
|
|
76
|
+
const sDataSourceType = typeof oDataSource;
|
|
77
|
+
const mDataSource = {data: [], type: "array"};
|
|
78
|
+
|
|
79
|
+
if (!oDataSource) {
|
|
80
|
+
return null;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (sDataSourceType !== "object") {
|
|
84
|
+
Log.error("CommaSeparatedValues#processDataSource: Unable to apply data source of type " + sDataSourceType);
|
|
85
|
+
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (oDataSource.dataUrl) {
|
|
90
|
+
Log.error("CommaSeparatedValues#processDataSource: URLs (such as dataUrl) are not supported as data sources. Type: " + sDataSourceType);
|
|
91
|
+
|
|
92
|
+
return null;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
if (Array.isArray(oDataSource)) {
|
|
96
|
+
mDataSource.data = oDataSource;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* If <code>ClientListBinding</code>, we use the binding path to receive the
|
|
101
|
+
* data from the underlying model. This takes sorter and filters into account.
|
|
102
|
+
*/
|
|
103
|
+
if (oDataSource.isA?.("sap.ui.model.ClientListBinding")) {
|
|
104
|
+
const aData = [];
|
|
105
|
+
|
|
106
|
+
oDataSource.getAllCurrentContexts().forEach(function(oContext) {
|
|
107
|
+
aData.push(oContext.getObject());
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
mDataSource.data = aData;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
return mDataSource;
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Applies default settings to the export configuration.
|
|
118
|
+
*
|
|
119
|
+
* - Adjusts the provided settings object to include default values where necessary.
|
|
120
|
+
* - Delegates the actual adjustment logic to the CSVBuilder instance.
|
|
121
|
+
*
|
|
122
|
+
* @param {object} mParameters Export parameters object
|
|
123
|
+
*
|
|
124
|
+
* @returns {Promise} A Promise that resolves when default settings have been applied
|
|
125
|
+
* @private
|
|
126
|
+
*/
|
|
127
|
+
CSV.prototype.setDefaultExportSettings = function(mParameters) {
|
|
128
|
+
if (!mParameters.fileType) {
|
|
129
|
+
mParameters.fileType = FileType.CSV;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
if (!mParameters.workbook.separator) {
|
|
133
|
+
mParameters.workbook.separator = ",";
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
return Promise.resolve();
|
|
137
|
+
};
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Creates a Promise that is resolved after the export has been finished.
|
|
141
|
+
*
|
|
142
|
+
* - Ensures that no other export process is running before starting a new one.
|
|
143
|
+
* - Resolves the Promise with the result of the build process.
|
|
144
|
+
* - Rejects the Promise if an error occurs or if a process is already running.
|
|
145
|
+
*
|
|
146
|
+
* @param {object} mParameters Validated export configuration
|
|
147
|
+
* @returns {Promise} A Promise that resolves when the build is complete
|
|
148
|
+
*
|
|
149
|
+
* @private
|
|
150
|
+
*/
|
|
151
|
+
CSV.prototype.createBuildPromise = function(mParameters) {
|
|
152
|
+
try {
|
|
153
|
+
if (this._oBuilder) {
|
|
154
|
+
return Promise.reject('Cannot start export: The process is already running');
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
const mWorkbook = mParameters.workbook;
|
|
158
|
+
|
|
159
|
+
this._bIsCancelled = false;
|
|
160
|
+
this._oBuilder = new CSVBuilder(mWorkbook.columns, mWorkbook.separator);
|
|
161
|
+
this._oBuilder.append(mParameters.dataSource.data);
|
|
162
|
+
|
|
163
|
+
const oBlob = this._oBuilder.build();
|
|
164
|
+
|
|
165
|
+
if (!this._bIsCancelled) {
|
|
166
|
+
ExportUtils.saveAsFile(oBlob, mParameters.fileName);
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
this._oBuilder = null;
|
|
170
|
+
|
|
171
|
+
return Promise.resolve();
|
|
172
|
+
} catch (oError) {
|
|
173
|
+
return Promise.reject(oError);
|
|
174
|
+
}
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Returns the specific MIME type.
|
|
179
|
+
*
|
|
180
|
+
* @public
|
|
181
|
+
* @returns {string} The MIME type of the Comma Separated Values format
|
|
182
|
+
*/
|
|
183
|
+
CSV.prototype.getMimeType = function() {
|
|
184
|
+
return "text/csv";
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
return CSV;
|
|
188
|
+
});
|
|
@@ -27,7 +27,7 @@ sap.ui.define([
|
|
|
27
27
|
* @class The <code>sap.ui.export.ExportBase</code> class allows you to export table data from a UI5 application to certain formats. This class is an abstract class that requires specific implementations for each file format.
|
|
28
28
|
*
|
|
29
29
|
* @author SAP SE
|
|
30
|
-
* @version 1.
|
|
30
|
+
* @version 1.142.0
|
|
31
31
|
*
|
|
32
32
|
* @since 1.96
|
|
33
33
|
* @alias sap.ui.export.ExportBase
|
|
@@ -28,7 +28,7 @@ sap.ui.define([
|
|
|
28
28
|
* @class The <code>sap.ui.export.ExportHandler</code> class allows you to export table data from an SAPUI5 application.
|
|
29
29
|
*
|
|
30
30
|
* @author SAP SE
|
|
31
|
-
* @version 1.
|
|
31
|
+
* @version 1.142.0
|
|
32
32
|
*
|
|
33
33
|
* @since 1.102
|
|
34
34
|
* @alias sap.ui.export.ExportHandler
|
|
@@ -145,7 +145,7 @@ sap.ui.define([
|
|
|
145
145
|
* @class Utilities related to export to enable reuse in integration scenarios (e.g. tables).
|
|
146
146
|
*
|
|
147
147
|
* @author SAP SE
|
|
148
|
-
* @version 1.
|
|
148
|
+
* @version 1.142.0
|
|
149
149
|
*
|
|
150
150
|
* @since 1.59
|
|
151
151
|
* @alias sap.ui.export.ExportUtils
|
|
@@ -1245,7 +1245,7 @@ sap.ui.define([
|
|
|
1245
1245
|
*
|
|
1246
1246
|
* @param {object} oContext Context object
|
|
1247
1247
|
* @param {string} [oContext.application] Name of the application (default: "SAP UI5")
|
|
1248
|
-
* @param {string} [oContext.version] Application version (default: "1.
|
|
1248
|
+
* @param {string} [oContext.version] Application version (default: "1.142.0")
|
|
1249
1249
|
* @param {string} [oContext.title] Title that will be written to the file (NOT the filename)
|
|
1250
1250
|
* @param {string} [oContext.modifiedBy] Optional user context that will be written to the file
|
|
1251
1251
|
* @param {string} [oContext.sheetName] Name of the data sheet - Maximum length of 31 characters
|
|
@@ -28,7 +28,7 @@ sap.ui.define([
|
|
|
28
28
|
* @class The <code>sap.ui.export.PortableDocument</code> class allows you to export table data from a UI5 application to a Portable Document Format (*.PDF) file.
|
|
29
29
|
*
|
|
30
30
|
* @author SAP SE
|
|
31
|
-
* @version 1.
|
|
31
|
+
* @version 1.142.0
|
|
32
32
|
*
|
|
33
33
|
* @since 1.96
|
|
34
34
|
* @alias sap.ui.export.PortableDocument
|
|
@@ -96,7 +96,7 @@ sap.ui.define([
|
|
|
96
96
|
* columns: aColumns,
|
|
97
97
|
* context: {
|
|
98
98
|
* application: 'Debug Test Application',
|
|
99
|
-
* version: '1.
|
|
99
|
+
* version: '1.142.0',
|
|
100
100
|
* title: 'Some random title',
|
|
101
101
|
* modifiedBy: 'John Doe',
|
|
102
102
|
* metaSheetName: 'Custom metadata',
|
|
@@ -183,7 +183,7 @@ sap.ui.define([
|
|
|
183
183
|
* @class The <code>sap.ui.export.Spreadsheet</code> class allows you to export table data from a UI5 application to a spreadsheet file.
|
|
184
184
|
*
|
|
185
185
|
* @author SAP SE
|
|
186
|
-
* @version 1.
|
|
186
|
+
* @version 1.142.0
|
|
187
187
|
*
|
|
188
188
|
* @since 1.50
|
|
189
189
|
* @alias sap.ui.export.Spreadsheet
|
|
@@ -21,7 +21,7 @@ sap.ui.define(['sap/base/Log', 'sap/ui/export/ExportUtils'], function(Log, Expor
|
|
|
21
21
|
* Utility class to perform spreadsheet export.
|
|
22
22
|
*
|
|
23
23
|
* @author SAP SE
|
|
24
|
-
* @version 1.
|
|
24
|
+
* @version 1.142.0
|
|
25
25
|
*
|
|
26
26
|
* @alias sap.ui.export.SpreadsheetExport
|
|
27
27
|
* @private
|
|
@@ -51,8 +51,8 @@ sap.ui.define(['sap/base/Log', 'sap/ui/export/ExportUtils'], function(Log, Expor
|
|
|
51
51
|
});
|
|
52
52
|
}
|
|
53
53
|
|
|
54
|
-
function onError(
|
|
55
|
-
postMessage({ error:
|
|
54
|
+
function onError(oError) {
|
|
55
|
+
postMessage({ error: oError.message || oError });
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
function onFinish(oArrayBuffer) {
|
|
@@ -62,8 +62,8 @@ sap.ui.define(['sap/base/Log', 'sap/ui/export/ExportUtils'], function(Log, Expor
|
|
|
62
62
|
// Export directly from an array in memory.
|
|
63
63
|
// TBD: convert dates as in exportUtils
|
|
64
64
|
function exportArray() {
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
let oSpreadsheet;
|
|
66
|
+
let fnConvertData;
|
|
67
67
|
|
|
68
68
|
function start(DataProvider, XLSXBuilder) {
|
|
69
69
|
fnConvertData = DataProvider.getDataConverter(mParams);
|
|
@@ -85,7 +85,7 @@ sap.ui.define(['sap/base/Log', 'sap/ui/export/ExportUtils'], function(Log, Expor
|
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
function exportInProcess() {
|
|
88
|
-
|
|
88
|
+
let oSpreadsheet, oRequest;
|
|
89
89
|
|
|
90
90
|
function start(DataProvider, XLSXBuilder) {
|
|
91
91
|
var provider = new DataProvider(mParams);
|
|
@@ -122,42 +122,64 @@ sap.ui.define(['sap/base/Log', 'sap/ui/export/ExportUtils'], function(Log, Expor
|
|
|
122
122
|
}
|
|
123
123
|
|
|
124
124
|
function exportInWorker() {
|
|
125
|
-
|
|
126
|
-
|
|
125
|
+
let oSpreadsheetWorker;
|
|
126
|
+
const mWorkerParams = {};
|
|
127
127
|
|
|
128
128
|
var fnCancel = function() {
|
|
129
|
-
|
|
129
|
+
oSpreadsheetWorker.postMessage({ cancel: true });
|
|
130
130
|
onFinish();
|
|
131
131
|
};
|
|
132
132
|
|
|
133
|
-
function createWorker(
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
133
|
+
function createWorker(sUrl) {
|
|
134
|
+
const {promise: oPromise, resolve: fnResolve, reject: fnReject} = Promise.withResolvers();
|
|
135
|
+
const oWorker = new Worker(sUrl);
|
|
136
|
+
const errorHandler = (oEvent) => {
|
|
137
|
+
oWorker.terminate();
|
|
138
|
+
fnReject(oEvent);
|
|
139
|
+
};
|
|
140
|
+
const messageHandler = (oEvent) => {
|
|
141
|
+
if (oEvent.data.initialized) {
|
|
142
|
+
oWorker.removeEventListener("message", messageHandler);
|
|
143
|
+
oWorker.removeEventListener("error", errorHandler);
|
|
144
|
+
|
|
145
|
+
oWorker.addEventListener("message", postMessage);
|
|
146
|
+
oWorker.addEventListener("error", onError);
|
|
147
|
+
fnResolve(oWorker);
|
|
148
|
+
}
|
|
149
|
+
};
|
|
141
150
|
|
|
142
|
-
|
|
151
|
+
oWorker.addEventListener("message", messageHandler);
|
|
152
|
+
oWorker.addEventListener("error", errorHandler);
|
|
143
153
|
|
|
144
|
-
return
|
|
154
|
+
return oPromise;
|
|
145
155
|
}
|
|
146
156
|
|
|
147
|
-
function
|
|
148
|
-
|
|
149
|
-
|| /^[^/]+\/[^/].*$|^\/[^/].*$/i.test(url); //check for relative address
|
|
150
|
-
}
|
|
157
|
+
function createBlobWorker() {
|
|
158
|
+
Log.warning('Direct worker is not allowed. Load the worker via Blob.');
|
|
151
159
|
|
|
152
|
-
|
|
153
|
-
|
|
160
|
+
const sBlobCode = `self.origin = "${mWorkerParams.base}"; importScripts("${mWorkerParams.src}");`;
|
|
161
|
+
const oBlobURL = window.URL.createObjectURL(new Blob([sBlobCode]));
|
|
154
162
|
|
|
155
|
-
|
|
163
|
+
return createWorker(oBlobURL);
|
|
164
|
+
}
|
|
156
165
|
|
|
157
|
-
|
|
158
|
-
|
|
166
|
+
/**
|
|
167
|
+
* Returns a worker instance. First tries to create a direct worker, if this fails, e.g. due to
|
|
168
|
+
* cross-origin or CSP restrictions, a blob worker is created. When no worker can be created, the
|
|
169
|
+
* Promise is rejected.
|
|
170
|
+
*
|
|
171
|
+
* @returns {Promise<Worker>} Worker instance
|
|
172
|
+
*/
|
|
173
|
+
async function getWorker() {
|
|
174
|
+
let oWorker;
|
|
159
175
|
|
|
160
|
-
|
|
176
|
+
try {
|
|
177
|
+
oWorker = await createWorker(mWorkerParams.src);
|
|
178
|
+
} catch (oError) {
|
|
179
|
+
oWorker = await createBlobWorker();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return oWorker;
|
|
161
183
|
}
|
|
162
184
|
|
|
163
185
|
function noWorker() {
|
|
@@ -165,34 +187,23 @@ sap.ui.define(['sap/base/Log', 'sap/ui/export/ExportUtils'], function(Log, Expor
|
|
|
165
187
|
fnCancel = exportInProcess(mParams).cancel;
|
|
166
188
|
}
|
|
167
189
|
|
|
168
|
-
function start() {
|
|
190
|
+
async function start() {
|
|
169
191
|
try {
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
noWorker();
|
|
175
|
-
e.preventDefault();
|
|
176
|
-
});
|
|
177
|
-
e.preventDefault();
|
|
178
|
-
});
|
|
179
|
-
} catch (err1) {
|
|
180
|
-
try {
|
|
181
|
-
spreadsheetWorker = blobWorker();
|
|
182
|
-
} catch (err2) {
|
|
183
|
-
noWorker();
|
|
184
|
-
}
|
|
192
|
+
oSpreadsheetWorker = await getWorker();
|
|
193
|
+
oSpreadsheetWorker.postMessage(mParams);
|
|
194
|
+
} catch (oError) {
|
|
195
|
+
noWorker();
|
|
185
196
|
}
|
|
186
197
|
}
|
|
187
198
|
|
|
188
199
|
// worker settings
|
|
189
|
-
|
|
190
|
-
|
|
200
|
+
mWorkerParams.base = ExportUtils.normalizeUrl(sap.ui.require.toUrl('sap/ui/export/js/'));
|
|
201
|
+
mWorkerParams.src = `${mWorkerParams.base}SpreadsheetWorker.js`;
|
|
191
202
|
|
|
192
203
|
start();
|
|
193
204
|
|
|
194
205
|
// fnCancel may be overwritten asynchronously after return, therefore it should be wrapped into a closure
|
|
195
|
-
return {cancel:
|
|
206
|
+
return {cancel: () => { fnCancel(); }};
|
|
196
207
|
}
|
|
197
208
|
|
|
198
209
|
if (mParams.dataSource.type === 'array') {
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
* SAPUI5
|
|
3
|
+
* (c) Copyright 2025 SAP SE. All rights reserved.
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
sap.ui.define([
|
|
7
|
+
"sap/ui/base/EventProvider",
|
|
8
|
+
"sap/base/Log"
|
|
9
|
+
], function(EventProvider, Log) {
|
|
10
|
+
"use strict";
|
|
11
|
+
|
|
12
|
+
// Matches a formula (for usage see #escapeContent):
|
|
13
|
+
// Starts with one of = + - @ but excludes "number only" formulas like -123,45 or =1.234e+5 as they are safe to use
|
|
14
|
+
const rFormula = /^[=\+\-@](?![\d.,]+(?:e[\+-]?\d+)?$)/i;
|
|
15
|
+
const MAX_CELL_LENGTH = 32760;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* CSVBuilder provides functionality to build and export data in CSV format.
|
|
19
|
+
*
|
|
20
|
+
* @extends sap.ui.base.EventProvider
|
|
21
|
+
* @class
|
|
22
|
+
* @constructor
|
|
23
|
+
* @param {Array.<Object>} aColumns Array of column metadata objects.
|
|
24
|
+
* Each object must have:
|
|
25
|
+
* <ul>
|
|
26
|
+
* <li>{string} label: The column header name in the CSV file.</li>
|
|
27
|
+
* <li>{string} property: The property name used to extract data for this column from each data row.</li>
|
|
28
|
+
* </ul>
|
|
29
|
+
* @param {string} [sSeparator=","] Character used to separate columns in the CSV file. Default is comma (",").
|
|
30
|
+
*/
|
|
31
|
+
const CSVBuilder = EventProvider.extend("sap.ui.export.js.CSVBuilder", {
|
|
32
|
+
|
|
33
|
+
constructor: function(aColumns, sSeparator = ",") {
|
|
34
|
+
|
|
35
|
+
this.validateSettings(aColumns, sSeparator);
|
|
36
|
+
|
|
37
|
+
this.aCompleteData = [];
|
|
38
|
+
this.aColumns = aColumns;
|
|
39
|
+
this.sSeparator = sSeparator;
|
|
40
|
+
|
|
41
|
+
// Create the regex for escaping content based on the separator
|
|
42
|
+
const escapedSeparator = this.sSeparator.replace(/[-\/\\^$*+?.()|[\]{}]/g, '\\$&');
|
|
43
|
+
this.rContentNeedsEscaping = new RegExp(`[\\r\\n"\\t${escapedSeparator}]`);
|
|
44
|
+
},
|
|
45
|
+
|
|
46
|
+
validateSettings(aColumns, sSeparator) {
|
|
47
|
+
if (!Array.isArray(aColumns)) {
|
|
48
|
+
throw new Error("Column information must be in form of an Array");
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
if (aColumns.length === 0) {
|
|
52
|
+
throw new Error("Column information must not be an empty array");
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
if (!sSeparator || typeof sSeparator !== "string" || sSeparator.length !== 1) {
|
|
56
|
+
throw new Error("Separator must be a single character string");
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Appends data to the current dataset and converts it into a CSV-formatted string.
|
|
63
|
+
*
|
|
64
|
+
* @param {Array} aData An array of objects containing the data to be added to the dataset, which can later be converted into a CSV file
|
|
65
|
+
*/
|
|
66
|
+
CSVBuilder.prototype.append = function(aData) {
|
|
67
|
+
const aColumnKeys = this.aColumns.map((col) => col.property);
|
|
68
|
+
|
|
69
|
+
aData.forEach((obj) => {
|
|
70
|
+
const aCells = [];
|
|
71
|
+
|
|
72
|
+
aColumnKeys.forEach((key) => {
|
|
73
|
+
aCells.push(this.getValue(obj, key));
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
this.aCompleteData.push(aCells.join(this.sSeparator));
|
|
77
|
+
});
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Retrieves and escapes the value of a specified property from a data object.
|
|
82
|
+
*
|
|
83
|
+
* @param {object} oData
|
|
84
|
+
* @param {string} sProperty
|
|
85
|
+
* @returns
|
|
86
|
+
*/
|
|
87
|
+
CSVBuilder.prototype.getValue = function(oData, sProperty) {
|
|
88
|
+
let sValue = oData[sProperty];
|
|
89
|
+
|
|
90
|
+
// Explicitly ignore NaN since the String conversion happens afterwards
|
|
91
|
+
// Early return for empty strings will be handled by the escapeContent method
|
|
92
|
+
if (sValue === null || typeof sValue === "undefined") {
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (typeof sValue !== "string") {
|
|
97
|
+
sValue = String(sValue);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return this.escapeContent(sValue);
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Escapes the content of a cell in a CSV file according to the CSV format specification (RFC 4180).
|
|
105
|
+
*
|
|
106
|
+
* This method processes the input value and applies the following rules:
|
|
107
|
+
* <ul>
|
|
108
|
+
* <li>If the value contains special characters (for example, separator, newline, or double quotes), it is enclosed in double quotes.</li>
|
|
109
|
+
* <li>Double quotes within the value are escaped by doubling them (for example, `"` becomes `""`).</li>
|
|
110
|
+
* <li>If the value starts with '=', '+', '-', or '@', a single quote is prepended to prevent CSV injection.</li>
|
|
111
|
+
* <li>Values longer than 32,760 characters will be truncated to ensure compatibility with spreadsheet software.</li>
|
|
112
|
+
* </ul>
|
|
113
|
+
*
|
|
114
|
+
* @param {string} valueToCheck - The cell value to process and escape according to CSV rules
|
|
115
|
+
* @returns {string} The escaped value, ready for inclusion in a CSV file
|
|
116
|
+
*/
|
|
117
|
+
CSVBuilder.prototype.escapeContent = function(valueToCheck) {
|
|
118
|
+
let sValue = valueToCheck;
|
|
119
|
+
|
|
120
|
+
if (!sValue) {
|
|
121
|
+
return sValue;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
sValue = sValue.trim();
|
|
125
|
+
|
|
126
|
+
// Remove Unicode BOM if present
|
|
127
|
+
if (sValue.charCodeAt(0) === 0xFEFF) {
|
|
128
|
+
sValue = sValue.slice(1);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// Prepend single quote in case cell content is a formula
|
|
132
|
+
if (rFormula.test(sValue)) {
|
|
133
|
+
sValue = "'" + sValue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Prevent cell overflow
|
|
137
|
+
if (sValue.length > MAX_CELL_LENGTH) {
|
|
138
|
+
sValue = sValue.slice(0, MAX_CELL_LENGTH);
|
|
139
|
+
Log.warning("Cell content truncated to prevent overflow.");
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Convert value to string
|
|
143
|
+
sValue = sValue.toString();
|
|
144
|
+
|
|
145
|
+
// Check if the value contains the separator or other special characters
|
|
146
|
+
const bContainsSeparatorChar = sValue.indexOf(this.sSeparator) > -1;
|
|
147
|
+
|
|
148
|
+
// Only wrap content with double quotes if it contains the separator char,
|
|
149
|
+
// a new line (CR / LF), a double quote, or other special characters
|
|
150
|
+
if (bContainsSeparatorChar || this.rContentNeedsEscaping.test(sValue)) {
|
|
151
|
+
// Escape double quotes by preceding them with another one
|
|
152
|
+
sValue = sValue.replace(/"/g, '""');
|
|
153
|
+
|
|
154
|
+
// Wrap final content with double quotes
|
|
155
|
+
sValue = `"${sValue}"`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return sValue;
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Combines the column labels and corresponding row data into a CSV-formatted string using a separator defined in the settings.
|
|
163
|
+
*
|
|
164
|
+
* This method performs the following actions:
|
|
165
|
+
* <ul>
|
|
166
|
+
* <li>Converts the CSV-formatted string into a <code>Blob</code> object.</li>
|
|
167
|
+
* <li>Adds a UTF-8 Byte Order Mark (BOM) at the beginning of the <code>Blob</code> for compatibility with spreadsheet software.</li>
|
|
168
|
+
* </ul>
|
|
169
|
+
*
|
|
170
|
+
* @returns {Blob} A Blob object containing the CSV data with a UTF-8 BOM
|
|
171
|
+
*/
|
|
172
|
+
CSVBuilder.prototype.build = function() {
|
|
173
|
+
|
|
174
|
+
// Generate the CSV header
|
|
175
|
+
const aColumnNamesCSV = this.aColumns.map((item) => item.label);
|
|
176
|
+
const sColumnNamesCSV = aColumnNamesCSV.join(this.sSeparator) + "\r\n";
|
|
177
|
+
|
|
178
|
+
// Combine all line items into a CSV-formatted string
|
|
179
|
+
const sCsvContent = this.aCompleteData.join("\r\n");
|
|
180
|
+
|
|
181
|
+
// Combine the header and data
|
|
182
|
+
const sCompleteCSV = sColumnNamesCSV + sCsvContent;
|
|
183
|
+
|
|
184
|
+
// Add UTF-8 BOM
|
|
185
|
+
const blobData = new TextEncoder().encode(sCompleteCSV);
|
|
186
|
+
const blobBOM = Uint8Array.of(0xef, 0xbb, 0xbf);
|
|
187
|
+
const blobContent = new Uint8Array(blobBOM.length + blobData.length);
|
|
188
|
+
blobContent.set(blobBOM);
|
|
189
|
+
blobContent.set(blobData, blobBOM.length);
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
return new Blob([blobContent], {
|
|
193
|
+
type: 'text/csv;charset=utf-8'
|
|
194
|
+
});
|
|
195
|
+
} catch (error) {
|
|
196
|
+
throw new Error("Failed to create CSV Blob");
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
return CSVBuilder;
|
|
201
|
+
});
|
|
@@ -12,6 +12,10 @@ importScripts(origin + 'XLSXBuilder.js');
|
|
|
12
12
|
importScripts(origin + '../provider/DataProviderBase.js');
|
|
13
13
|
importScripts(origin + 'libs/JSZip3.js');
|
|
14
14
|
|
|
15
|
+
postMessage({
|
|
16
|
+
initialized: true
|
|
17
|
+
});
|
|
18
|
+
|
|
15
19
|
self.onmessage = function(oMessage) {
|
|
16
20
|
'use strict';
|
|
17
21
|
|